Skip to content

Commit f69b036

Browse files
committed
Adjust JSON output to add groups
1 parent 664ebf0 commit f69b036

File tree

10 files changed

+212
-106
lines changed

10 files changed

+212
-106
lines changed

alerts/alerts.go

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package alerts
2+
3+
import (
4+
"net"
5+
"time"
6+
7+
"github.com/alphasoc/nfr/client"
8+
"github.com/alphasoc/nfr/groups"
9+
)
10+
11+
// AlertMapper maps response to internal alert struct.
12+
type AlertMapper struct {
13+
groups *groups.Groups
14+
}
15+
16+
// Alert represents alert api struct.
17+
type Alert struct {
18+
Follow string `json:"follow"`
19+
More bool `json:"more"`
20+
Events []Event `json:"events"`
21+
}
22+
23+
// Event from alert.
24+
type Event struct {
25+
Type string `json:"type"`
26+
Flags []string `json:"flags"`
27+
Groups []Group `json:"groups"`
28+
Threats []Threat `json:"threats"`
29+
30+
// fileds common for dns and ip event
31+
Timestamp time.Time `json:"ts"`
32+
SrcIP net.IP `json:"srcIp"`
33+
34+
// ip event fileds
35+
SrcPort int `json:"srcPort"`
36+
DstIP net.IP `json:"destIp"`
37+
DstPort int `json:"destPort"`
38+
Protocol string `json:"proto"`
39+
BytesIn int `json:"bytesIn"`
40+
BytesOut int `json:"bytesOut"`
41+
42+
// dns event fileds
43+
Query string `json:"query"`
44+
RecordType string `json:"recordType"`
45+
}
46+
47+
// Threat for event.
48+
type Threat struct {
49+
ID string `json:"id"`
50+
Severity int `json:"severity"`
51+
Description string `json:"desc"`
52+
Policy bool `json:"policy"`
53+
Deprecated bool `json:"deprecated"`
54+
}
55+
56+
// Group describe group event belongs to.
57+
type Group struct {
58+
Label string `json:"label"`
59+
Description string `json:"desc"`
60+
}
61+
62+
// NewAlertMapper creates new alert mapper.
63+
func NewAlertMapper(groups *groups.Groups) *AlertMapper {
64+
return &AlertMapper{groups: groups}
65+
}
66+
67+
// Map maps client response to alert.
68+
func (m *AlertMapper) Map(resp *client.AlertsResponse) *Alert {
69+
var alert = &Alert{
70+
Follow: resp.Follow,
71+
More: resp.More,
72+
Events: make([]Event, len(resp.Alerts)),
73+
}
74+
75+
for i := range resp.Alerts {
76+
alert.Events[i] = Event{
77+
Type: "alert",
78+
Flags: resp.Alerts[i].Wisdom.Flags,
79+
}
80+
for _, threat := range resp.Alerts[i].Threats {
81+
alert.Events[i].Threats = append(alert.Events[i].Threats, Threat{
82+
ID: threat,
83+
Severity: resp.Threats[threat].Severity,
84+
Description: resp.Threats[threat].Title,
85+
Policy: resp.Threats[threat].Policy,
86+
Deprecated: false,
87+
})
88+
}
89+
90+
switch resp.Alerts[i].EventType {
91+
case "dns":
92+
alert.Events[i].Timestamp = resp.Alerts[i].DNSEvent.Timestamp
93+
alert.Events[i].SrcIP = resp.Alerts[i].DNSEvent.SrcIP
94+
alert.Events[i].Query = resp.Alerts[i].DNSEvent.Query
95+
alert.Events[i].RecordType = resp.Alerts[i].DNSEvent.QType
96+
case "ip":
97+
alert.Events[i].Timestamp = resp.Alerts[i].IPEvent.Timestamp
98+
alert.Events[i].SrcIP = resp.Alerts[i].IPEvent.SrcIP
99+
alert.Events[i].SrcPort = resp.Alerts[i].IPEvent.SrcPort
100+
alert.Events[i].DstIP = resp.Alerts[i].IPEvent.DstIP
101+
alert.Events[i].DstPort = resp.Alerts[i].IPEvent.DstPort
102+
alert.Events[i].Protocol = resp.Alerts[i].IPEvent.Protocol
103+
alert.Events[i].BytesIn = resp.Alerts[i].IPEvent.BytesIn
104+
alert.Events[i].BytesOut = resp.Alerts[i].IPEvent.BytesOut
105+
}
106+
107+
for _, group := range m.groups.FindGroupsBySrcIP(alert.Events[i].SrcIP) {
108+
alert.Events[i].Groups = append(alert.Events[i].Groups, Group{
109+
Label: group.Name,
110+
Description: group.Label,
111+
})
112+
}
113+
}
114+
return alert
115+
}

