Skip to content

Core: correlation keys on outcome, not category strings — brute_force_then_login + ids_then_brute_force generalized (ADR-0071 D3) #78

Description

@gterdem

Summary

Generalize the two correlation rules that are currently welded to one plugin's labels:
brute_force_then_login (the CRITICAL "probable compromise" rule) and ids_then_brute_force.
Their selectors move from private category strings and hard-coded source_type names to
contract fields with published vocabularies: outcome (#77) and ocsf_class (#76). Also
retires PR #73's marked-interim selector union.

Why

brute_force_then_login is the loudest verdict in the product — and it is load-bearing in the
intensity model: an attacker who succeeds stops attempting, so the intensity states fade at
the exact moment the situation got worse (the fade-on-success inversion, ADR-0070). Today the
rule only sees events labeled with the syslog plugin's exact strings, and ids_then_brute_force
discards non-syslog/non-suricata events before it even reads the category. Any new
auth-capable source is silently excluded from the path that catches compromise. After this
issue, every source that sets outcome + the Authentication class joins automatically — no
plugin names anywhere in core.

Context

Acceptance criteria

  • _brute_force_then_login SHALL select failures as
    ocsf_class == 3002 and outcome == "failure" and successes as
    ocsf_class == 3002 and outcome == "success"; thresholds and the 30-min window are
    verbatim unchanged (≥3 failures, ≥1 success after the last failure).
  • _ids_then_brute_force SHALL replace its named-source corroboration with: ≥1 ALERT
    event from a different source_type than the auth-failure events, coinciding within
    the existing 10-min window with ≥3 failed auth attempts (selector as above); thresholds
    and score deltas verbatim unchanged.
  • WHEN a linux_auth actor produces ≥3 outcome=failure auth events followed by an
    outcome=success within 30 min, brute_force_then_login SHALL fire (the PR source: Linux auth & intrusion signals (linux_auth) — local mode #73
    reachability scenario, now without the selector union).
  • Must-NOT: after this lands, no core detection rule SHALL compare
    SecurityEvent.category to a literal — verified mechanically:
    grep -n '"SSH Brute Force"\|"SSH Login"' packages/firewatch-core/src/ returns only
    comments/docstrings (PR source: Linux auth & intrusion signals (linux_auth) — local mode #73's burst/intense pair is exempt only if Core: attempt intensity — attempts.py predicate + decayed-rate estimator + attempt_pressure (retires sustained_attack) #53/Core: attack_in_progress + campaign — intensity queue entry (HIGH ALERT) via the assertion gate #54 have not yet
    retired it; those rules carry their own retirement).
  • Must-NOT: rule names, registered severities, auto_escalate flags, score deltas, and
    ESCALATION_POLICY entries SHALL NOT change.
  • Must-NOT: tests/golden/fixtures/expected_scores.json SHALL NOT move
    (detection_rule_names: [] is pinned throughout; any drift is a regression by
    definition).
  • Must-NOT: events with outcome=None or a non-3002 class SHALL NOT be selected as
    auth failures/successes (no fabricated reachability — a source that doesn't declare
    outcomes stays out, by design).
  • Tests SHALL cover: the syslog-only path (unchanged behavior), the linux_auth path
    (new reachability), a mixed-source corroboration case, and the must-NOTs above.

Out of scope

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Priority: core of its milestoneenhancementNew feature or request

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions