feat(#255): /threat-model --format=dragon for OWASP Threat Dragon JSON export#258
Conversation
…N export Adds a `--format` flag to /threat-model so adopters can emit OWASP Threat Dragon v2 JSON alongside the existing markdown catalogue. Default behaviour (markdown only) is unchanged for backwards compat. - Add serialize_dragon.py — stdlib-only Python serialiser that maps the skill's DFD + STRIDE findings into Threat Dragon v2 JSON (actor/process/store/flow/trust-boundary-box shapes; threats[] attached to parent cells with the 6 schema-required fields). - Use auto-grid layout (actors y=0, processes y=200, stores y=400; x-spaced 200px). Trust boundaries wrap their children with a 40px margin. Dragon's auto-arrange re-flows on first open. - Add fixtures/sample-input.yaml + tests/test_serialize_dragon.sh (17-case smoke test covering top-level keys, shape-class counts, flow ref resolution, STRIDE attachment, severity normalisation, --strict bad-input exit, SKILL.md doc presence, AgDR presence). - Extend SKILL.md with Usage section + Step 6 (serialiser invocation + structured-input schema) + an opt-in default rule. - Add AgDR-0022 capturing the format choice (Dragon v2 vs TMT .tm7 vs IriusRisk vs OTM vs GraphViz) and the auto-grid layout decision. Closes #255 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
atlas-apex
left a comment
There was a problem hiding this comment.
Code Review: PR #258
Commit: a54041b402e5e74ae23effc8879e6eca844aebeb
The serialiser is well-shaped: schema mapping is faithful (DFD vocabulary → Dragon shapes 1:1, STRIDE findings nest under data.threats[] with all six required fields, severity normalised into the High/Medium/Low enum, flow source/target reference real cell UUIDs, cells emitted in the correct shapes→boundaries→flows order with sensible z-indexes -1/1-3/10), the 17-case smoke test is meaningfully assertive rather than merely structural (cross-class threat-parent placement, dangling-ref detection under --strict, severity enum conformance, and SKILL.md/AgDR cross-checks), and the auto-grid produces a clean starting state that Dragon's own auto-arrange can re-flow. The PyYAML fallback parser is suitably narrow — it cleanly handles the documented input schema (inline maps, block lists of maps, nested keys, quoted/unquoted scalars) and the limits are documented up front ("not a general YAML parser"). Backwards-compat is preserved (default --format=markdown unchanged), and the YAML fallback matches the existing /journey skill convention. One nit: docs/agdr/AgDR-0024-threat-dragon-export.md has id: AgDR-0022 in its YAML frontmatter and references "AgDR-0022" throughout the body and in SKILL.md citations — the renumber appears to have moved only the filename. Worth fixing the frontmatter id: and body references for AgDR-index consistency, but non-blocking.
Verdict
APPROVED
Reviewed by Rex (Code Reviewer Agent)
Reviewed commit: a54041b402e5e74ae23effc8879e6eca844aebeb
atlas-apex
left a comment
There was a problem hiding this comment.
Re-approved at 2e5958a — verified diff is exactly the 3-line doc fix (AgDR-0024 frontmatter id 0022 to 0024, plus two SKILL.md citation updates from AgDR-0022 to AgDR-0024). No code changes, no regression risk. LGTM.
…ut (#259) * feat(#256): /process skill — extract process from code, BPMN 2.0 output - New skill at .claude/skills/process/SKILL.md that maps a named business process by scanning across seven discovery axes (explicit workflow defs, queue/job chains, cron triggers, state-column transitions, API choreography, existing BPMN/Mermaid, documented steps), interviews only on gaps, then emits a lint-clean BPMN 2.0 file the operator can open in Camunda Modeler. - Anchor-scoped discovery (operator supplies process slug + entry point — endpoint / state machine / queue job / directory) with reachability bounding from the anchor. No exhaustive scans. - Cross-repo traversal via apexyard.projects.yaml — when a handoff target matches a registered project, the skill recurses into that repo (offers to clone workspace/<name>/ if missing, default-yes). Unregistered targets render as external participant pools. - generate-bpmn.sh emits valid BPMN 2.0 XML with bpmn:laneSet, bpmn:messageFlow for cross-pool handoffs, source citations via bpmn:documentation on every element. Pipes through `npx bpmn-auto-layout` for <bpmndi> coords; falls back to bare BPMN with warning when Node is missing. - lint.sh wraps `bpmnlint` (recommended ruleset + label-required + no-disconnected + no-implicit-split) as the merge gate; overridable via per-project .bpmnlintrc. - AgDR-0024 captures the seven sub-decisions: BPMN 2.0 over alternatives (DMN/CMMN/Mermaid sequence/raw flowchart), bpmn-auto-layout over manual coords, bpmnlint as the gate, read-first-then-ask over operator-only authoring, swimlanes-default vs pools-opt-in, registry-lookup vs heuristic URL matching, on-demand cloning offer vs black-box fallback. - Three test scripts cover discovery (smoke.sh — 18 checks across all seven axes on a synthetic Express+Prisma+BullMQ+XState fixture), cross-repo registry lookup (test_cross_repo.sh — 6 checks), and BPMN emission edge cases (test_bpmn_emit.sh — 21 checks: special-char XML escaping, message flows, --pools mode, flow refs, xmllint validity). - CLAUDE.md skill registry bumped to 44; docs/multi-project.md "How skills behave" table updated. Closes #256 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(#256): renumber AgDR to 0025 — #258 took 0024 during parallel landing * fix(#256): align AgDR-0025 frontmatter id (renumber missed YAML field) --------- Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ies + classifications (#260) * feat(#257): /dfd skill — extract Data Flow Diagram with trust boundaries + classifications Introduces /dfd as the canonical Data Flow Diagram producer. The DFD becomes the source of truth for /threat-model and /compliance-check — both refactored in this commit to consume from projects/<name>/architecture/dfd.md instead of regenerating their own data-flow views. Six-axis discovery (external actors, processes, data stores, data flows, trust boundaries, data classifications). Classifications detect via three additive pathways: code annotations (@pii, @sensitive, CLASSIFIED:), env-var heuristics (*_SECRET, *_TOKEN, EMAIL_*), and schema-column heuristics (email, phone, card_number). An optional explicit registry at docs/data-classification.{md,yaml} overrides heuristic labels. Two output formats: Mermaid markdown (default, renders inline on GitHub, zero new toolchain) and OWASP Threat Dragon v2 JSON (on --format=dragon, for visual editing in Threat Dragon). The Dragon serialiser is a pure function over the in-memory model so #255 (/threat-model --format=dragon) can share or import it. Multi-repo discovery via a new shared helper _lib-multi-repo-trace.sh (intended to also be consumed by /process #256). Three resolvers: mrt_resolve_target (hostname/URL/topic → registered project), mrt_is_third_party (known SaaS vendor detection), mrt_workspace_for (workspace clone path resolution). The refactors of /threat-model and /compliance-check are surgical — Step 1 of /threat-model now reads dfd.md and offers /dfd first if it doesn't exist; Step 4 of /compliance-check reads the classifications + external-services list from dfd.md for cross-border-transfer and DPA detection. Output formats and audit-history persistence contracts in both consumers are unchanged. AgDR-0024 captures the design rationale: Mermaid as primary, Threat Dragon as secondary; classifications as a first-class concept; the single-source-of-truth refactor; the shared multi-repo trace helper. Cross-PR coordination: - #255 (/threat-model --format=dragon) — shares the Threat Dragon serialiser; if #255 lands first with the serialiser inside /threat-model, this skill imports from there - #256 (/process) — shares the _lib-multi-repo-trace.sh helper; if /process extracted its own version first, the consumer that lands later should consolidate Tests (.claude/skills/dfd/tests/smoke.sh, 52 assertions) cover all three fixtures from the AC: single-service six-axis discovery, cross-service trust-boundary detection, three-pathway classification detection. Plus Mermaid generator output shape, Threat Dragon JSON schema validation, and verification that /threat-model and /compliance-check actually reference dfd.md. Closes #257 * fix(#257): renumber AgDR to 0026 — #258 + #259 took 0024 + 0025 during parallel landing * fix(#257): address Rex blockers — shellcheck SC2221/2222, MD032, lychee 404, AgDR refs - _lib-multi-repo-trace.sh: drop redundant subset patterns in third-party detector (e.g. *api.amplitude.com* covered by broader *amplitude.com*) — clears SC2221/2222. - dfd/SKILL.md: blank lines around Discovery + Classifications lists — clears MD032. - dfd/SKILL.md: Threat Dragon schema link now points to the repo root (sub-path 404'd). - AgDR cross-refs across 5 files renumbered 0024 → 0026 (renumber missed downstream). --------- Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
…N export (#258) * feat(#255): /threat-model --format=dragon for OWASP Threat Dragon JSON export Adds a `--format` flag to /threat-model so adopters can emit OWASP Threat Dragon v2 JSON alongside the existing markdown catalogue. Default behaviour (markdown only) is unchanged for backwards compat. - Add serialize_dragon.py — stdlib-only Python serialiser that maps the skill's DFD + STRIDE findings into Threat Dragon v2 JSON (actor/process/store/flow/trust-boundary-box shapes; threats[] attached to parent cells with the 6 schema-required fields). - Use auto-grid layout (actors y=0, processes y=200, stores y=400; x-spaced 200px). Trust boundaries wrap their children with a 40px margin. Dragon's auto-arrange re-flows on first open. - Add fixtures/sample-input.yaml + tests/test_serialize_dragon.sh (17-case smoke test covering top-level keys, shape-class counts, flow ref resolution, STRIDE attachment, severity normalisation, --strict bad-input exit, SKILL.md doc presence, AgDR presence). - Extend SKILL.md with Usage section + Step 6 (serialiser invocation + structured-input schema) + an opt-in default rule. - Add AgDR-0022 capturing the format choice (Dragon v2 vs TMT .tm7 vs IriusRisk vs OTM vs GraphViz) and the auto-grid layout decision. Closes #255 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(#255): renumber AgDR to 0024 — 0022 + 0023 landed on dev while #258 was open * fix(#255): align AgDR frontmatter id + SKILL.md citations to 0024 --------- Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ut (#259) * feat(#256): /process skill — extract process from code, BPMN 2.0 output - New skill at .claude/skills/process/SKILL.md that maps a named business process by scanning across seven discovery axes (explicit workflow defs, queue/job chains, cron triggers, state-column transitions, API choreography, existing BPMN/Mermaid, documented steps), interviews only on gaps, then emits a lint-clean BPMN 2.0 file the operator can open in Camunda Modeler. - Anchor-scoped discovery (operator supplies process slug + entry point — endpoint / state machine / queue job / directory) with reachability bounding from the anchor. No exhaustive scans. - Cross-repo traversal via apexyard.projects.yaml — when a handoff target matches a registered project, the skill recurses into that repo (offers to clone workspace/<name>/ if missing, default-yes). Unregistered targets render as external participant pools. - generate-bpmn.sh emits valid BPMN 2.0 XML with bpmn:laneSet, bpmn:messageFlow for cross-pool handoffs, source citations via bpmn:documentation on every element. Pipes through `npx bpmn-auto-layout` for <bpmndi> coords; falls back to bare BPMN with warning when Node is missing. - lint.sh wraps `bpmnlint` (recommended ruleset + label-required + no-disconnected + no-implicit-split) as the merge gate; overridable via per-project .bpmnlintrc. - AgDR-0024 captures the seven sub-decisions: BPMN 2.0 over alternatives (DMN/CMMN/Mermaid sequence/raw flowchart), bpmn-auto-layout over manual coords, bpmnlint as the gate, read-first-then-ask over operator-only authoring, swimlanes-default vs pools-opt-in, registry-lookup vs heuristic URL matching, on-demand cloning offer vs black-box fallback. - Three test scripts cover discovery (smoke.sh — 18 checks across all seven axes on a synthetic Express+Prisma+BullMQ+XState fixture), cross-repo registry lookup (test_cross_repo.sh — 6 checks), and BPMN emission edge cases (test_bpmn_emit.sh — 21 checks: special-char XML escaping, message flows, --pools mode, flow refs, xmllint validity). - CLAUDE.md skill registry bumped to 44; docs/multi-project.md "How skills behave" table updated. Closes #256 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(#256): renumber AgDR to 0025 — #258 took 0024 during parallel landing * fix(#256): align AgDR-0025 frontmatter id (renumber missed YAML field) --------- Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ies + classifications (#260) * feat(#257): /dfd skill — extract Data Flow Diagram with trust boundaries + classifications Introduces /dfd as the canonical Data Flow Diagram producer. The DFD becomes the source of truth for /threat-model and /compliance-check — both refactored in this commit to consume from projects/<name>/architecture/dfd.md instead of regenerating their own data-flow views. Six-axis discovery (external actors, processes, data stores, data flows, trust boundaries, data classifications). Classifications detect via three additive pathways: code annotations (@pii, @sensitive, CLASSIFIED:), env-var heuristics (*_SECRET, *_TOKEN, EMAIL_*), and schema-column heuristics (email, phone, card_number). An optional explicit registry at docs/data-classification.{md,yaml} overrides heuristic labels. Two output formats: Mermaid markdown (default, renders inline on GitHub, zero new toolchain) and OWASP Threat Dragon v2 JSON (on --format=dragon, for visual editing in Threat Dragon). The Dragon serialiser is a pure function over the in-memory model so #255 (/threat-model --format=dragon) can share or import it. Multi-repo discovery via a new shared helper _lib-multi-repo-trace.sh (intended to also be consumed by /process #256). Three resolvers: mrt_resolve_target (hostname/URL/topic → registered project), mrt_is_third_party (known SaaS vendor detection), mrt_workspace_for (workspace clone path resolution). The refactors of /threat-model and /compliance-check are surgical — Step 1 of /threat-model now reads dfd.md and offers /dfd first if it doesn't exist; Step 4 of /compliance-check reads the classifications + external-services list from dfd.md for cross-border-transfer and DPA detection. Output formats and audit-history persistence contracts in both consumers are unchanged. AgDR-0024 captures the design rationale: Mermaid as primary, Threat Dragon as secondary; classifications as a first-class concept; the single-source-of-truth refactor; the shared multi-repo trace helper. Cross-PR coordination: - #255 (/threat-model --format=dragon) — shares the Threat Dragon serialiser; if #255 lands first with the serialiser inside /threat-model, this skill imports from there - #256 (/process) — shares the _lib-multi-repo-trace.sh helper; if /process extracted its own version first, the consumer that lands later should consolidate Tests (.claude/skills/dfd/tests/smoke.sh, 52 assertions) cover all three fixtures from the AC: single-service six-axis discovery, cross-service trust-boundary detection, three-pathway classification detection. Plus Mermaid generator output shape, Threat Dragon JSON schema validation, and verification that /threat-model and /compliance-check actually reference dfd.md. Closes #257 * fix(#257): renumber AgDR to 0026 — #258 + #259 took 0024 + 0025 during parallel landing * fix(#257): address Rex blockers — shellcheck SC2221/2222, MD032, lychee 404, AgDR refs - _lib-multi-repo-trace.sh: drop redundant subset patterns in third-party detector (e.g. *api.amplitude.com* covered by broader *amplitude.com*) — clears SC2221/2222. - dfd/SKILL.md: blank lines around Discovery + Classifications lists — clears MD032. - dfd/SKILL.md: Threat Dragon schema link now points to the repo root (sub-path 404'd). - AgDR cross-refs across 5 files renumbered 0024 → 0026 (renumber missed downstream). --------- Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
Summary
--format=markdown|dragon|bothflag to/threat-model. Default behaviour (markdown only) is unchanged — backwards compatible.--format=dragonwrites<output-dir>/threat-model.jsonin OWASP Threat Dragon v2 schema. Adopters can open it directly in the Dragon desktop / web app for visual editing, sharing with security teams, and ongoing maintenance.actor, processes →process, data stores →store, trust boundaries →trust-boundary-box, data flows →flowwithsource.cell/target.cellUUIDs). STRIDE findings attach to their parent shape viadata.threats[]with the six schema-required fields (description, mitigation, severity, status, title, type),modelType: STRIDE, andstatus: Open.y=0, processes aty=200, stores aty=400; x-spaced 200px). Trust-boundary boxes wrap their children with a 40px margin. Dragon's auto-arrange re-flows on first open — the grid is just a sane starting state. Rationale: AgDR-0022.Testing
Run the smoke test from the repo root:
Expected: 17 passed, 0 failed. Validates:
--outversion,summary.title,detail.{contributors, diagrams, diagramTop, reviewer, threatTop})id/shape/zIndexsource.cell/target.cellpoints at a real cell UUIDHigh/Medium/Lowenum--strictexits non-zero on bad input (orphan flow source/target)SKILL.mddocuments--formatin frontmatterargument-hintand## UsageEnd-to-end against the bundled fixture:
Then open
/tmp/threat-model.jsonin OWASP Threat Dragon (File → Open Existing Threat Model). ClickAuto-arrangeon the toolbar to re-flow the grid.Backwards-compat check (default unchanged): invoke
/threat-modelwithout any flag and confirm the existing markdown +_lib-audit-history.shpersistence flow still fires, untouched.Glossary
td.vue/src/assets/schema/threat-dragon-v2.schema.json.trust-boundary-boxdata.isTrustBoundary: true.Closes #255
🤖 Generated with Claude Code