Skip to content

Contract/SDK: SecurityEvent.outcome — the auth-outcome vocabulary (ADR-0071 D1/D2/D4) #77

Description

@gterdem

Summary

Add one field to the plugin contract's event model: SecurityEvent.outcome —
"success" | "failure" | "unknown" | None — stating whether the event's attempt succeeded,
from the producing source's perspective. Anchored verbatim to ECS event.outcome; exported as
OCSF status_id. Populate it in the syslog-family normalizers, and give the contract's
category field the honest semantics it has always lacked.

Why

FireWatch's CRITICAL detection ("brute force, then a successful login — probable compromise")
needs to know which events are failures and which are successes. Today that fact exists
only inside each plugin's private category strings — which is why the core rule is coupled to
one plugin's labels, and why a fully contract-conformant source (linux_auth, PR #73) shipped
with zero ability to reach the CRITICAL rule. The contract mandates category but defines no
vocabulary for it; outcome carries the actual fact, with two published standards behind it.
This makes "adding a source requires zero core edits" true in the sense that counts: the next
auth-capable source joins the CRITICAL path by setting values it already knows.

Context

Acceptance criteria

  • SecurityEvent SHALL gain outcome: Literal["success","failure","unknown"] | None = None
    (additive, defaulted — existing plugins remain conformant unchanged).
  • The store SHALL gain the matching nullable column via the established idempotent additive
    migration pattern; FilterSpec MAY gain outcome filtering (cheap here, else follow-up).
  • The syslog normalizer SHALL set: "SSH Brute Force" → failure, "SSH Login" → success,
    "Sudo Failure" → failure; the syslog_cef fallback path SHALL mirror it.
  • linux_auth (post-PR source: Linux auth & intrusion signals (linux_auth) — local mode #73 merge) SHALL set outcome on its failure/success/sudo/pam rows in
    this issue's diff.
  • WHEN the OCSF export serializes an event whose class carries status_id, it SHALL map
    success→1, failure→2, unknown→0, and SHALL omit the attribute when outcome=None.
  • PLUGIN_CONTRACT.md SHALL gain (v1.5): outcome in the normalize() MUST-set-where-known
    list with the ECS definitions quoted verbatim, and a category semantics subsection —
    category is a stable, human-readable, per-source label; core detection logic MUST NOT key
    on it (with the two named interim exceptions and their retirement pointer).
  • Must-NOT: no normalizer SHALL fabricate an outcome (Suricata / Azure WAF / AWS NFW /
    ClamAV set nothing in this issue; events without a knowable result stay None, never
    defaulted to failure).
  • Must-NOT: tests/golden/fixtures/expected_scores.json SHALL NOT move; outcome
    assertions in normalize goldens are additive new pins, not a re-bless.
  • Collection modes: mode-agnostic — model + normalize() + export only.

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