@@ -20,12 +20,16 @@ package alertsender
2020
2121import (
2222 "bytes"
23+ "encoding/json"
2324 "fmt"
2425 "reflect"
2526 "strings"
27+ "time"
2628
2729 "github.com/mitchellh/mapstructure"
30+
2831 "github.com/rabbitstack/fibratus/pkg/event"
32+ "github.com/rabbitstack/fibratus/pkg/event/params"
2933 "github.com/yuin/goldmark"
3034 "github.com/yuin/goldmark/extension"
3135 "github.com/yuin/goldmark/renderer/html"
@@ -163,6 +167,182 @@ func (a *Alert) MDToHTML() error {
163167 return nil
164168}
165169
170+ // MarshalJSON encodes the alert to JSON format.
171+ func (a Alert ) MarshalJSON () ([]byte , error ) {
172+ var msg = & struct {
173+ ID string `json:"id"`
174+ Title string `json:"title"`
175+ Severity string `json:"severity"`
176+ Text string `json:"text,omitempty"`
177+ Description string `json:"description"`
178+ Labels map [string ]string `json:"labels,omitempty"`
179+ Events []struct {
180+ Name string `json:"name"`
181+ Category string `json:"category"`
182+ Timestamp time.Time `json:"timestamp"`
183+ Params map [string ]any `json:"params"`
184+ Callstack []string `json:"callstack,omitempty"`
185+ Proc * struct {
186+ PID uint32 `json:"pid"`
187+ TID uint32 `json:"tid"`
188+ PPID uint32 `json:"ppid"`
189+ Name string `json:"name"`
190+ Exe string `json:"exe"`
191+ Cmdline string `json:"cmdline,omitempty"`
192+ Pname string `json:"parent_name,omitempty"`
193+ Pcmdline string `json:"parent_cmdline,omitempty"`
194+ Cwd string `json:"cwd,omitempty"`
195+ SID string `json:"sid"`
196+ Username string `json:"username"`
197+ Domain string `json:"domain"`
198+ SessionID uint32 `json:"session_id"`
199+ IntegrityLevel string `json:"integrity_level"`
200+ IsWOW64 bool `json:"is_wow64"`
201+ IsPackaged bool `json:"is_packaged"`
202+ IsProtected bool `json:"is_protected"`
203+ Ancestors []string `json:"ancestors"`
204+ } `json:"proc,omitempty"`
205+ } `json:"events"`
206+ }{
207+ ID : a .ID ,
208+ Title : a .Title ,
209+ Severity : a .Severity .String (),
210+ Text : a .Text ,
211+ Description : a .Description ,
212+ Labels : a .Labels ,
213+ }
214+
215+ events := make ([]struct {
216+ Name string `json:"name"`
217+ Category string `json:"category"`
218+ Timestamp time.Time `json:"timestamp"`
219+ Params map [string ]any `json:"params"`
220+ Callstack []string `json:"callstack,omitempty"`
221+ Proc * struct {
222+ PID uint32 `json:"pid"`
223+ TID uint32 `json:"tid"`
224+ PPID uint32 `json:"ppid"`
225+ Name string `json:"name"`
226+ Exe string `json:"exe"`
227+ Cmdline string `json:"cmdline,omitempty"`
228+ Pname string `json:"parent_name,omitempty"`
229+ Pcmdline string `json:"parent_cmdline,omitempty"`
230+ Cwd string `json:"cwd,omitempty"`
231+ SID string `json:"sid"`
232+ Username string `json:"username"`
233+ Domain string `json:"domain"`
234+ SessionID uint32 `json:"session_id"`
235+ IntegrityLevel string `json:"integrity_level"`
236+ IsWOW64 bool `json:"is_wow64"`
237+ IsPackaged bool `json:"is_packaged"`
238+ IsProtected bool `json:"is_protected"`
239+ Ancestors []string `json:"ancestors"`
240+ } `json:"proc,omitempty"`
241+ }, 0 , len (a .Events ))
242+
243+ for _ , e := range a .Events {
244+ var evt = struct {
245+ Name string `json:"name"`
246+ Category string `json:"category"`
247+ Timestamp time.Time `json:"timestamp"`
248+ Params map [string ]any `json:"params"`
249+ Callstack []string `json:"callstack,omitempty"`
250+ Proc * struct {
251+ PID uint32 `json:"pid"`
252+ TID uint32 `json:"tid"`
253+ PPID uint32 `json:"ppid"`
254+ Name string `json:"name"`
255+ Exe string `json:"exe"`
256+ Cmdline string `json:"cmdline,omitempty"`
257+ Pname string `json:"parent_name,omitempty"`
258+ Pcmdline string `json:"parent_cmdline,omitempty"`
259+ Cwd string `json:"cwd,omitempty"`
260+ SID string `json:"sid"`
261+ Username string `json:"username"`
262+ Domain string `json:"domain"`
263+ SessionID uint32 `json:"session_id"`
264+ IntegrityLevel string `json:"integrity_level"`
265+ IsWOW64 bool `json:"is_wow64"`
266+ IsPackaged bool `json:"is_packaged"`
267+ IsProtected bool `json:"is_protected"`
268+ Ancestors []string `json:"ancestors"`
269+ } `json:"proc,omitempty"`
270+ }{
271+ Name : e .Name ,
272+ Category : string (e .Category ),
273+ Timestamp : e .Timestamp ,
274+ Params : make (map [string ]any ),
275+ Callstack : make ([]string , 0 , len (e .Callstack )),
276+ }
277+
278+ // populate event parameters
279+ for _ , param := range e .Params {
280+ if param .Type == params .Bool || param .Type == params .PID ||
281+ param .Type == params .TID || param .Type == params .Port || param .IsNumber () {
282+ evt .Params [param .Name ] = param .Value
283+ } else {
284+ evt .Params [param .Name ] = param .String ()
285+ }
286+ }
287+
288+ // populate callstack
289+ for i := range e .Callstack {
290+ frame := e .Callstack [len (e .Callstack )- i - 1 ]
291+ evt .Callstack = append (evt .Callstack , fmt .Sprintf ("%s %s!%s" , frame .Addr , frame .Module , frame .Symbol ))
292+ }
293+
294+ ps := e .PS
295+ if ps != nil {
296+ evt .Proc = & struct {
297+ PID uint32 `json:"pid"`
298+ TID uint32 `json:"tid"`
299+ PPID uint32 `json:"ppid"`
300+ Name string `json:"name"`
301+ Exe string `json:"exe"`
302+ Cmdline string `json:"cmdline,omitempty"`
303+ Pname string `json:"parent_name,omitempty"`
304+ Pcmdline string `json:"parent_cmdline,omitempty"`
305+ Cwd string `json:"cwd,omitempty"`
306+ SID string `json:"sid"`
307+ Username string `json:"username"`
308+ Domain string `json:"domain"`
309+ SessionID uint32 `json:"session_id"`
310+ IntegrityLevel string `json:"integrity_level"`
311+ IsWOW64 bool `json:"is_wow64"`
312+ IsPackaged bool `json:"is_packaged"`
313+ IsProtected bool `json:"is_protected"`
314+ Ancestors []string `json:"ancestors"`
315+ }{
316+ PID : ps .PID ,
317+ TID : e .Tid ,
318+ PPID : ps .Ppid ,
319+ Name : ps .Name ,
320+ Exe : ps .Exe ,
321+ Cmdline : ps .Cmdline ,
322+ Cwd : ps .Cwd ,
323+ SID : ps .SID ,
324+ Username : ps .Username ,
325+ Domain : ps .Domain ,
326+ SessionID : ps .SessionID ,
327+ IntegrityLevel : ps .TokenIntegrityLevel ,
328+ IsWOW64 : ps .IsWOW64 ,
329+ IsPackaged : ps .IsPackaged ,
330+ IsProtected : ps .IsProtected ,
331+ Ancestors : ps .Ancestors (),
332+ }
333+ if ps .Parent != nil {
334+ evt .Proc .Pname = ps .Parent .Name
335+ evt .Proc .Pcmdline = ps .Parent .Cmdline
336+ }
337+ }
338+
339+ events = append (events , evt )
340+ }
341+ msg .Events = events
342+
343+ return json .Marshal (msg )
344+ }
345+
166346// NewAlert builds a new alert.
167347func NewAlert (title , text string , tags []string , severity Severity ) Alert {
168348 return Alert {Title : title , Text : text , Tags : tags , Severity : severity }
0 commit comments