alerts/graylog_writer.go

+19-29
Original file line numberDiff line numberDiff line change
@@ -35,45 +35,35 @@ func NewGraylogWriter(uri string, level int) (*GraylogWriter, error) {
3535
}
3636

3737
// Write writes alert response to graylog server.
38-
func (w *GraylogWriter) Write(resp *client.AlertsResponse) error {
39-
// do not log if there is no alerts
40-
if len(resp.Alerts) == 0 {
41-
return nil
42-
}
43-
38+
func (w *GraylogWriter) Write(alert *Alert) error {
4439
// send each alert as separate message.
45-
for _, alert := range resp.Alerts {
46-
for _, threat := range alert.Threats {
40+
for _, event := range alert.Events {
41+
for _, threat := range event.Threats {
4742
m := &gelf.Message{
4843
Version: "1.1",
4944
Host: w.hostname,
50-
ShortMessage: resp.Threats[threat].Title,
45+
ShortMessage: threat.Description,
5146
Timestamp: time.Now().Unix(),
5247
Level: w.level,
5348
Extra: map[string]interface{}{
54-
"severity": resp.Threats[threat].Severity,
55-
"policy": strconv.FormatBool(resp.Threats[threat].Policy),
56-
"flags": strings.Join(alert.Wisdom.Flags, ","),
57-
"threat": threat,
49+
"severity": threat.Severity,
50+
"policy": strconv.FormatBool(threat.Policy),
51+
"flags": strings.Join(event.Flags, ","),
52+
"threat": threat.ID,
5853
"engine_agent": client.DefaultUserAgent,
5954
},
6055
}
61-
switch alert.EventType {
62-
case "dns":
63-
m.Extra["original_event"] = alert.DNSEvent.Timestamp.String()
64-
m.Extra["src_ip"] = alert.DNSEvent.SrcIP
65-
m.Extra["query"] = alert.DNSEvent.Query
66-
m.Extra["record_type"] = alert.DNSEvent.QType
67-
case "ip":
68-
m.Extra["original_event"] = alert.IPEvent.Timestamp.String()
69-
m.Extra["protocol"] = alert.IPEvent.Protocol
70-
m.Extra["src_ip"] = alert.IPEvent.SrcIP
71-
m.Extra["src_port"] = alert.IPEvent.SrcPort
72-
m.Extra["dest_ip"] = alert.IPEvent.DstIP
73-
m.Extra["dest_port"] = alert.IPEvent.DstPort
74-
m.Extra["bytes_in"] = alert.IPEvent.BytesIn
75-
m.Extra["bytes_out"] = alert.IPEvent.BytesOut
76-
}
56+
m.Extra["original_event"] = event.Timestamp.String()
57+
m.Extra["src_ip"] = event.SrcIP
58+
m.Extra["query"] = event.Query
59+
m.Extra["record_type"] = event.RecordType
60+
61+
m.Extra["protocol"] = event.Protocol
62+
m.Extra["src_port"] = event.SrcPort
63+
m.Extra["dest_ip"] = event.DstIP
64+
m.Extra["dest_port"] = event.DstPort
65+
m.Extra["bytes_in"] = event.BytesIn
66+
m.Extra["bytes_out"] = event.BytesOut
7767
if err := w.g.Send(m); err != nil {
7868
return err
7969
}

alerts/json_writer.go

+2-9
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ package alerts
33
import (
44
"encoding/json"
55
"os"
6-
7-
"github.com/alphasoc/nfr/client"
86
)
97

108
// JSONFileWriter implements Writer interface and writes alerts in json format.
@@ -29,13 +27,8 @@ func NewJSONFileWriter(file string) (*JSONFileWriter, error) {
2927
}
3028

3129
// Write writes alerts response to the file in json format.
32-
func (l *JSONFileWriter) Write(e *client.AlertsResponse) error {
33-
// do not log if there is no alerts
34-
if len(e.Alerts) == 0 {
35-
return nil
36-
}
37-
38-
b, err := json.Marshal(e)
30+
func (l *JSONFileWriter) Write(alert *Alert) error {
31+
b, err := json.Marshal(alert)
3932
if err != nil {
4033
return err
4134
}

alerts/poller.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Package alerts polls and writes alerts from AlphaSOC api.
1+
// Package alerts polls and writes alerts from AlphaSOC Engine.
22
package alerts
33

44
import (
@@ -17,13 +17,15 @@ type Poller struct {
1717
ticker *time.Ticker
1818
follow string
1919
followFile string
20+
mapper *AlertMapper
2021
}
2122

2223
// NewPoller creates new poller base on give client and writer.
23-
func NewPoller(c client.Client) *Poller {
24+
func NewPoller(c client.Client, mapper *AlertMapper) *Poller {
2425
return &Poller{
2526
c: c,
2627
writers: make([]Writer, 0),
28+
mapper: mapper,
2729
}
2830
}
2931

@@ -66,8 +68,14 @@ func (p *Poller) do(interval time.Duration, count int) error {
6668
return err
6769
}
6870

71+
if len(alerts.Alerts) == 0 {
72+
continue
73+
}
74+
75+
newAlerts := p.mapper.Map(alerts)
76+
6977
for _, w := range p.writers {
70-
if err := w.Write(alerts); err != nil {
78+
if err := w.Write(newAlerts); err != nil {
7179
return err
7280
}
7381
}

alerts/qradar_writer.go

+20-29
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"log/syslog"
66
"strings"
77

8-
"github.com/alphasoc/nfr/client"
98
"github.com/alphasoc/nfr/leef"
109
"github.com/alphasoc/nfr/version"
1110
)
@@ -32,43 +31,35 @@ func NewQRadarWriter(raddr string) (*QRadarWriter, error) {
3231
}
3332

3433
// Write writes alert response to the qradar syslog input.
35-
func (w *QRadarWriter) Write(resp *client.AlertsResponse) error {
36-
if len(resp.Alerts) == 0 {
37-
return nil
38-
}
39-
34+
func (w *QRadarWriter) Write(alert *Alert) error {
4035
// send each alert as separate message.
41-
for _, alert := range resp.Alerts {
42-
for _, threat := range alert.Threats {
36+
for _, event := range alert.Events {
37+
for _, threat := range event.Threats {
4338
e := leef.NewEvent()
44-
e.SetHeader("AlphaSOC", tag, strings.TrimPrefix(version.Version, "v"), threat)
39+
e.SetHeader("AlphaSOC", tag, strings.TrimPrefix(version.Version, "v"), threat.ID)
4540

46-
e.SetSevAttr(resp.Threats[threat].Severity * 2)
47-
if resp.Threats[threat].Policy {
41+
e.SetSevAttr(threat.Severity * 2)
42+
if threat.Policy {
4843
e.SetPolicyAttr("1")
4944
} else {
5045
e.SetPolicyAttr("0")
5146
}
52-
e.SetAttr("flags", strings.Join(alert.Wisdom.Flags, ","))
53-
e.SetAttr("description", resp.Threats[threat].Title)
47+
e.SetAttr("flags", strings.Join(event.Flags, ","))
48+
e.SetAttr("description", threat.Description)
5449

5550
e.SetDevTimeFormatAttr("MMM dd yyyy HH:mm:ss")
56-
switch alert.EventType {
57-
case "dns":
58-
e.SetDevTimeAttr(alert.DNSEvent.Timestamp.Format("Jan 02 2006 15:04:05"))
59-
e.SetSrcAttr(alert.DNSEvent.SrcIP)
60-
e.SetAttr("query", alert.DNSEvent.Query)
61-
e.SetAttr("recordType", alert.DNSEvent.QType)
62-
case "ip":
63-
e.SetDevTimeAttr(alert.IPEvent.Timestamp.Format("Jan 02 2006 15:04:05"))
64-
e.SetProtoAttr(alert.IPEvent.Protocol)
65-
e.SetSrcAttr(alert.IPEvent.SrcIP)
66-
e.SetSrcPortAttr(alert.IPEvent.SrcPort)
67-
e.SetDstAttr(alert.IPEvent.DstIP)
68-
e.SetDstPortAttr(alert.IPEvent.DstPort)
69-
e.SetSrcBytesAttr(alert.IPEvent.BytesIn)
70-
e.SetDstBytesAttr(alert.IPEvent.BytesOut)
71-
}
51+
e.SetDevTimeAttr(event.Timestamp.Format("Jan 02 2006 15:04:05"))
52+
e.SetProtoAttr(event.Protocol)
53+
e.SetSrcAttr(event.SrcIP)
54+
e.SetSrcAttr(event.SrcIP)
55+
e.SetSrcPortAttr(event.SrcPort)
56+
e.SetDstAttr(event.DstIP)
57+
e.SetDstPortAttr(event.DstPort)
58+
e.SetSrcBytesAttr(event.BytesIn)
59+
e.SetDstBytesAttr(event.BytesOut)
60+
e.SetAttr("query", event.Query)
61+
e.SetAttr("recordType", event.RecordType)
62+
7263
if err := w.w.Alert(e.String()); err != nil {
7364
return err
7465
}

alerts/syslog_writer.go

+2-8
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"encoding/json"
55
"fmt"
66
"log/syslog"
7-
8-
"github.com/alphasoc/nfr/client"
97
)
108

119
// SyslogWriter implements Writer interface and write
@@ -25,12 +23,8 @@ func NewSyslogWriter(raddr string) (*SyslogWriter, error) {
2523
}
2624

2725
// Write writes alert response to the syslog input.
28-
func (w *SyslogWriter) Write(resp *client.AlertsResponse) error {
29-
if len(resp.Alerts) == 0 {
30-
return nil
31-
}
32-
33-
b, err := json.Marshal(resp)
26+
func (w *SyslogWriter) Write(alert *Alert) error {
27+
b, err := json.Marshal(alert)
3428
if err != nil {
3529
return err
3630
}

alerts/writer.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
package alerts
22

3-
import (
4-
"github.com/alphasoc/nfr/client"
5-
)
6-
73
// Writer interface for log api alerts response.
84
type Writer interface {
9-
Write(*client.AlertsResponse) error
5+
Write(*Alert) error
106
}

executor/executor.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,15 @@ func New(c client.Client, cfg *config.Config) (*Executor, error) {
5858
cfg: cfg,
5959
}
6060

61+
groups, err := createGroups(cfg)
62+
if err != nil {
63+
return nil, err
64+
}
65+
e.groups = groups
66+
6167
if cfg.HasOutputs() {
62-
e.alertsPoller = alerts.NewPoller(c)
68+
mapper := alerts.NewAlertMapper(groups)
69+
e.alertsPoller = alerts.NewPoller(c, mapper)
6370
if err := e.alertsPoller.SetFollowDataFile(cfg.Data.File); err != nil {
6471
return nil, err
6572
}
@@ -92,12 +99,6 @@ func New(c client.Client, cfg *config.Config) (*Executor, error) {
9299

93100
e.dnsbuf = packet.NewDNSPacketBuffer()
94101
e.ipbuf = packet.NewIPPacketBuffer()
95-
groups, err := createGroups(cfg)
96-
if err != nil {
97-
return nil, err
98-
}
99-
e.groups = groups
100-
101102
return e, nil
102103
}
103104

@@ -540,6 +541,7 @@ func createGroups(cfg *config.Config) (*groups.Groups, error) {
540541
for name, group := range cfg.ScopeConfig.Groups {
541542
g := &groups.Group{
542543
Name: name,
544+
Label: group.Label,
543545
SrcIncludes: group.InScope,
544546
SrcExcludes: group.OutScope,
545547
DstIncludes: []string{"0.0.0.0/0", "::/0"},

0 commit comments

Comments
 (0)