feat(go): advance validation sidecar toward production
This commit is contained in:
parent
6a06eba255
commit
f85c2e831a
92 changed files with 8881 additions and 91 deletions
92
go-agent/internal/correlation/correlation.go
Normal file
92
go-agent/internal/correlation/correlation.go
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// Package correlation adds higher-confidence stories without hiding or
|
||||
// replacing their raw alerts. It is transport- and persistence-independent.
|
||||
package correlation
|
||||
|
||||
import "sort"
|
||||
|
||||
const SIDMultiStageIntrusion = 100080
|
||||
|
||||
type Incident struct {
|
||||
FirstTimestamp float64 `json:"first_ts"`
|
||||
LastTimestamp float64 `json:"last_ts"`
|
||||
Signatures []string `json:"signatures"`
|
||||
}
|
||||
|
||||
type Record struct {
|
||||
SID int `json:"sid"`
|
||||
Signature string `json:"signature"`
|
||||
Classtype string `json:"classtype"`
|
||||
Severity string `json:"severity"`
|
||||
Summary string `json:"summary"`
|
||||
WindowSeconds int `json:"window_seconds"`
|
||||
MatchedSignatures []string `json:"matched_signatures"`
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
SID int
|
||||
Signature string
|
||||
Classtype string
|
||||
Severity string
|
||||
Summary string
|
||||
RequiredAny []map[string]bool
|
||||
MaxWindow int
|
||||
}
|
||||
|
||||
var DefaultRules = []Rule{{
|
||||
SID: SIDMultiStageIntrusion, Signature: "correlation.multi_stage_intrusion",
|
||||
Classtype: "multi-stage-intrusion", Severity: "CRITICAL",
|
||||
Summary: "Web/database service spawned a shell and the same incident showed " +
|
||||
"suspicious egress or an unusual listener within the correlation window",
|
||||
RequiredAny: []map[string]bool{
|
||||
{"exec_rule.web-rce": true},
|
||||
{"host_rule.suspicious-egress": true, "host_rule.suspicious-listener": true},
|
||||
},
|
||||
MaxWindow: 600,
|
||||
}}
|
||||
|
||||
func Correlate(incident Incident, rules []Rule) []Record {
|
||||
if rules == nil {
|
||||
rules = DefaultRules
|
||||
}
|
||||
signatures := make(map[string]bool, len(incident.Signatures))
|
||||
for _, signature := range incident.Signatures {
|
||||
signatures[signature] = true
|
||||
}
|
||||
duration := incident.LastTimestamp - incident.FirstTimestamp
|
||||
result := make([]Record, 0)
|
||||
for _, rule := range rules {
|
||||
if duration > float64(rule.MaxWindow) {
|
||||
continue
|
||||
}
|
||||
matched := true
|
||||
for _, choices := range rule.RequiredAny {
|
||||
found := false
|
||||
for choice := range choices {
|
||||
if signatures[choice] {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
matched = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
matchedSignatures := make([]string, 0, len(signatures))
|
||||
for signature := range signatures {
|
||||
matchedSignatures = append(matchedSignatures, signature)
|
||||
}
|
||||
sort.Strings(matchedSignatures)
|
||||
result = append(result, Record{
|
||||
SID: rule.SID, Signature: rule.Signature, Classtype: rule.Classtype,
|
||||
Severity: rule.Severity, Summary: rule.Summary,
|
||||
WindowSeconds: rule.MaxWindow, MatchedSignatures: matchedSignatures,
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue