Add enrichment and SID coverage gates
This commit is contained in:
parent
93ac47dd82
commit
66472134d8
32 changed files with 1623 additions and 31 deletions
73
tests/test_sid_coverage.py
Normal file
73
tests/test_sid_coverage.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Emission-based coverage: every built-in SID has a fixture or drill.
|
||||
|
||||
Event-rule SIDs are covered by JSON fixtures under tests/fixtures/sids/
|
||||
(replayable by operators via `enodia-sentinel rules test <file>`); the other
|
||||
engines are covered by drills in tests/sid_drills.py. This module replays
|
||||
and runs all of them for real and asserts the emitted sids match the
|
||||
registry in enodia_sentinel.sids.
|
||||
"""
|
||||
import json
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from enodia_sentinel import ruleops, sids
|
||||
from enodia_sentinel.config import Config
|
||||
from tests.sid_drills import DRILLS
|
||||
|
||||
FIXTURE_DIR = Path(__file__).resolve().parent / "fixtures" / "sids"
|
||||
|
||||
|
||||
def fixture_sids() -> dict[int, set[int]]:
|
||||
"""Replay every fixture; map declared sid to sids actually emitted."""
|
||||
out: dict[int, set[int]] = {}
|
||||
for path in sorted(FIXTURE_DIR.glob("*.json")):
|
||||
declared = int(path.name.split("-", 1)[0])
|
||||
event = json.loads(path.read_text())
|
||||
alerts = ruleops.test_event(Config(), event)
|
||||
out[declared] = {a.sid for a in alerts}
|
||||
return out
|
||||
|
||||
|
||||
class TestEventFixtures(unittest.TestCase):
|
||||
def test_every_event_rule_sid_has_an_emitting_fixture(self):
|
||||
covered = fixture_sids()
|
||||
wanted = sids.engine_sids("exec_rule") | sids.engine_sids("syscall_rule")
|
||||
for sid in sorted(wanted):
|
||||
self.assertIn(sid, covered,
|
||||
f"no fixture file for sid {sid} in {FIXTURE_DIR}")
|
||||
self.assertIn(sid, covered[sid],
|
||||
f"fixture for sid {sid} did not emit it")
|
||||
|
||||
def test_fixtures_emit_only_registered_sids(self):
|
||||
registry = sids.all_sids()
|
||||
for declared, emitted in fixture_sids().items():
|
||||
self.assertLessEqual(emitted, registry,
|
||||
f"fixture {declared} emitted unregistered sids")
|
||||
|
||||
|
||||
def drill_sids() -> dict[int, set[int]]:
|
||||
"""Run every non-event drill; map declared sid to sids emitted."""
|
||||
return {sid: {a.sid for a in drill()} for sid, drill in sorted(DRILLS.items())}
|
||||
|
||||
|
||||
class TestSidCoverageGate(unittest.TestCase):
|
||||
def test_every_non_event_sid_has_an_emitting_drill(self):
|
||||
covered = drill_sids()
|
||||
event_sids = sids.engine_sids("exec_rule") | sids.engine_sids("syscall_rule")
|
||||
wanted = sids.all_sids() - event_sids
|
||||
self.assertEqual(set(covered), wanted)
|
||||
for sid in sorted(wanted):
|
||||
self.assertIn(sid, covered[sid], f"drill for sid {sid} did not emit it")
|
||||
|
||||
def test_fixtures_and_drills_equal_registry(self):
|
||||
emitted = set()
|
||||
for sids_for_fixture in fixture_sids().values():
|
||||
emitted.update(sids_for_fixture)
|
||||
for sids_for_drill in drill_sids().values():
|
||||
emitted.update(sids_for_drill)
|
||||
self.assertEqual(emitted, sids.all_sids())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue