$npx -y skills add transilienceai/communitytools --skill attack-path-stitcherStitches confirmed single-asset findings into multi-hop attack paths across the organization. Builds a graph where nodes are assets and edges are confirmed exploit hops citing the findings that enable them.
| 1 | # Attack Path Stitcher |
| 2 | |
| 3 | The Validation Run task (#3) produces confirmed findings *per asset*. Real attacker risk lives in **chains**: a finding on asset A leaks credentials that enable a finding on asset B that pivots into asset C. This skill builds that graph. |
| 4 | |
| 5 | Mounted onto cloud-agent task #6. |
| 6 | |
| 7 | ## Trigger |
| 8 | |
| 9 | Cron daily (default 03:00 UTC). May also re-run after a Validation Run task batch completes. |
| 10 | |
| 11 | ## Workflow |
| 12 | |
| 13 | 1. **Load inputs.** |
| 14 | - `validated/*.json` — every confirmed finding across all engagements. |
| 15 | - `artifacts/org-surface.json` — the org-wide surface graph (assets, services, network zones, trust relationships). |
| 16 | - `findings/finding-NNN/evidence/raw-source.txt` — for credential / token extraction during stitching. |
| 17 | 2. **Build asset nodes.** One node per asset in `org-surface.json`, attributed with: `tier`, `services`, `network_zone`, `trust_relationships`. |
| 18 | 3. **Build edges** — one edge per detected pivot. See `reference/edge-detectors.md` for the seven detectors: |
| 19 | - Credential reuse (creds leaked on A reused as auth on B) |
| 20 | - Shared secret / API key (same secret appears in two assets' evidence) |
| 21 | - Trust-zone transitive access (A in zone X has implicit reach to B in zone X) |
| 22 | - AD path hops (kerberoast / DC sync / RBCD chains) |
| 23 | - Cloud IAM role chains (assume-role from compromised asset) |
| 24 | - SSRF → internal asset reach (A's SSRF reaches B's internal endpoint) |
| 25 | - Supply-chain (A is a dependency of B per `source-code-scanning` SBOM) |
| 26 | 4. **Compute reachability closure.** For each tier-`crown_jewel` node, BFS backwards through edges to find every external-facing node that can reach it. Mark these as "entry points". |
| 27 | 5. **Write graph** to `artifacts/attack-paths.json` plus a human DOT file `artifacts/attack-paths.dot` (renderable with Graphviz). |
| 28 | |
| 29 | Implementation runs through `tools/chain-merger.py` which handles the graph construction. The skill provides the *rules* the tool consults; the tool does the iteration. |
| 30 | |
| 31 | ## Output |
| 32 | |
| 33 | ``` |
| 34 | {OUTPUT_DIR}/ |
| 35 | artifacts/ |
| 36 | attack-paths.json # nodes, edges, entry_points, crown_jewel_paths |
| 37 | attack-paths.dot # Graphviz source |
| 38 | attack-paths.md # ranked list of distinct paths (human read) |
| 39 | ``` |
| 40 | |
| 41 | `attack-paths.json` schema: |
| 42 | |
| 43 | ```json |
| 44 | { |
| 45 | "generated_at": "2026-05-13T03:00:00Z", |
| 46 | "nodes": [ |
| 47 | {"id": "asset42", "tier": "revenue", "services": ["http/443"], "zone": "dmz", |
| 48 | "external": true, "findings": ["finding-012", "finding-018"], "max_cvss": 9.8} |
| 49 | ], |
| 50 | "edges": [ |
| 51 | {"src": "asset42", "dst": "asset77", "detector": "credential-reuse", |
| 52 | "via_findings": ["finding-012", "finding-019"], |
| 53 | "evidence": "credential (userpass) present in evidence of asset42 and asset77", |
| 54 | "feasibility": 1.0} |
| 55 | ], |
| 56 | "entry_points": ["asset42", "asset05"], |
| 57 | "confirmed_paths": [ |
| 58 | {"jewel": "asset99", "paths": [ |
| 59 | {"hops": ["asset42", "asset77", "asset99"], |
| 60 | "edges": [{"src":"asset42","dst":"asset77","detector":"credential-reuse","feasibility":1.0,"via_findings":["finding-012"]}, |
| 61 | {"src":"asset77","dst":"asset99","detector":"ssrf-reach","feasibility":1.0,"via_findings":["finding-024"]}], |
| 62 | "feasibility": 1.0, "max_cvss": 9.8, "path_class": "confirmed"} |
| 63 | ]} |
| 64 | ], |
| 65 | "inferred_paths": [ |
| 66 | {"jewel": "asset99", "paths": [ |
| 67 | {"hops": ["asset05", "asset99"], "edges": [...], |
| 68 | "feasibility": 0.5, "max_cvss": 7.5, "path_class": "inferred"} |
| 69 | ]} |
| 70 | ], |
| 71 | "truncation": { |
| 72 | "edge_cap_hit": false, "depth_truncated_count": 0, |
| 73 | "topn_dropped_count": 0, "max_depth": 8, "edge_cap": 50000 |
| 74 | } |
| 75 | } |
| 76 | ``` |
| 77 | |
| 78 | **Crucial for RFP §3.3 compliance**: `confirmed_paths` contains ONLY paths where every edge has feasibility 1.0 AND every edge cites at least one validated finding. These are the "confirmed attack paths" the RFP requires. `inferred_paths` carries topology / supply-chain hops with no PoC evidence — surfaced for analyst review but excluded from remediation SLA buckets by `risk-prioritiser`. |
| 79 | |
| 80 | ## Rules |
| 81 | |
| 82 | 1. **Edges require evidence.** An edge is only written if at least one finding's evidence corroborates the pivot. No speculative edges. |
| 83 | 2. **Bi-directional ≠ assumed.** If A reaches B, do not infer B reaches A. Each direction needs its own evidence. |
| 84 | 3. **Deduplicate by `(src, dst, detector)`.** Multiple findings that enable the same hop merge into one edge with `via_findings` listing all of them. |
| 85 | 4. **Feasibility ∈ {1.0, 0.5, 0.25}.** Reliable PoC re-run = 1.0; conditional (race, timing, specific user) = 0.5; theoretical (logically follows but never demonstrated) = 0.25. |
| 86 | 5. **Limit path enumeration.** For each crown-jewel, return top-10 paths per class (confirmed + inferred separately) sorted by `feasibility × max_cvss / hop_count`. Full graph is in `attack-paths.json` for downstre |