Size S · depends on: none
Goal
Let an operator create one exclusion that applies to every rule supporting that match type, instead of creating the same exclusion once per rule.
Why
The backend already does this and nothing exposes it. Snapshot.Excluded falls back to shared entries when a rule-specific one does not match:
// server/rules/internal/detectionconfig/snapshot.go
func (s *Snapshot) Excluded(ruleID string, matchType api.ExclusionMatchType, value, hostID string) bool {
if s.matchAny(ruleID, matchType, value, hostID) {
return true
}
return ruleID != "" && s.matchAny("", matchType, value, hostID)
}
An exclusion stored with an empty rule_id already matches all rules, the create API accepts it, and the resolver honours it. The only thing missing is a way for an operator to ask for it.
This is also the shape the market treats as the common case: SentinelOne and CrowdStrike both default to blanket exclusions scoped by host, not per-detection. "Our build tool is benign everywhere" is the request operators actually make, and today it costs them one exclusion per rule and gets stale the moment a rule is added.
Scope
- UI: the create-exclusion form gains a scope choice, "This rule only" (default) or "All rules". The all-rules option sends
rule_id: "".
- UI: the exclusion list distinguishes rule-scoped from all-rules entries, so a blanket exclusion is never mistaken for a narrow one.
- API docs: document the empty-
rule_id semantics, which are currently only discoverable by reading the resolver.
- Keep the existing safety guidance visible: prefer the narrowest scope, and prefer hash or signer (
sha256, cdhash, team_id, signing_id) over path_glob.
Acceptance criteria
- An operator can create an exclusion that applies to every rule supporting the chosen match type, without naming a rule.
- The exclusion list shows, per entry, whether it is scoped to one rule or to all rules.
- An all-rules exclusion demonstrably suppresses findings in more than one rule.
- Creating one still requires a reason, as today.
Tests
- UI: the scope selector renders, defaults to rule-scoped, and the all-rules choice submits an empty
rule_id.
- UI: the list view labels both kinds distinctly.
- Integration: one all-rules exclusion suppresses findings from two different rules that share a match type; a rule-scoped one suppresses only its own.
Out of scope
Size S · depends on: none
Goal
Let an operator create one exclusion that applies to every rule supporting that match type, instead of creating the same exclusion once per rule.
Why
The backend already does this and nothing exposes it.
Snapshot.Excludedfalls back to shared entries when a rule-specific one does not match:An exclusion stored with an empty
rule_idalready matches all rules, the create API accepts it, and the resolver honours it. The only thing missing is a way for an operator to ask for it.This is also the shape the market treats as the common case: SentinelOne and CrowdStrike both default to blanket exclusions scoped by host, not per-detection. "Our build tool is benign everywhere" is the request operators actually make, and today it costs them one exclusion per rule and gets stale the moment a rule is added.
Scope
rule_id: "".rule_idsemantics, which are currently only discoverable by reading the resolver.sha256,cdhash,team_id,signing_id) overpath_glob.Acceptance criteria
Tests
rule_id.Out of scope
read_only_in_phase_aand there is no membership population, so a group cannot be created to scope to.