From d32b7383266fc50cd7491affb87f7d0e4a7b5633 Mon Sep 17 00:00:00 2001
From: Sergey Wh1te <sergey.i@lido.fi>
Date: Mon, 9 Dec 2024 14:17:59 +0300
Subject: [PATCH] feature: Filter only desired Findings

---
 Changelog.md                        |  3 +++
 internal/env/notify_config.go       | 23 +++++++++++++------
 internal/pkg/consumer/consumer.go   | 35 +++++++++++++++++++----------
 internal/utils/registry/registry.go |  1 +
 notification.sample.yaml            | 19 ++++++++++++++++
 5 files changed, 62 insertions(+), 19 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 6e44ce5..59947a9 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,6 @@
+## 09.12.2024
+1. Dynamic config feature: filter only desired Findings.
+
 ## 05.12.2024
 1. Remove PublishedAlerts metrics
 2. Added NotifyChannels: `forwarder_notification_channel_error_total`
diff --git a/internal/env/notify_config.go b/internal/env/notify_config.go
index 0fd4207..d232eba 100644
--- a/internal/env/notify_config.go
+++ b/internal/env/notify_config.go
@@ -42,13 +42,15 @@ type OpsGenieChannel struct {
 }
 
 type Consumer struct {
-	ConsumerName string   `mapstructure:"consumerName"`
-	Type         string   `mapstructure:"type"`
-	ChannelID    string   `mapstructure:"channel_id"`
-	Severities   []string `mapstructure:"severities"`
-	ByQuorum     bool     `mapstructure:"by_quorum"`
-	Subjects     []string `mapstructure:"subjects"`
-	SeveritySet  registry.FindingMapping
+	ConsumerName     string   `mapstructure:"consumerName"`
+	Type             string   `mapstructure:"type"`
+	ChannelID        string   `mapstructure:"channel_id"`
+	Severities       []string `mapstructure:"severities"`
+	ByQuorum         bool     `mapstructure:"by_quorum"`
+	Subjects         []string `mapstructure:"subjects"`
+	Filter           []string `mapstructure:"filter"`
+	SeveritySet      registry.FindingMapping
+	FindingFilterMap registry.FindingFilterMap
 }
 
 type NotificationConfig struct {
@@ -150,6 +152,8 @@ func ValidateConfig(cfg *NotificationConfig) error {
 
 	for _, consumer := range cfg.Consumers {
 		severitySet := make(registry.FindingMapping)
+		findingFilter := make(registry.FindingFilterMap)
+
 		for _, severity := range consumer.Severities {
 			if _, exists := validSeverities[databus.Severity(severity)]; !exists {
 				return fmt.Errorf("consumer '%s' references an unknown severity level '%s'", consumer.ConsumerName, severity)
@@ -157,7 +161,12 @@ func ValidateConfig(cfg *NotificationConfig) error {
 			severitySet[databus.Severity(severity)] = true
 		}
 
+		for _, alertID := range consumer.Filter {
+			findingFilter[alertID] = true
+		}
+
 		consumer.SeveritySet = severitySet
+		consumer.FindingFilterMap = findingFilter
 	}
 
 	for _, consumer := range cfg.Consumers {
diff --git a/internal/pkg/consumer/consumer.go b/internal/pkg/consumer/consumer.go
index 9356be3..c4efed0 100644
--- a/internal/pkg/consumer/consumer.go
+++ b/internal/pkg/consumer/consumer.go
@@ -33,12 +33,13 @@ type Consumer struct {
 	redisClient *redis.Client
 	repo        *Repo
 
-	name        string
-	subject     string
-	severitySet registry.FindingMapping
-	byQuorum    bool
-	quorumSize  uint
-	notifier    notifiler.FindingSender
+	name             string
+	subject          string
+	severitySet      registry.FindingMapping
+	byQuorum         bool
+	quorumSize       uint
+	findingFilterMap registry.FindingFilterMap
+	notifier         notifiler.FindingSender
 }
 
 func New(
@@ -50,6 +51,7 @@ func New(
 	consumerName,
 	subject string,
 	SeveritySet registry.FindingMapping,
+	findingFilterMap registry.FindingFilterMap,
 	byQuorum bool,
 	quorumSize uint,
 	notifier notifiler.FindingSender,
@@ -61,12 +63,13 @@ func New(
 		redisClient: redisClient,
 		repo:        repo,
 
-		name:        consumerName,
-		subject:     subject,
-		severitySet: SeveritySet,
-		byQuorum:    byQuorum,
-		quorumSize:  quorumSize,
-		notifier:    notifier,
+		name:             consumerName,
+		subject:          subject,
+		severitySet:      SeveritySet,
+		findingFilterMap: findingFilterMap,
+		byQuorum:         byQuorum,
+		quorumSize:       quorumSize,
+		notifier:         notifier,
 	}
 }
 
@@ -116,6 +119,7 @@ func NewConsumers(log *slog.Logger, metrics *metrics.Store, redisClient *redis.C
 				consumerName,
 				subject,
 				consumerCfg.SeveritySet,
+				consumerCfg.FindingFilterMap,
 				consumerCfg.ByQuorum,
 				quorumSize,
 				notificationChannel,
@@ -172,6 +176,13 @@ func (c *Consumer) GetConsumeHandler(ctx context.Context) func(msg jetstream.Msg
 			return
 		}
 
+		if len(c.findingFilterMap) > 0 {
+			if _, ok := c.findingFilterMap[finding.AlertId]; !ok {
+				c.ackMessage(msg)
+				return
+			}
+		}
+
 		if c.byQuorum == false {
 			if sendErr := c.notifier.SendFinding(ctx, finding); sendErr != nil {
 				c.log.Error(fmt.Sprintf(`Could not send bot-finding: %v`, sendErr),
diff --git a/internal/utils/registry/registry.go b/internal/utils/registry/registry.go
index 14ba4b1..4bfa8fa 100644
--- a/internal/utils/registry/registry.go
+++ b/internal/utils/registry/registry.go
@@ -37,3 +37,4 @@ var CodeOwners = map[string]string{
 }
 
 type FindingMapping = map[databus.Severity]bool
+type FindingFilterMap = map[string]bool
diff --git a/notification.sample.yaml b/notification.sample.yaml
index de35daf..209b09b 100644
--- a/notification.sample.yaml
+++ b/notification.sample.yaml
@@ -31,6 +31,9 @@ discord_channels:
   - id: Discord2
     description: Discord channel for all messages >= Low, no quorum
     webhook_url: YOUR_DISCORD_WEBHOOK_URL_2
+  - id: AnalyticsSubscriber
+    description: "Analytics channel for getting only specific findings (filter: WITHDRAWALS-BIG-WITHDRAWAL-REQUEST-BATCH)"
+    webhook_url: YOUR_DISCORD_WEBHOOK_URL_2
 
 opsgenie_channels:
   - id: OpsGenie1
@@ -125,3 +128,19 @@ consumers:
       - findings.protocol.steth
       - findings.protocol.arb
       - findings.dao.opt
+
+  - consumerName: AnalyticsSubscriber
+    type: Discord
+    channel_id: AnalyticsSubscriber
+    severities:
+      - Low
+      - Info
+      - Medium
+      - High
+      - Critical
+    by_quorum: True
+    subjects:
+      - findings.protocol.steth
+    filter:
+      - WITHDRAWALS-BIG-WITHDRAWAL-REQUEST-BATCH
+