diff --git a/html/images/appliances/GOFASTv2_back_thumb.jpg b/html/images/appliances/GOFASTv2_back_thumb.jpg new file mode 100644 index 000000000..02751ecdc Binary files /dev/null and b/html/images/appliances/GOFASTv2_back_thumb.jpg differ diff --git a/html/images/appliances/GOFASTv2_front_thumb.jpg b/html/images/appliances/GOFASTv2_front_thumb.jpg new file mode 100644 index 000000000..fd6d64b1a Binary files /dev/null and b/html/images/appliances/GOFASTv2_front_thumb.jpg differ diff --git a/server/modules/elastalert/elastalert.go b/server/modules/elastalert/elastalert.go index 8d374c492..ddfd2c1be 100644 --- a/server/modules/elastalert/elastalert.go +++ b/server/modules/elastalert/elastalert.go @@ -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 @@ -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"}) @@ -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 } @@ -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 } @@ -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"` @@ -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, @@ -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) @@ -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) @@ -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) { diff --git a/server/modules/elastalert/elastalert_test.go b/server/modules/elastalert/elastalert_test.go index 4f852609f..1dc373098 100644 --- a/server/modules/elastalert/elastalert_test.go +++ b/server/modules/elastalert/elastalert_test.go @@ -339,7 +339,8 @@ func TestSigmaToElastAlertSunnyDay(t *testing.T) { })).Return([]byte(""), 0, time.Duration(0), nil) engine := ElastAlertEngine{ - IOManager: iom, + IOManager: iom, + additionalAlerters: []string{"email", "slack"}, } det := &model.Detection{ @@ -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 @@ -409,7 +410,9 @@ func TestSigmaToElastAlertSunnyDayLicensed(t *testing.T) { })).Return([]byte(""), 0, time.Duration(0), nil) engine := ElastAlertEngine{ - IOManager: iom, + IOManager: iom, + additionalAlerters: []string{"email", "slack"}, + additionalAlerterParams: "foo: bar", } det := &model.Detection{ @@ -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 @@ -444,10 +447,64 @@ realert: seconds: 0 filter: - 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()