Skip to content

fix(rule-flood-guard): scope flood detection and rule disabling per tenant - #2495

Merged
yllada merged 2 commits into
release/v12.0.0from
backlog/v12_rule_flood_guard_multitenant
Aug 20, 2026
Merged

fix(rule-flood-guard): scope flood detection and rule disabling per tenant#2495
yllada merged 2 commits into
release/v12.0.0from
backlog/v12_rule_flood_guard_multitenant

Conversation

@yllada

@yllada yllada commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

The problem

rule-flood-guard watches for correlation rules that flood the alert list and
disables them. It was doing both halves of that job across tenant boundaries.

Counting. The ClickHouse aggregation grouped by name and dataSource
only. The SDK driver renders those fields as a literal GROUP BY, so every
tenant's alerts landed in the same bucket:

Tenant A: 30 alerts. Tenant B: 25 alerts. Counted as 55, tripping a
threshold of 50 that neither of them reached on their own.

Disabling. Both backend calls carried X-Internal-Key and no
X-Tenant-Id. The middleware treats an internal caller without that header as
tenantless, and engine_config.go maps an empty tenant to the platform tenant.
Since RuleStore.SetEnabled writes into a per-tenant DisabledRules list, the
disable was recorded against the platform tenant while the tenant that actually
flooded kept flooding.

search-by-filters had the mirror image of the same problem: with no tenant it
returns every tenant's rules, so a rule name shared across tenants could get
another customer's rule disabled, and the ruleActive flag was read from the
wrong tenant's view — skipping rules that were live and flooding for a customer
because the platform tenant already had them off.

What changed

File Change
aggregation.go tenantId added as the first GROUP BY field. New bucketsFromGroups flattens the nested tree by reading levels through Group.Field rather than by depth.
backend.go X-Tenant-Id set on rule lookup, deactivation and notification. Notify reaches the offending tenant, with a copy to the platform tenant unless they are the same.
guard.go Buckets with no tenant are dropped with a warning instead of reaching the backend, where an empty tenant would have resolved to the platform tenant.
README.md Documents the per-tenant scoping of counting, disabling and notifying.

Two smaller corrections came along with it. The notification now names the
offending tenant and reports the observed count instead of the threshold, and
it interpolates the configured windowHours — the template said "in the last
24h" unconditionally, which misreported the period whenever an operator changed
the window.

@github-actions

Copy link
Copy Markdown

🛑 AI review — Sensitive area, extra care recommended

This PR touches critical paths or introduces changes the model cannot judge with sufficient confidence. Review carefully before merging.

architecture (gemini-3-flash-lite) — clean

Summary: Rule flood guard updated for tenant isolation; implementation is clean and adheres to existing store and plugin patterns.

No findings.

bugs (gemini-3-flash-lite) — clean

Summary: Tenant-scoping added to rule-flood-guard without introducing concrete bugs.

No findings.

🛑 security (gemini-3-flash-lite) — high/critical — please review

Summary: Rule flood guard plugin updated for multitenancy, touching backend authorization headers and tenant isolation paths.

No findings.

🔴 go-deps — pending updates

🔍 Discovered 30 Go projects

📦 Dependencies with updates available:

  📁 ./plugins/crowdstrike:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/azure:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/soc-ai:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/events:
     - github.com/threatwinds/go-sdk: v1.1.27-0.20260811073440-251cb9d842cd → v1.1.31

  📁 ./plugins/gcp:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/bitdefender:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/o365:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/rule-flood-guard:
     - github.com/threatwinds/go-sdk: v1.1.27-0.20260811073440-251cb9d842cd → v1.1.31

  📁 ./plugins/stats:
     - github.com/threatwinds/go-sdk: v1.1.27-0.20260811073440-251cb9d842cd → v1.1.31

  📁 ./plugins/feeds:
     - github.com/threatwinds/go-sdk: v1.1.27-0.20260811073440-251cb9d842cd → v1.1.31

  📁 ./plugins/geolocation:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/playground:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/soar:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/sophos:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./plugins/alerts:
     - github.com/threatwinds/go-sdk: v1.1.27-0.20260811073440-251cb9d842cd → v1.1.31

  📁 ./plugins/aws:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./log-input:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31
     - google.golang.org/grpc: v1.83.0 → v1.83.1

  📁 ./backend:
     - github.com/aws/aws-sdk-go-v2/config: v1.32.36 → v1.32.37
     - github.com/aws/aws-sdk-go-v2/credentials: v1.19.35 → v1.19.36
     - github.com/aws/aws-sdk-go-v2/service/sts: v1.45.5 → v1.45.6
     - google.golang.org/grpc: v1.83.0 → v1.83.1

  📁 ./collectors/collector:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31
     - google.golang.org/grpc: v1.83.0 → v1.83.1

  📁 ./collectors/forwarder:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31
     - google.golang.org/grpc: v1.83.0 → v1.83.1

  📁 ./collectors/as400:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31
     - google.golang.org/grpc: v1.83.0 → v1.83.1

  📁 ./collectors/utmstack:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31
     - google.golang.org/grpc: v1.83.0 → v1.83.1

  📁 ./tools/rulecheck:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31

  📁 ./agent-manager:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31
     - google.golang.org/grpc: v1.83.0 → v1.83.1

  📁 ./agent:
     - github.com/threatwinds/go-sdk: v1.1.28 → v1.1.31
     - google.golang.org/grpc: v1.83.0 → v1.83.1

❌ Please update dependencies before merging.

@yllada
yllada merged commit 1aef4bf into release/v12.0.0 Aug 20, 2026
1 check passed
@yllada
yllada deleted the backlog/v12_rule_flood_guard_multitenant branch August 20, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant