$npx -y skills add transilienceai/communitytools --skill risk-prioritiserRisk-based prioritisation of confirmed attack paths. Combines exploit feasibility, technical CVSS severity, and asset business impact into a single ranked list driving remediation roadmaps.
| 1 | # Risk Prioritiser |
| 2 | |
| 3 | Consume the attack-path graph + the client-supplied business-tier map and emit a single ranked list of paths the org should remediate first. Mounted onto cloud-agent task #7. |
| 4 | |
| 5 | ## Trigger |
| 6 | |
| 7 | Cron daily after `attack-path-stitcher` (task #6) completes. Also event-fires when a new `validated/*.json` is written with `nvd.score >= 9.0`. |
| 8 | |
| 9 | ## Workflow |
| 10 | |
| 11 | 1. **Load inputs.** |
| 12 | - `artifacts/attack-paths.json` — graph from task #6. Split into `confirmed_paths` (RFP-grade) and `inferred_paths` (topology/supply-chain). |
| 13 | - `business-tier-map.csv` — client-supplied asset → tier. Search order: `{OUTPUT_DIR}/business-tier-map.csv` → `projects/rfp-3.3/schemas/business-tier-map.csv`. |
| 14 | 2. **Score each `confirmed_paths` entry.** |
| 15 | - `feasibility` = product of every edge's `feasibility` along the path. For confirmed paths this is always 1.0. |
| 16 | - `technical_severity` = `max_cvss_along_path / 10`. |
| 17 | - `business_impact` = tier weight of the *destination* (crown_jewel = 1.0 by definition). |
| 18 | - `entry_exposure` = 1.0 if entry node is `external: true`, else 0.5. |
| 19 | - **Final score** = `feasibility × technical_severity × business_impact × entry_exposure`. |
| 20 | 3. **Score each single-asset validated finding** that does not sit on a confirmed path. Same formula; `feasibility = 1.0`, `business_impact` = asset's own tier weight, hop count = 1. |
| 21 | 4. **Score each `inferred_paths` entry**, but cap their bucket placement at `theoretical` regardless of numeric score. Inferred paths NEVER reach `immediate` / `short_term` / `medium_term` — RFP §3.3 requires "confirmed", and inferred paths are evidence-deficient by construction. |
| 22 | 5. **Sort descending.** Stable tie-break order: max CVSS desc → hop count asc → finding age desc (newer first). |
| 23 | 6. **Bucket into roadmap tiers.** |
| 24 | - `immediate` (0-7 days): confirmed AND score ≥ 0.6 |
| 25 | - `short_term` (7-30 days): confirmed AND 0.3 ≤ score < 0.6 |
| 26 | - `medium_term` (30-90 days): confirmed AND 0.1 ≤ score < 0.3 |
| 27 | - `monitor`: confirmed AND score < 0.1 |
| 28 | - `theoretical` (track-only): any path with `path_class != "confirmed"`. Surfaced in board reports but excluded from the remediation SLA roadmap. |
| 29 | 7. **Write outputs** to `artifacts/attack-paths-ranked.json` + `attack-paths-ranked.md`. Each row carries `path_class` so downstream consumers can filter. |
| 30 | |
| 31 | ## Tier weights |
| 32 | |
| 33 | | Tier | Weight | |
| 34 | |---|---| |
| 35 | | `crown_jewel` | 1.00 | |
| 36 | | `revenue` | 0.70 | |
| 37 | | `support` | 0.40 | |
| 38 | | `dev` | 0.20 | |
| 39 | | `unknown` | 0.30 | |
| 40 | |
| 41 | The `unknown` weight is deliberately above `support` to bias toward investigating un-mapped assets — they often turn out to be high-tier once discovered. |
| 42 | |
| 43 | ## Output |
| 44 | |
| 45 | ``` |
| 46 | {OUTPUT_DIR}/ |
| 47 | artifacts/ |
| 48 | attack-paths-ranked.json |
| 49 | attack-paths-ranked.md |
| 50 | ``` |
| 51 | |
| 52 | `attack-paths-ranked.json` schema: |
| 53 | |
| 54 | ```json |
| 55 | { |
| 56 | "generated_at": "2026-05-13T03:30:00Z", |
| 57 | "tier_weights_used": {"crown_jewel": 1.0, "revenue": 0.7, "support": 0.4, "dev": 0.2, "unknown": 0.3}, |
| 58 | "ranked": [ |
| 59 | { |
| 60 | "rank": 1, |
| 61 | "kind": "path", |
| 62 | "path_class": "confirmed", |
| 63 | "path_id": "asset05->asset42->asset99", |
| 64 | "hops": ["asset05", "asset42", "asset99"], |
| 65 | "feasibility": 1.0, |
| 66 | "max_cvss": 9.8, |
| 67 | "business_impact": 1.0, |
| 68 | "entry_exposure": 1.0, |
| 69 | "score": 0.98, |
| 70 | "bucket": "immediate", |
| 71 | "remediation_focus": "asset05" |
| 72 | }, |
| 73 | { |
| 74 | "rank": 2, |
| 75 | "kind": "finding", |
| 76 | "path_class": "confirmed", |
| 77 | "finding_id": "finding-018", |
| 78 | "asset": "asset42", |
| 79 | "feasibility": 1.0, |
| 80 | "max_cvss": 9.1, |
| 81 | "business_impact": 0.7, |
| 82 | "entry_exposure": 1.0, |
| 83 | "score": 0.637, |
| 84 | "bucket": "immediate" |
| 85 | } |
| 86 | ], |
| 87 | "buckets": {"immediate": 4, "short_term": 11, "medium_term": 22, "monitor": 8, "theoretical": 14} |
| 88 | } |
| 89 | ``` |
| 90 | |
| 91 | ## Remediation focus |
| 92 | |
| 93 | For each ranked path, pick a single `remediation_focus` asset — the one whose patch breaks the chain at the lowest cost: |
| 94 | |
| 95 | - **If any edge has feasibility < 1.0** → focus on that edge's *source* asset. The weakest pivot is the attacker's cheapest hop; hardening it eliminates the easiest entry. |
| 96 | - **If every edge is feasibility 1.0** (fully confirmed chain) → focus on the path's **entry asset** (`hops[0]`). Patching the externally-reachable foothold breaks every downstream hop and is the cheapest patch surface operationally. |
| 97 | |
| 98 | This rule yields a single deterministic asset id per path. The previous "edge contribution formula" was deprecated — on uniformly-confirmed chains every edge has identical contribution, which gives no useful signal. |
| 99 | |
| 100 | ## Rules |
| 101 | |
| 102 | 1. **Always derive from `attack-paths.json`.** Do not re-derive feasibility / CVSS — those are already validated by upstream tasks. |
| 103 | 2. **`business-tier-map.csv` is authoritative.** If an asset is missing from the map, use `unknown` tier weight + flag `unmapped_assets[]` |