{"slug": "doc-foundry-ai-writes-security-detection-rules-then-proves-they-work", "title": "Doc-foundry – AI writes Security detection rules, then proves they work", "summary": "DOC-foundry, an open-source detection-engineering project, generates Sigma security detection rules from threat inputs such as CVE entries, CISA KEV listings and ATT&CK techniques, then validates each rule through a Blue-vs-Red adversarial loop and ships it with a proof bundle containing a positive corpus, a benign baseline, an adversarial mutation set and a computed coverage label of real-coverage, brittle or broken with FP/FN numbers and a reproducibility hash. Four role-specialized LLM agents (Threat Analyst, Blue Detection Engineer, Telemetry and Red-Team Adversary) do the authoring, while a deterministic referee handles FP/FN evaluation, adversarial evasion, the coverage verdict and the deployment gate so that, per the project, \"Same input → same verdict → same reproducibility hash.\" The project ships a live example run on a CISA KEV entry that produced three rule versions and eight reviewer findings, and runs offline via `python src/run.py --evals` on the standard library alone.", "body_md": "Turn a threat (a CVE, a CISA KEV entry, an incident writeup, an ATT&CK technique)\ninto a **validated first-version detection** — a proven starting point a detection\nteam hardens further, not a claim of finished, forever-bulletproof coverage — plus\n**three artifacts that ship together**:\n\n1. a **Sigma detection rule** that has survived a Blue-vs-Red adversarial loop,\n2. a **threat report** with the round-by-round**convergence history** , and\n3. a **proof bundle** — a positive corpus the rule fires on, a benign baseline it\nstays silent on, an adversarial mutation set, and a**computed coverage label** (`real-coverage` /`brittle` /`broken` ) with FP/FN numbers and a reproducibility hash.\n\nThe thing nobody else produces is #3. A model can write you a Sigma rule; it cannot\nhand you a *proof* that the rule works, an adversary that *provably* evades the weak\nversion, and an honest record of how the rule was hardened until it held.\n\n**A bounded, fixed adversary.** Red commits to **two real, documented attacks** in\nround one and re-throws *those same two* against the hardened rule each round — the\nsame \"built once, can't move its goalposts\" discipline as the fixed test corpus.\n`real-coverage` therefore means something concrete and honest: the first-version rule\nfires on every attack in the corpus, stays silent on benign look-alikes, and caught\n**both** of Red's real attacks. It is a validated baseline, not a promise of coverage\nagainst every future evasion — that is the detection team's job to extend.\n\n**Agents propose; the engine proves.** Four role-specialized LLM agents (Threat\nAnalyst, Blue Detection Engineer, Telemetry, Red-Team Adversary) do the creative\nwork. Everything that decides truth — FP/FN evaluation, adversarial evasion,\nthe coverage verdict, red-team verification, and the deployment gate — is a\ndeterministic referee. Same input → same verdict → same reproducibility hash.\n\n**Two pipelines share this agent cast.** The one described above **proves** — Blue\nauthors, Red attacks, a deterministic referee decides. The\n**Review Chain****reviews** — draft → Red Team → revision → SOC Lead → final rule, with every note\nthat shaped it. Different question, different deliverable; pick per what you need.\n\n📄 **See real output first:**\n[`examples/joomla-icagenda-cve-2026-48939/review-history.md`](https://github.com/mjha-arya/DOC-foundry/blob/main/examples/joomla-icagenda-cve-2026-48939/review-history.md)\n— a live run on a CISA KEV entry, three rule versions, eight reviewer findings.\n\n```\npip install -r requirements.txt          # or just run --evals on stdlib alone\n\n# LIVE — any threat text; a real model authors it (needs GEMINI_API_KEY or ANTHROPIC_API_KEY)\npython src/run.py \"CVE-2026-45659 SharePoint deserialization RCE\"\npython src/run.py \"T1218.011 rundll32 remote scriptlet execution via mshtml\"\n\n# CANNED — a fixture scenario, fully offline, no key (what the test suite drives)\npython src/run.py --threat fixtures/certutil_t1105.json   # brittle→repair→proven\n\npython src/run.py --corpus atomic         # positives from REAL Atomic Red Team commands\npython src/run.py --max-rounds 3          # self-repair loop cap (default 3)\npython src/run.py --evals                 # deterministic engine + proof + workflow tests\npython src/run.py --approve alice         # human approves → gated deploy succeeds\npython src/run.py --claude \"...\"          # force claude-opus-4-8 authoring (ANTHROPIC_API_KEY)\npython src/run.py --gemini \"...\"          # force Gemini authoring (GEMINI_API_KEY)\n```\n\nThere is **no default scenario** — the Foundry takes the threat you give it. A bare\n`python src/run.py` has nothing to work on and says so.\nBundles are written to `Outputs/<rule-slug>/` (`rule.yml`, `report.md`, `proof.json`).\n\nThe pipeline above asks *\"is this rule provably correct?\"* and answers with a\ndeterministic verdict. The **Review Chain** asks a different question — *\"what would\neach stakeholder say about this rule, and what does it look like after their feedback\nis applied?\"* — and answers with a **final rule plus the complete trail that shaped it**.\n\n```\ningest ─▶ extract ─▶ telemetry ─▶ profile\n    ─▶ Blue: DRAFT v1\n    ─▶ RED TEAM review        (findings: where is this evadable / blind)\n    ─▶ Blue: REVISION v2      (each edit cites the finding id it answers)\n    ─▶ SOC LEAD review        (findings: volume, triage, severity, tuning)\n    ─▶ Blue: FINAL v3\n    ─▶ package\npython src/run.py --chain \"CVE-2026-48939 Joomla iCagenda arbitrary file upload RCE\"\npython -m uvicorn webui.server:app --port 8000   # → http://127.0.0.1:8000  (chain UI)\n                                                 # → /classic for the proof loop above\n```\n\nRed and SOC are not the same reviewer in two hats — they pull in **opposite**\ndirections. Hardening against evasion broadens a rule; broadening raises alert volume.\nRunning both, in that order, is the point: the final rule is the one that survived both\npressures, and the trail shows exactly what each pass cost.\n\nOutput lands in `Outputs/<rule-slug>/` as `final-rule.yml`, `review-history.md`\n(the full progression, rendered) and `review.json`.\n\n**→ [`examples/joomla-icagenda-cve-2026-48939/`](https://github.com/mjha-arya/DOC-foundry/blob/main/examples/joomla-icagenda-cve-2026-48939)**\n— a real run on CVE-2026-48939 (Joomla iCagenda upload-to-RCE, CISA KEV), authored by\n`claude-opus-4-8`. Three rule versions, eight findings, the whole trail:\n[`review-history.md`](https://github.com/mjha-arya/DOC-foundry/blob/main/examples/joomla-icagenda-cve-2026-48939/review-history.md).\n\nBlue's first draft fired on a web daemon spawning a shell **that runs recon commands**.\nBoth reviewers took it apart — in opposite directions:\n\n**Red Team:** *\"No — I'd just skip the recon commands (or use a container's busybox\nshell) and this rule never fires despite a full RCE.\"*\n\n`R1` mandatory `recon_intent` turns a lineage detection into a keyword detection ·\n`R2` on Alpine `/bin/sh` is busybox, so every shell clause misses ·\n`R3` literal `contains` dies to shell obfuscation ·\n`R4` `setsid`/double-fork re-parents to PID 1 and lineage fails\n\n**SOC Lead:** *\"No — this fires on your own benign cron example on day one; the parent\nscoping is broken and it'll bury the rota in www-data noise.\"*\n\n`S1` the `OR` made web-server parentage optional — any www-data process spawning a\nshell fired ·\n`S2` php-fpm→php is normal web-app behaviour, not RCE ·\n`S3` the alert carried one field, so the analyst pivots consoles ·\n`S4` `level: high` on that noise floor burns out the on-call\n\nRed pushed Blue to **broaden**; the SOC Lead pushed it to **narrow** straight back.\nThat tension is the product: hardening against evasion raises alert volume, and the\nfinal rule is the one that survived both pressures. SOC reviews last, so what ships is\ntuned for the queue — v3 made parent lineage mandatory, excluded the known-good\ninterpreter chains, expanded the triage fields, dropped to `level: medium`, and carries\na three-step response playbook with a containment threshold.\n\n**Reviewed, not validated — and the artifact says so.** This chain does not invoke\nthe proof core. It never executes the rule against telemetry, counts no false\npositives, and issues no coverage verdict. A rule that has been through four\nprofessional reviewers *looks* finished, which is precisely why the packaged output\nleads with that sentence. Run the classic pipeline (or `--evals`) when you need proof\nrather than review.\n\n```\npython -m uvicorn webui.server:app --port 8000\n# → http://127.0.0.1:8000 ; type ANY threat, watch the loop run\npython tools/warm_cache.py \"the exact threat you'll demo\"   # optional — see below\n```\n\nFastAPI + SSE over the **real** graph. Two streams are interleaved and neither is\nscripted: **node events** fire when a graph node genuinely completes, and **token\nevents** stream each agent's raw model output *as it is generated*, via the token sink\non `providers.py`. The graph runs on a worker thread precisely because tokens arrive\nmid-node; the queue between them is the only coupling, and it is one-way.\n\nYou watch: the LangGraph shape animate (the node in flight glows, the edge into it\nflows, the **loop-back arc fires red on a repair round**), the agent console type out\nwhat the model is producing right now, the Sigma rule write itself line by line, and\nthe convergence timeline, Kartikeya gate and reproducibility hash land at the end.\n\n**It authors LIVE — there are no presets and no fixture/replay path in the UI**, so any\nthreat works. Determinism comes from `providers.py`'s on-disk cache: temperature 0,\nkeyed by `sha256(model|system|user)`, so re-running the *same threat text* replays\nbyte-identical model output → same rule → same bundle hash. Cold-run latency depends\nentirely on the model (a 3-round convergence on `gemini-flash-lite` lands in ~20-40s;\na bigger model is minutes). `warm_cache.py` pre-pays it for a threat you plan to demo.\n\n**Replay is labeled, always.** A cached token stream is marked `cached` end-to-end and\nthe console badge reads **⟳ REPLAY (cached)** instead of **● GENERATING**. A viewer\ncannot tell replay from generation by looking, so the UI says which it is. Dressing\ncached bytes up as live motion would be exactly the \"vibes\" this project refuses.\nThe sink is observation only: detach it and the verdict and hash are byte-identical.\n\n**Red-team refusals are handled honestly.** Safety-tuned models sometimes decline the\nadversary role. A refusal is recorded, surfaced in the UI, and — critically —\n`graph.node_verdict` refuses to award `real-coverage` to a round nobody attacked.\n\"No evasions found\" and \"the adversary never ran\" must never look alike.\n\n```\n  ingest ─▶ extract ─▶ telemetry            (Analyst normalizes; Telemetry fixes the test set)\n                          │\n                          ▼\n   ┌──────────────── SELF-REPAIR LOOP (LangGraph conditional edges) ─────────────────┐\n   │                                                                                  │\n   │   blue_author ─▶ evaluate ─▶ battery ─▶ red_propose ─▶ red_verify ─▶ verdict     │\n   │        ▲          (FP/FN)   (mutation   (Red-Team     (engine-      (coverage)    │\n   │        │             │       battery)    proposes)     verifies)        │         │\n   │        │             └── un-evaluable rule ─────────────────────────────┤         │\n   │        │                  (engine refuses → broken, skip the round)     │         │\n   │        └───────────────── repair: verdict ≠ real-coverage ◀────────────┘         │\n   │                                                                        │         │\n   └──────────────────────────── done: real-coverage OR round == cap ──────┘         │\n                                                                            ▼\n                                                                     gate ─▶ bundle\n\n  [ blue/red/telemetry/analyst = LLM agents — PROPOSE ]\n  [ evaluate·battery·red_verify·verdict·gate = deterministic referee — PROVE ]\n```\n\n- **Agents (`src/agents.py`)** — four distinct personas. The**Blue Detection\nEngineer** authors a robust-by-construction first version (behavior + command-line\nintent, not one brittle string) and re-authors on a brittle/broken verdict using the*concrete* evidence (the exact evasions + false positives). The**Red-Team Adversary** commits to**two real, documented attacks** in round one and re-throws*those same\ntwo* against the hardened rule every round — a fixed adversarial test set, not a fresh\nambush each round, so Blue closes a bounded pair of gaps and honest convergence is\nreachable.\n- **Research grounding (`src/knowledge_base.py` + `rule_corpus/`)** — a two-partition**RAG** knowledge base grounds both proposers in real, researcher-authored OSS:\nBlue authors from peer-reviewed**SigmaHQ** rules; Red attacks from**MITRE ATT&CK** defense-evasion (e.g.**T1036.003 Rename System Utilities** ) and**LOLBAS** . The\nretriever is lexical (BM25), fully offline and deterministic. It grounds*proposals\nonly* — it never touches the proof engine and never enters the reproducibility hash.\n- **`src/engine.py`** — the deterministic Sigma matcher (the proof core). Zero deps.\nHonesty contract: it*raises* on constructs it can't truly evaluate (`|cidr` ,\naggregations, the`|in` that models like to invent) rather than silently passing —\nan un-evaluable rule is never scored as \"covered\". The loop treats that as a**broken** round and hands the engine's own message back to Blue as repair\nevidence, so a rule that doesn't run costs a round, not the run.\n- **`src/evaluator.py`** — FP/FN evaluation + a deterministic**adversarial mutation\nbattery** (binary-rename, casing, whitespace, caret/quote obfuscation, path\nrelocation), and`merge_redteam()` which folds**engine-verified** red-team\nevasions into the same verdict. Brittleness is*measured* , not guessed.\n- **`src/graph.py`** — the loop.` red_verify` replays every red-team event through\nthe engine:**a bypass counts only if the engine confirms the rule failed to fire.** Unverified proposals are discarded — the adversary cannot assert a bypass.\n- **`src/governance.py`** —**Kartikeya's Gate = Blast Radius × Capability Delta** .\nStructural invariants a persuasive model can't argue away:*egress always\nrequires a human* , and*non-real coverage floors blast radius* .\n- **`src/mcp_server.py`** — governed tool boundary. The graph can neither fetch nor\ndeploy;`deploy_detection` refuses any approval token not bound to the exact\nbundle hash.**The harness holds the privilege, not the agent.**\n\n`python src/run.py --threat fixtures/certutil_t1105.json` — offline, no key:\n\n- **Round 1** — Blue anchors on the`Image` path. The deterministic battery lands the**binary-rename** bypass*and* the Red-Team lands a real-world variant (certutil\ncopied to`AdobeUpdater.exe` ), engine-verified → verdict**BRITTLE** → loop back.\n- **Round 2** — Blue re-authors on rename-resistant`OriginalFileName` . The battery\nevades nothing, and the**same** red-team rename is now**engine-discarded** because\nthe rule fires on it → verdict**REAL-COVERAGE** → Kartikeya's Gate → bundle.\n\nThe report shows the full arc (rule v1 brittle → evasion → rule v2 proven). That is the\nFoundry being honest about a rule that *looks* fine and isn't — then fixing it and\nproving the fix held.\n\nThis scenario is a **fixture**, not the demo: it exists so the workflow runs offline and\nthe test suite has a deterministic end-to-end case. The UI never touches it. A live\nthreat converges on its own merits or doesn't — and \"doesn't\" is a real result the\nFoundry is built to report rather than hide.\n\nThe Analyst's per-behavior log sources elect a single category for the run (majority\nvote); the Telemetry agent builds the fixed corpus on it, and Blue is told both that\ncategory and the exact **field names** the corpus carries. Blue sees no event values and\nno labels, so it cannot overfit — it just cannot key on a field that isn't in the\ntelemetry, which is an automatic recall-0 `broken` round for a reason that has nothing\nto do with detection quality. (A rule anchored on `TargetFilename` can never fire on\nprocess-creation events.)\n\nThis is the [Shiva/Kartikeya](https://github.com/mjha-arya/DOC-foundry/blob/README.md) thesis applied to detection engineering:\ndeploying a detection is a **capability request** (egress to a SIEM), scored through\n`Gate = Blast Radius × Capability Delta` before any human is bothered — and *no*\nautonomous path can push a rule. Least privilege and human-gated egress are computed\nand enforced in code, not asserted in a prompt.\n\n*Runs fully offline/deterministic. The sibling senior-security-engineering-agent\nnarrates with Gemini; the Foundry authors with Claude — but in both, the model only\nproposes and the proof is deterministic.*\n\nThe Foundry's **proof core** (`engine.py → evaluator.py → governance.py`) is deterministic and\nself-contained. Its **seams** — ingest, author, convert, test-corpus, deploy, report — are designed\nto be fed by open-source detection sources:\n\n- [`docs/OSS-PARTNERS.md`](https://github.com/mjha-arya/DOC-foundry/blob/main/docs/OSS-PARTNERS.md) — canonical partner catalog: each repo mapped to the\ntool's actual pipeline stage, with priority, integration point, and**license posture** .\n- [`docs/OSS-INTEGRATION-MINDMAP.md`](https://github.com/mjha-arya/DOC-foundry/blob/main/docs/OSS-INTEGRATION-MINDMAP.md) — the full researched map.\n\n**First wire-in (highest value):** Atomic Red Team → positive corpus + EVTX-ATTACK-SAMPLES → benign\nbaseline, so the adversarial battery runs against *real* attack telemetry. Then pySigma + SigmAIQ for\nmulti-SIEM authoring/conversion (the `SigmaRuleArtifact.backends` slot already exists for this).", "url": "https://wpnews.pro/news/doc-foundry-ai-writes-security-detection-rules-then-proves-they-work", "canonical_source": "https://github.com/mjha-arya/DOC-foundry", "published_at": "2026-11-13 23:40:12+00:00", "updated_at": "2026-11-14 00:20:55.458340+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "ai-safety", "artificial-intelligence"], "entities": ["DOC-foundry", "CISA KEV", "Sigma", "Atomic Red Team", "MITRE ATT&CK", "Gemini", "Claude", "Anthropic"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/doc-foundry-ai-writes-security-detection-rules-then-proves-they-work", "markdown": "https://wpnews.pro/news/doc-foundry-ai-writes-security-detection-rules-then-proves-they-work.md", "text": "https://wpnews.pro/news/doc-foundry-ai-writes-security-detection-rules-then-proves-they-work.txt", "jsonld": "https://wpnews.pro/news/doc-foundry-ai-writes-security-detection-rules-then-proves-they-work.jsonld"}}