# SID Drill Coverage Design **Status:** Implemented — registry, event fixtures, offline drills, and the emission-based coverage gate are in the test suite. **Goal:** Fixture or drill coverage for every built-in SID, enforced by a CI gate that cannot rot: adding a detection without a drill fails the suite. ## Context Enodia Sentinel has 52 built-in SIDs across seven engines: | Range | Engine | |---|---| | 100001–100004 | eBPF exec rules (`events/rules.py`) | | 100010–100016 | poll detectors (inline sids) | | 100017–100021, 100026–100027 | FIM / pkgdb / pkgverify | | 100022–100025, 100028–100031, 100034–100035, 100037–100038 | rootcheck | | 100032–100033, 100036, 100039, 100048 | poll detectors (constants) | | 100040–100047, 100050–100051 | posture | | 100060–100066 | eBPF syscall rules (`events/syscall_rules.py`) | Existing engine test files cover most behavior but nothing proves per-SID completeness, and the bash red-team harness covers only ~9 core signatures. ## Decisions (from brainstorming Q&A) 1. **Deliverable:** coverage gate + fixtures (not fixtures-only, not bash harness extension). 2. **Registry:** new product module `enodia_sentinel/sids.py` — aggregates existing constants by importing them (no renumbering, no moving definitions) into a `BUILTIN_SIDS` table: `sid`, `name`, `engine`, defining module. 3. **Gate proof:** emission-based. The gate runs every drill for real and asserts the union of emitted alert `sid`s equals the registry exactly, in both directions. ## Components - `enodia_sentinel/sids.py` — canonical SID table plus a sanity test (uniqueness, matches imported constants). - `tests/fixtures/sids/-.json` — one event JSON per exec-rule and syscall-rule SID, in the exact format `rules test` accepts, so each fixture doubles as an operator drill (`enodia-sentinel rules test tests/fixtures/sids/100001-*.json`). - `tests/sid_drills.py` — `DRILLS: dict[int, Callable[[], list[Alert]]]` for non-event engines. Each drill builds minimal fake state (fake `SystemState`, monkeypatched rootcheck views, temp FIM tree, posture file fakes — same styles the existing tests use) and invokes the real engine code. - `tests/test_sid_coverage.py` — the gate: replays all fixtures through the real rule engines, runs all drills, and asserts emitted sids == registry. Failure messages name the uncovered or unregistered SID. ## Docs - `docs/RULES.md`: short "drill fixtures" note documenting the fixture naming convention and replay command. - `docs/ROADMAP.md`: tick the per-SID fixture/drill item when implemented. ## Out of scope (YAGNI) - Extending the bash red-team harness — live-host drills for rootkit-module, kernel-taint, and pkg-DB-tamper SIDs are not safely simulable on a real host. - Correlation SIDs — none exist yet; the gate will catch them when they land. ## Execution note Implement in an isolated git worktree: a concurrent session holds uncommitted edits in this checkout (triage/enrichment work touching `cli.py`, `respond.py`, `snapshot.py`, event sources, and docs).