Skip to content

Commit 96f832f

Browse files
Add runnable audience-targeted demo scenarios + architecture/demos docs
Five narrated demos (ISSO/ISSM, SOC, sysadmin/DevSecOps, auditor, edge/air-gap) drive the real comint_osquery/cognis_mil API over bundled offline fixtures and the committed feed cache; each exits 0 and is covered by tests. Adds docs/ARCHITECTURE.md (mermaid pipeline + fleet + data-model) and docs/DEMOS.md (audience table), a Demos section + diagram in README, and a pytest module that executes every scenario. Fix pre-existing demo-dir test to skip __pycache__.
1 parent 859cb9e commit 96f832f

12 files changed

Lines changed: 619 additions & 2 deletions

README.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,40 @@ pip install -e ../../shared
114114
pip install -e .
115115
```
116116

117-
## Demos — real-use-case library
117+
## Demos
118+
119+
Five runnable, narrated scenarios in [`demos/`](demos/) drive the **real**
120+
`comint-osquery` / `cognis_mil` API over the bundled offline fixtures — no
121+
network, no fabricated output, each exits 0. Each targets a different audience.
122+
Full write-up: [`docs/DEMOS.md`](docs/DEMOS.md).
123+
124+
```bash
125+
PYTHONUTF8=1 python demos/run_all.py # all five, end to end
126+
PYTHONUTF8=1 python demos/03_sysadmin_fleet.py # or just one
127+
```
128+
129+
| # | Scenario | Audience | What it shows |
130+
|---|----------|----------|---------------|
131+
| 1 | `01_isso_assessment.py` | **ISSO / ISSM** | Scan → composite risk + full RMF crosswalk → OSCAL 1.1.2 SAR for eMASS |
132+
| 2 | `02_soc_detection.py` | **SOC / endpoint** | Emit the scheduled osquery STIG pack; map every query to its ATT&CK technique |
133+
| 3 | `03_sysadmin_fleet.py` | **Sysadmins / DevSecOps** | Fleet correlation (systemic vs isolated) + golden-baseline drift |
134+
| 4 | `04_auditor_poam.py` | **Auditors / assessors** | eMASS POA&M workbook + tamper-evident hash-chained audit trail |
135+
| 5 | `05_airgap_enrichment.py` | **Edge / air-gap** | Resolve official NIST titles + ATT&CK→CTID countermeasures, fully offline |
136+
137+
How a scan flows end to end (full diagram in [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)):
138+
139+
```mermaid
140+
flowchart LR
141+
osq[osquery agent<br/>scheduled pack] -->|JSON| scan[scan / scan_fleet]
142+
stig[(STIG_PACK<br/>NIST+STIG+CCI+ATT&CK)] --> scan
143+
scan --> findings[Findings + risk score]
144+
findings --> exp[6 exporters<br/>console/json/sarif/md/oscal/csv]
145+
findings --> corr[fleet correlation<br/>+ POA&M]
146+
findings --> enrich[offline feed enrich<br/>OSCAL 800-53 + ATT&CK<->NIST]
147+
findings --> audit[(hash-chained<br/>audit log)]
148+
```
149+
150+
### Fixture library
118151

119152
Each `demos/<NN-name>/` holds osquery snapshot JSON in the tool's real input
120153
shape (`{query_name: [failing rows…]}`) plus a `SCENARIO.md` that explains where

demos/01_isso_assessment.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""Scenario 1 - ISSO / ISSM: turn host telemetry into an RMF assessment.
2+
3+
The ISSO's question is never "is this one setting wrong?" — it is "what is my
4+
control posture, what's the composite risk, and can I hand an AO an OSCAL
5+
artifact?" This demo scans an un-hardened host fixture with the REAL
6+
``comint_osquery.core.scan`` and shows the full RMF crosswalk every finding
7+
already carries (NIST 800-53 control + DISA STIG rule + CCI + ATT&CK technique),
8+
then renders OSCAL 1.1.2 Assessment Results — the package an ISSO actually
9+
uploads to eMASS.
10+
11+
Offline: reads the bundled ``demos/01-failing-host`` osquery snapshot fixture.
12+
"""
13+
import json
14+
15+
from _common import fixture, rule, section
16+
from comint_osquery.core import scan
17+
from cognis_mil import to_console, to_oscal_skeleton
18+
19+
20+
def main() -> None:
21+
rule("ISSO / ISSM - host telemetry -> RMF control posture -> OSCAL")
22+
23+
target = fixture("01-failing-host")
24+
print("\nScanning an un-hardened Ubuntu host snapshot (osquery JSON results).")
25+
print(f" target: demos/01-failing-host/\n")
26+
27+
result = scan(target)
28+
29+
print(f"Composite risk: {result.composite_score}/100 ({result.risk_level})")
30+
print(f"Findings: {result.total_findings()} over {result.items_scanned} snapshot file(s)\n")
31+
32+
section("Each finding carries its full RMF crosswalk")
33+
print(f" {'CAT':<10} {'NIST':<10} {'DISA STIG':<11} {'CCI':<13} {'ATT&CK':<11} Title")
34+
for f in result.findings:
35+
print(f" {f.severity.value.upper():<10} {f.nist_800_53:<10} {f.disa_stig:<11} "
36+
f"{(f.cci or '-'):<13} {(f.mitre_attack or '-'):<11} {f.title}")
37+
38+
section("OSCAL 1.1.2 Assessment Results (the eMASS-ingestible artifact)")
39+
oscal = json.loads(to_oscal_skeleton(result))
40+
ar = oscal["assessment-results"]
41+
res0 = ar["results"][0]
42+
print(f" oscal-version : {ar['metadata']['oscal-version']}")
43+
print(f" classification: {ar['metadata']['props'][0]['value']}")
44+
print(f" observations : {len(res0['observations'])}")
45+
print(f" findings : {len(res0['findings'])} (each target status = not-satisfied)")
46+
f0 = res0["findings"][0]
47+
print(f" e.g. finding : '{f0['title']}' -> control {f0['target']['target-id']} "
48+
f"[{f0['target']['status']['state']}]")
49+
print("\n UUIDs are deterministic (uuid5), so re-scans diff cleanly in the RMF package.")
50+
51+
print("\nThe ISSO now has a scored posture and an OSCAL package to attach to the SAR.")
52+
53+
54+
if __name__ == "__main__":
55+
main()

demos/02_soc_detection.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
"""Scenario 2 - SOC / endpoint detection engineering: ship the osquery pack.
2+
3+
A SOC doesn't want a one-off scan — it wants the *scheduled* query pack loaded
4+
into the osquery fleet agent so the weak configs surface continuously, and it
5+
wants every query mapped to the MITRE ATT&CK technique the weak config enables,
6+
so the detection lands in the right place on the ATT&CK matrix.
7+
8+
This demo emits the REAL osquery YAML pack with ``core.emit_query_pack`` and
9+
prints the ATT&CK coverage the ``STIG_PACK`` provides — straight from the
10+
shipped pack metadata, no fabricated techniques.
11+
12+
Offline: no network, no files written (pack is returned as a string).
13+
"""
14+
from collections import defaultdict
15+
16+
from _common import rule, section
17+
from comint_osquery.core import STIG_PACK, emit_query_pack
18+
19+
20+
def main() -> None:
21+
rule("SOC / ENDPOINT - schedule the pack, map every query to ATT&CK")
22+
23+
section("osquery scheduled-query pack (load into the fleet agent)")
24+
pack = emit_query_pack()
25+
# show the header + the first scheduled query so the shape is concrete
26+
shown = 0
27+
for line in pack.splitlines():
28+
print(" " + line)
29+
if line.strip().startswith("snapshot:"):
30+
shown += 1
31+
if shown >= 2:
32+
print(" ...")
33+
break
34+
print(f"\n {len(STIG_PACK)} STIG-aligned queries, interval=3600s, snapshot mode.")
35+
print(" Load with: osqueryi --config_path=stig_pack.yaml")
36+
37+
section("ATT&CK coverage (technique <- the weak config that enables it)")
38+
by_tech = defaultdict(list)
39+
for name, cfg in STIG_PACK.items():
40+
by_tech[cfg.get("attack", "")].append((name, cfg))
41+
for tech in sorted(by_tech):
42+
rows = by_tech[tech]
43+
cfg0 = rows[0][1]
44+
print(f" {tech:<12} {cfg0['severity'].value.upper():<10} "
45+
f"({len(rows)} query/queries)")
46+
for name, cfg in rows:
47+
print(f" - {name} [{cfg['nist']} / {cfg['stig']}]")
48+
49+
techniques = sorted({c['attack'] for c in STIG_PACK.values() if c.get('attack')})
50+
print(f"\n {len(techniques)} distinct ATT&CK techniques covered: {', '.join(techniques)}")
51+
print("\n Failing rows from these queries become detections the SOC can pivot on.")
52+
53+
54+
if __name__ == "__main__":
55+
main()

demos/03_sysadmin_fleet.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""Scenario 3 - sysadmins / DevSecOps: systemic vs isolated, and baseline drift.
2+
3+
One host failing a control is a remediation ticket. The *same* control failing
4+
on every host is a broken golden image / GPO / Ansible role — a very different,
5+
much higher-leverage fix. This demo drives the REAL ``comint_osquery.fleet``
6+
engine over three edge hosts built from one image: it correlates failures into
7+
``systemic`` / ``widespread`` / ``isolated`` scope, picks the cleanest host as a
8+
golden baseline, and reports per-host drift.
9+
10+
Offline: reads the bundled ``demos/12-fleet-systemic`` per-host snapshots.
11+
"""
12+
from _common import fixture, rule, section
13+
from comint_osquery import fleet as fl
14+
15+
16+
def main() -> None:
17+
rule("SYSADMIN / DEVSECOPS - fleet correlation + baseline drift")
18+
19+
target = fixture("12-fleet-systemic")
20+
print("\nScanning 3 edge hosts built from one golden image.")
21+
print(" target: demos/12-fleet-systemic/ (host-edge01..03.json)\n")
22+
23+
hosts = fl.scan_fleet(target)
24+
for h in hosts:
25+
state = "clean" if h.ok else f"failing: {', '.join(sorted(h.failing))}"
26+
print(f" {h.host:<10} {state}")
27+
28+
section("Correlation: where is the blast radius?")
29+
summ = fl.fleet_summary(hosts)
30+
print(f" hosts scanned : {summ['hosts_scanned']} "
31+
f"(clean={len(summ['hosts_clean'])}, failing={len(summ['hosts_failing'])})")
32+
corr = summ["correlation"]
33+
if summ["systemic_findings"]:
34+
print("\n SYSTEMIC (every host -> fix the IMAGE, not the host):")
35+
for q in summ["systemic_findings"]:
36+
d = corr[q]
37+
print(f" [SYS] {d['nist']:<8} {d['title']} ({d['count']}/{summ['hosts_scanned']} hosts)")
38+
if summ["isolated_findings"]:
39+
print("\n ISOLATED (single host -> per-host ticket):")
40+
for q in summ["isolated_findings"]:
41+
d = corr[q]
42+
print(f" [ISO] {d['nist']:<8} {d['title']} -> {', '.join(d['hosts'])}")
43+
44+
section("Baseline drift against the cleanest host")
45+
base = fl.pick_baseline(hosts)
46+
drift = fl.baseline_drift(hosts, base)
47+
print(f" auto-selected baseline: {drift['baseline']} "
48+
f"(its own failing set: {drift['in_baseline'] or 'none'})")
49+
if not drift["drifted_hosts"]:
50+
print(" no drift detected.")
51+
for host in drift["drifted_hosts"]:
52+
regs = drift["drift"][host]["regression_controls"]
53+
print(f" [DRIFT-] {host}: regressions beyond baseline -> {', '.join(regs)}")
54+
55+
print("\n The systemic FIPS failure says: re-bake the image. The rest are tickets.")
56+
57+
58+
if __name__ == "__main__":
59+
main()

demos/04_auditor_poam.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Scenario 4 - auditors / assessors: a POA&M workbook + a provable audit trail.
2+
3+
The artifact an auditor lives by is the Plan of Action & Milestones (POA&M): one
4+
row per (weakness, asset), with a CAT level, the security check (STIG/CCI), and a
5+
severity-driven scheduled-completion date. This demo builds the REAL eMASS-style
6+
POA&M with ``comint_osquery.fleet.poam_items`` and shows the columns, then uses
7+
the shipped hash-chained ``AuditLog`` to make the assessment itself
8+
tamper-evident — and proves the chain catches an edit.
9+
10+
Offline: reads the bundled ``demos/12-fleet-systemic`` snapshots; the audit log
11+
is written to a throwaway temp file.
12+
"""
13+
import csv
14+
import io
15+
import tempfile
16+
from pathlib import Path
17+
18+
from _common import fixture, rule, section
19+
from comint_osquery import fleet as fl
20+
from cognis_mil import AuditLog
21+
22+
23+
def main() -> None:
24+
rule("AUDITOR / ASSESSOR - eMASS POA&M + tamper-evident audit trail")
25+
26+
target = fixture("12-fleet-systemic")
27+
hosts = fl.scan_fleet(target)
28+
29+
section("POA&M workbook (one row per failing control, per host)")
30+
# assessed_at fixed so the scheduled-completion dates are deterministic.
31+
items = fl.poam_items(hosts, office="J6 / Cyber", assessed_at=0)
32+
print(f" {len(items)} POA&M item(s). eMASS columns: {len(fl.POAM_COLUMNS)}")
33+
print(f"\n {'Item ID':<13} {'CAT':<7} {'Control':<9} {'Sched. Complete':<16} Check")
34+
for it in items:
35+
print(f" {it['POA&M Item ID']:<13} {it['Raw Severity']:<7} "
36+
f"{it['Security Control Number (NC/NA)']:<9} "
37+
f"{it['Scheduled Completion Date']:<16} {it['Security Checks']}")
38+
39+
# Render the real CSV and confirm it round-trips (eMASS import sanity).
40+
csv_text = fl.poam_to_csv(items)
41+
rows = list(csv.DictReader(io.StringIO(csv_text)))
42+
print(f"\n CSV renders {len(rows)} data row(s), RFC 4180 quoted, header = eMASS columns.")
43+
44+
section("Tamper-evident audit trail (hash-chained, local-only)")
45+
log_path = Path(tempfile.mkdtemp(prefix="comint_audit_")) / "audit.log"
46+
log = AuditLog(log_path)
47+
log.append({"actor": "isso", "action": "scan", "target": "demos/12-fleet-systemic"})
48+
for it in items:
49+
log.append({"actor": "isso", "action": "poam_item", "id": it["POA&M Item ID"]})
50+
ok, msg = log.verify()
51+
print(f" appended {len(items) + 1} entries -> verify(): intact={ok} ({msg})")
52+
53+
# Tamper: rewrite one line's body directly, bypassing append().
54+
lines = log_path.read_text().splitlines()
55+
lines[1] = lines[1].replace("poam_item", "poam_item_HACKED")
56+
log_path.write_text("\n".join(lines) + "\n")
57+
ok2, msg2 = log.verify()
58+
print(f" after editing one row directly: intact={ok2} ({msg2})")
59+
print("\n The chain catches the edit -> the assessment record is provable, not asserted.")
60+
61+
62+
if __name__ == "__main__":
63+
main()

demos/05_airgap_enrichment.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""Scenario 5 - edge / air-gap operators: enrich findings fully offline.
2+
3+
On disconnected gear you still want a finding's bare control id resolved to its
4+
*official* NIST 800-53 title, and its single STIG-mapped ATT&CK technique
5+
expanded into the full CTID-recommended countermeasure control set
6+
(defense-in-depth for the RMF package). This demo runs the REAL
7+
``comint_osquery.feeds`` enrichment with ``offline=True`` against the committed
8+
fixture feed cache — exactly the sneakernet workflow: the cache was carried
9+
across the air gap, and nothing here touches the network.
10+
11+
Offline: ``_common`` points ``COGNIS_FEEDS_CACHE`` at ``tests/fixtures/feeds-cache``
12+
(trimmed OSCAL 800-53 rev5 catalog + CTID ATT&CK<->800-53 crosswalk).
13+
"""
14+
from _common import fixture, rule, section
15+
from comint_osquery.core import scan
16+
from comint_osquery import feeds
17+
18+
19+
def main() -> None:
20+
rule("EDGE / AIR-GAP - offline feed enrichment (official titles + countermeasures)")
21+
22+
print("\n Feed cache served offline (no network) — the sneakernet posture.")
23+
24+
result = scan(fixture("01-failing-host"))
25+
print(f" scanned demos/01-failing-host/ -> {result.total_findings()} finding(s)\n")
26+
27+
section("Enrich each finding from the authoritative feeds")
28+
summary = feeds.enrich_result(result, offline=True)
29+
for fid, info in summary.items():
30+
title = info["control_title"] or "(unresolved)"
31+
cms = info["attack_countermeasures"]
32+
print(f" {fid}")
33+
print(f" NIST {info['nist_800_53']:<8} -> {title}")
34+
if info["mitre_attack"]:
35+
head = ", ".join(cms[:6]) + (f" (+{len(cms) - 6} more)" if len(cms) > 6 else "")
36+
print(f" ATT&CK {info['mitre_attack']:<10} -> {len(cms)} countermeasure control(s): {head}")
37+
38+
resolved = sum(1 for i in summary.values() if i["control_title"])
39+
print(f"\n {resolved}/{len(summary)} control titles resolved from the OSCAL catalog, offline.")
40+
print(" Every finding now carries its official NIST title and a defense-in-depth set.")
41+
42+
43+
if __name__ == "__main__":
44+
main()

demos/_common.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Shared helpers for the runnable demo scenarios.
2+
3+
The ``demos/`` directory bundles two kinds of artifact:
4+
5+
* ``NN-name/`` directories — osquery snapshot JSON fixtures (the tool's real
6+
input shape) plus a ``SCENARIO.md``. These are the offline data the demos
7+
feed into the real API.
8+
* ``NN_name.py`` scripts (this family) — runnable, narrated scenarios that
9+
drive the **real** ``comint_osquery`` / ``cognis_mil`` API over those
10+
fixtures. No network, no fabricated functions, exit 0.
11+
12+
Every scenario is self-contained: it rebuilds nothing on disk it can't throw
13+
away, and points the data-feed engine at the committed fixture cache so feed
14+
enrichment works fully offline.
15+
"""
16+
from __future__ import annotations
17+
18+
import os
19+
import sys
20+
21+
# allow `python demos/NN_name.py` from anywhere
22+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
23+
24+
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
25+
DEMOS_DIR = os.path.join(REPO_ROOT, "demos")
26+
27+
# The committed, trimmed offline feed cache (OSCAL 800-53 rev5 catalog + the
28+
# CTID ATT&CK<->800-53 crosswalk). Pointing COGNIS_FEEDS_CACHE here makes every
29+
# `offline=True` feed read serve from disk — never the network.
30+
FIXTURE_CACHE = os.path.join(REPO_ROOT, "tests", "fixtures", "feeds-cache")
31+
os.environ.setdefault("COGNIS_FEEDS_CACHE", FIXTURE_CACHE)
32+
33+
34+
def fixture(name: str) -> str:
35+
"""Absolute path to a bundled demo fixture directory, e.g. ``01-failing-host``."""
36+
return os.path.join(DEMOS_DIR, name)
37+
38+
39+
def rule(title: str) -> None:
40+
print("\n" + "=" * 72)
41+
print(f" {title}")
42+
print("=" * 72)
43+
44+
45+
def section(title: str) -> None:
46+
print("\n" + "-" * 72)
47+
print(f" {title}")
48+
print("-" * 72)

0 commit comments

Comments
 (0)