Skip to content

Commit

Permalink
tiered notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
jertel committed Aug 21, 2024
1 parent 7e70e74 commit e36a9eb
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 33 deletions.
152 changes: 123 additions & 29 deletions server/modules/elastalert/elastalert.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,30 +83,41 @@ var acceptedExtensions = map[string]bool{
}

type ElastAlertEngine struct {
srv *server.Server
airgapBasePath string
failAfterConsecutiveErrorCount int
sigmaPackageDownloadTemplate string
elastAlertRulesFolder string
rulesFingerprintFile string
sigmaPipelineFinal string
sigmaPipelineSO string
sigmaPipelinesFingerprintFile string
sigmaRulePackages []string
autoEnabledSigmaRules []string
additionalAlerters []string
rulesRepos []*model.RuleRepo
reposFolder string
isRunning bool
interm sync.Mutex
airgapEnabled bool
notify bool
writeNoRead *string
aiSummaries *sync.Map // map[string]*detections.AiSummary{}
showAiSummaries bool
aiRepoUrl string
aiRepoBranch string
aiRepoPath string
srv *server.Server
airgapBasePath string
failAfterConsecutiveErrorCount int
sigmaPackageDownloadTemplate string
elastAlertRulesFolder string
rulesFingerprintFile string
sigmaPipelineFinal string
sigmaPipelineSO string
sigmaPipelinesFingerprintFile string
sigmaRulePackages []string
autoEnabledSigmaRules []string
additionalAlerters []string
additionalAlerterParams string
informationalSeverityAlerters []string
informationalSeverityAlerterParams string
lowSeverityAlerters []string
lowSeverityAlerterParams string
mediumSeverityAlerters []string
mediumSeverityAlerterParams string
highSeverityAlerters []string
highSeverityAlerterParams string
criticalSeverityAlerters []string
criticalSeverityAlerterParams string
rulesRepos []*model.RuleRepo
reposFolder string
isRunning bool
interm sync.Mutex
airgapEnabled bool
notify bool
writeNoRead *string
aiSummaries *sync.Map // map[string]*detections.AiSummary{}
showAiSummaries bool
aiRepoUrl string
aiRepoBranch string
aiRepoPath string
detections.SyncSchedulerParams
detections.IntegrityCheckerData
detections.IOManager
Expand Down Expand Up @@ -167,6 +178,17 @@ func (e *ElastAlertEngine) Init(config module.ModuleConfig) (err error) {
e.CommunityRulesImportErrorSeconds = module.GetIntDefault(config, "communityRulesImportErrorSeconds", DEFAULT_COMMUNITY_RULES_IMPORT_ERROR_SECS)
e.failAfterConsecutiveErrorCount = module.GetIntDefault(config, "failAfterConsecutiveErrorCount", DEFAULT_FAIL_AFTER_CONSECUTIVE_ERROR_COUNT)
e.additionalAlerters = module.GetStringArrayDefault(config, "additionalAlerters", []string{})
e.additionalAlerterParams = module.GetStringDefault(config, "additionalSev0AlertersParams", "")
e.informationalSeverityAlerters = module.GetStringArrayDefault(config, "additionalSev1Alerters", []string{})
e.informationalSeverityAlerterParams = module.GetStringDefault(config, "additionalSev1AlertersParams", "")
e.lowSeverityAlerters = module.GetStringArrayDefault(config, "additionalSev2Alerters", []string{})
e.lowSeverityAlerterParams = module.GetStringDefault(config, "additionalSev2AlertersParams", "")
e.mediumSeverityAlerters = module.GetStringArrayDefault(config, "additionalSev3Alerters", []string{})
e.mediumSeverityAlerterParams = module.GetStringDefault(config, "additionalSev3AlertersParams", "")
e.highSeverityAlerters = module.GetStringArrayDefault(config, "additionalSev4Alerters", []string{})
e.highSeverityAlerterParams = module.GetStringDefault(config, "additionalSev4AlertersParams", "")
e.criticalSeverityAlerters = module.GetStringArrayDefault(config, "additionalSev5Alerters", []string{})
e.criticalSeverityAlerterParams = module.GetStringDefault(config, "additionalSev5AlertersParams", "")
e.IntegrityCheckerData.FrequencySeconds = module.GetIntDefault(config, "integrityCheckFrequencySeconds", DEFAULT_INTEGRITY_CHECK_FREQUENCY_SECONDS)

pkgs := module.GetStringArrayDefault(config, "sigmaRulePackages", []string{"core", "emerging_threats_addon"})
Expand Down Expand Up @@ -427,7 +449,7 @@ func (e *ElastAlertEngine) SyncLocalDetections(ctx context.Context, detections [
continue
}

wrapped, err := wrapRule(det, eaRule, e.additionalAlerters)
wrapped, err := e.wrapRule(det, eaRule)
if err != nil {
continue
}
Expand Down Expand Up @@ -1075,7 +1097,7 @@ func (e *ElastAlertEngine) syncCommunityDetections(ctx context.Context, logger *
continue
}

rule, err = wrapRule(detect, rule, e.additionalAlerters)
rule, err = e.wrapRule(detect, rule)
if err != nil {
continue
}
Expand Down Expand Up @@ -1508,6 +1530,64 @@ func (e *ElastAlertEngine) MergeAuxiliaryData(detect *model.Detection) error {
return nil
}

func (e *ElastAlertEngine) getAdditionalAlerters(severity int) ([]string, string) {
// Start with default alerters
alerters := e.additionalAlerters
params := e.additionalAlerterParams

// Override if info or above severity
if severity > 0 {
if len(e.informationalSeverityAlerters) > 0 {
alerters = e.informationalSeverityAlerters
}
if len(e.informationalSeverityAlerterParams) > 0 {
params = e.informationalSeverityAlerterParams
}
}

// Override if low or above severity
if severity > 1 {
if len(e.lowSeverityAlerters) > 0 {
alerters = e.lowSeverityAlerters
}
if len(e.lowSeverityAlerterParams) > 0 {
params = e.lowSeverityAlerterParams
}
}

// Override if med or above severity
if severity > 2 {
if len(e.mediumSeverityAlerters) > 0 {
alerters = e.mediumSeverityAlerters
}
if len(e.mediumSeverityAlerterParams) > 0 {
params = e.mediumSeverityAlerterParams
}
}

// Override if high or crit severity
if severity > 3 {
if len(e.highSeverityAlerters) > 0 {
alerters = e.highSeverityAlerters
}
if len(e.highSeverityAlerterParams) > 0 {
params = e.highSeverityAlerterParams
}
}

// Override if crit severity
if severity > 4 {
if len(e.criticalSeverityAlerters) > 0 {
alerters = e.criticalSeverityAlerters
}
if len(e.criticalSeverityAlerterParams) > 0 {
params = e.criticalSeverityAlerterParams
}
}

return alerters, params
}

type CustomWrapper struct {
DetectionTitle string `yaml:"detection_title"`
DetectionPublicId string `yaml:"detection_public_id"`
Expand Down Expand Up @@ -1644,7 +1724,7 @@ func (dur *TimeFrame) UnmarshalYAML(unmarshal func(interface{}) error) error {
return err
}

func wrapRule(det *model.Detection, rule string, additionalAlerters []string) (string, error) {
func (e *ElastAlertEngine) wrapRule(det *model.Detection, rule string) (string, error) {
severities := map[model.Severity]int{
model.SeverityUnknown: 0,
model.SeverityInformational: 1,
Expand All @@ -1654,6 +1734,13 @@ func wrapRule(det *model.Detection, rule string, additionalAlerters []string) (s
model.SeverityCritical: 5,
}

alerters, params := e.getAdditionalAlerters(severities[det.Severity])
log.WithFields(log.Fields{
"sevNum": severities[det.Severity],
"sev": det.Severity,
"params": params,
}).Error("********** Got params")

sevNum := severities[det.Severity]
realert := TimeFrame{}
realert.SetSeconds(0)
Expand All @@ -1678,7 +1765,7 @@ func wrapRule(det *model.Detection, rule string, additionalAlerters []string) (s

if licensing.IsEnabled(licensing.FEAT_NTF) {
// Add any custom alerters to the rule.
for _, alerter := range additionalAlerters {
for _, alerter := range alerters {
alerter = strings.TrimSpace(alerter)
if len(alerter) > 0 {
wrapper.Alert = append(wrapper.Alert, alerter)
Expand All @@ -1690,8 +1777,15 @@ func wrapRule(det *model.Detection, rule string, additionalAlerters []string) (s
if err != nil {
return "", err
}
strYaml := string(rawYaml)

if licensing.IsEnabled(licensing.FEAT_NTF) {
if len(params) > 0 {
strYaml += "\n" + params + "\n"
}
}

return string(rawYaml), nil
return strYaml, nil
}

func (e *ElastAlertEngine) IntegrityCheck(canInterrupt bool, logger *log.Entry) (deployedButNotEnabled []string, enabledButNotDeployed []string, err error) {
Expand Down
65 changes: 61 additions & 4 deletions server/modules/elastalert/elastalert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,8 @@ func TestSigmaToElastAlertSunnyDay(t *testing.T) {
})).Return([]byte("<eql>"), 0, time.Duration(0), nil)

engine := ElastAlertEngine{
IOManager: iom,
IOManager: iom,
additionalAlerters: []string{"email", "slack"},
}

det := &model.Detection{
Expand All @@ -362,7 +363,7 @@ func TestSigmaToElastAlertSunnyDay(t *testing.T) {
assert.NoError(t, err)

// No license
wrappedRule, err := wrapRule(det, query, []string{"email", "slack"})
wrappedRule, err := engine.wrapRule(det, query)
assert.NoError(t, err)

expected := `detection_title: Test Detection
Expand Down Expand Up @@ -409,7 +410,9 @@ func TestSigmaToElastAlertSunnyDayLicensed(t *testing.T) {
})).Return([]byte("<eql>"), 0, time.Duration(0), nil)

engine := ElastAlertEngine{
IOManager: iom,
IOManager: iom,
additionalAlerters: []string{"email", "slack"},
additionalAlerterParams: "foo: bar",
}

det := &model.Detection{
Expand All @@ -424,7 +427,7 @@ func TestSigmaToElastAlertSunnyDayLicensed(t *testing.T) {

// License
licensing.Test(licensing.FEAT_NTF, 0, 0, "", "")
wrappedRule, err := wrapRule(det, query, []string{"email", "slack"})
wrappedRule, err := engine.wrapRule(det, query)
assert.NoError(t, err)

expected := `detection_title: Test Detection
Expand All @@ -444,10 +447,64 @@ realert:
seconds: 0
filter:
- eql: <eql>
foo: bar
`
assert.YAMLEq(t, expected, wrappedRule)
}

func TestAdditionalAlertersSev0(t *testing.T) {
engine := ElastAlertEngine{
additionalAlerters: []string{"email", "slack"},
additionalAlerterParams: "foo: bar",
}

for sev := range 6 {
alerters, params := engine.getAdditionalAlerters(sev)
assert.Equal(t, []string{"email", "slack"}, alerters)
assert.Equal(t, "foo: bar", params)
}
}

func TestAdditionalAlertersSev0Sev3(t *testing.T) {
engine := ElastAlertEngine{
additionalAlerters: []string{"email", "slack"},
additionalAlerterParams: "foo: bar",
mediumSeverityAlerters: []string{"teams"},
mediumSeverityAlerterParams: "foo: boo",
}

for sev := range 6 {
alerters, params := engine.getAdditionalAlerters(sev)
if sev < 3 {
assert.Equal(t, []string{"email", "slack"}, alerters)
assert.Equal(t, "foo: bar", params)
} else {
assert.Equal(t, []string{"teams"}, alerters)
assert.Equal(t, "foo: boo", params)
}
}
}

func TestAdditionalAlertersSev0Sev5(t *testing.T) {
engine := ElastAlertEngine{
additionalAlerters: []string{"email", "slack"},
additionalAlerterParams: "foo: bar",
criticalSeverityAlerters: []string{"teams"},
criticalSeverityAlerterParams: "foo: boo",
}

for sev := range 6 {
alerters, params := engine.getAdditionalAlerters(sev)
if sev < 5 {
assert.Equal(t, []string{"email", "slack"}, alerters)
assert.Equal(t, "foo: bar", params)
} else {
assert.Equal(t, []string{"teams"}, alerters)
assert.Equal(t, "foo: boo", params)
}
}
}

func TestSigmaToElastAlertError(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
Expand Down

0 comments on commit e36a9eb

Please sign in to comment.