From 47a506b3b4f2169cfb788d8c4cabc06bb67c4bea Mon Sep 17 00:00:00 2001 From: Ryan Gao Date: Thu, 16 Jul 2026 15:35:14 -0700 Subject: [PATCH] cybersecurity-attack-paths: fix edge-label handling, align to relationalai 1.21.1 p.relationships changed format in relationalai 1.17: from a decorated string ("-->") to the dotted relationship name ("Asset.exploit_to"). The 1.15-era decoration stripping no longer stripped anything and mangled every printed technique (e.g. "Asset.exploit"). Print the raw label as-is, update README/runbook expected output to match, and bump the pin 1.15.0 -> 1.21.1 (verified end-to-end against a live engine). Co-Authored-By: Claude Fable 5 --- v1/cybersecurity-attack-paths/README.md | 8 ++++---- .../cybersecurity_attack_paths.py | 16 ++++++---------- v1/cybersecurity-attack-paths/pyproject.toml | 2 +- v1/cybersecurity-attack-paths/runbook.md | 4 ++-- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/v1/cybersecurity-attack-paths/README.md b/v1/cybersecurity-attack-paths/README.md index f6c74273..a2bd8149 100644 --- a/v1/cybersecurity-attack-paths/README.md +++ b/v1/cybersecurity-attack-paths/README.md @@ -53,7 +53,7 @@ This template enumerates multi-step attack paths across an enterprise asset grap ### Tools - Python >= 3.10. -- `relationalai` SDK >= 1.15 (path enumeration with multi-edge patterns is a preview capability) and the `rai` CLI, both installed by the Quickstart steps below. +- `relationalai` SDK >= 1.21 (path enumeration with multi-edge patterns is a preview capability) and the `rai` CLI, both installed by the Quickstart steps below. - OS notes: works on macOS, Linux, and Windows; the Quickstart's virtual-environment activation command assumes macOS or Linux. ## Quickstart @@ -99,7 +99,7 @@ This template enumerates multi-step attack paths across an enterprise asset grap ```text 3 kill-chain attack path(s) reach a crown jewel (exploit -> cred -> 1-2 pivots, from an internet-facing asset): - [3 hops] VPN Gateway --[exploit]--> Jump Host --[cred]--> File Server --[pivot]--> Customer Database + [3 hops] VPN Gateway --[Asset.exploit_to]--> Jump Host --[Asset.cred_to]--> File Server --[Asset.pivot_to]--> Customer Database ``` See the runbook for the full output. @@ -142,7 +142,7 @@ CSV files --> Define Asset + technique edges --> Kill-chain enumeration (multi-e The analysis starts by modeling each attacker technique as its own directed relationship between assets — `exploit_to`, `cred_to`, `pivot_to` — plus a technique-agnostic `can_reach` union edge that the point query uses. -The centerpiece is a multi-edge path pattern (which needs `relationalai>=1.15`) that composes the techniques in series: an exploit first, then credential reuse, then one or more lateral pivots, ending at an explicit destination. Filtering the source to an internet-facing asset and the destination to a crown jewel pins the threat model. Enforcing edge order is the whole point — a single union edge or a flat join cannot express "exploit first, then credentials, then pivots," which is exactly the kill-chain signature analysts care about, and each hop records the technique it used. +The centerpiece is a multi-edge path pattern (which needs `relationalai>=1.21`) that composes the techniques in series: an exploit first, then credential reuse, then one or more lateral pivots, ending at an explicit destination. Filtering the source to an internet-facing asset and the destination to a crown jewel pins the threat model. Enforcing edge order is the whole point — a single union edge or a flat join cannot express "exploit first, then credentials, then pivots," which is exactly the kill-chain signature analysts care about, and each hop records the technique it used. A separate point query pins both endpoints by id to enumerate every route between a chosen entry point and a chosen crown jewel over the union edge. The kill-chains are then ranked by the exposure summed along each one. Finally, the assets lying on any crown-jewel chain are flagged back onto the ontology as `Asset.on_attack_path`, so a later query can pull them without re-enumerating paths. @@ -176,7 +176,7 @@ See `cybersecurity_attack_paths.py` for the implementation and `runbook.md` to r
Why do I see relationalai version or path import errors? -- Path enumeration with multi-edge patterns requires `relationalai` 1.15 or newer. Confirm your installed version with `python -m pip show relationalai`. +- Path enumeration with multi-edge patterns requires `relationalai` 1.21 or newer. Confirm your installed version with `python -m pip show relationalai`.
diff --git a/v1/cybersecurity-attack-paths/cybersecurity_attack_paths.py b/v1/cybersecurity-attack-paths/cybersecurity_attack_paths.py index dabb8a21..793d307d 100644 --- a/v1/cybersecurity-attack-paths/cybersecurity_attack_paths.py +++ b/v1/cybersecurity-attack-paths/cybersecurity_attack_paths.py @@ -2,7 +2,7 @@ Enumerates multi-step attack chains across an enterprise asset graph by composing distinct attacker techniques in series -- a capability unlocked by multi-edge path -patterns (relationalai>=1.15): +patterns (relationalai>=1.21): - Loads Asset nodes (hosts, services, accounts) and three DISTINCT directed edges between them, one per technique: exploit_to (vulnerability exploitation), @@ -109,7 +109,7 @@ def load_csv(filename): # -------------------------------------------------- # Paths: kill-chain attack paths (multi-relationship sequence) -# PREVIEW capability; requires relationalai>=1.15. +# PREVIEW capability; requires relationalai>=1.21. # -------------------------------------------------- # model.path(a.exploit_to, b.cred_to, c.pivot_to.repeat(1, MAX_PIVOTS), dst) is a # MULTI-EDGE pattern: distinct relationships in series. It matches the kill-chain @@ -157,16 +157,12 @@ def load_csv(filename): hop_df = hop_df.drop_duplicates(["path_id", "hop"]).sort_values(["path_id", "hop"]) # Reassemble each kill-chain: ordered asset names + the technique used at each hop. -def technique_label(raw): - # relationship labels arrive as e.g. "-->"; strip to the verb stem. - stem = raw.strip("-<>⟨⟩→ ") - return stem[:-3] if stem.endswith("_to") else stem - +# The technique is the relationship label as-is -- the dotted relationship name, +# e.g. "Asset.exploit_to". chains = [] for pid, g in kill_df.groupby("path_id"): assets = list(g.sort_values("step")["asset_name"]) - techs = [technique_label(t) for t in - hop_df[hop_df["path_id"] == pid].sort_values("hop")["technique"]] + techs = list(hop_df[hop_df["path_id"] == pid].sort_values("hop")["technique"]) labelled = assets[0] for nm, tech in zip(assets[1:], techs): labelled += f" --[{tech}]--> {nm}" @@ -183,7 +179,7 @@ def technique_label(raw): # -------------------------------------------------- # Pin both endpoints by id and enumerate all simple routes between them over the # technique-agnostic can_reach edge (any technique, 1..MAX_ROUTE_HOPS). This is the -# >=1.15 native point query -- src/dst unified to specific assets inside all_paths(). +# >=1.15-era native point query (still current) -- src/dst unified to specific assets inside all_paths(). src_pt, dst_pt = Asset.ref(), Asset.ref() route = model.path(src_pt.can_reach.repeat(1, MAX_ROUTE_HOPS), dst_pt).all_paths() diff --git a/v1/cybersecurity-attack-paths/pyproject.toml b/v1/cybersecurity-attack-paths/pyproject.toml index cf164c16..36a16b1f 100644 --- a/v1/cybersecurity-attack-paths/pyproject.toml +++ b/v1/cybersecurity-attack-paths/pyproject.toml @@ -9,7 +9,7 @@ description = "RelationalAI template: enumerate multi-step cyber attack chains a readme = "README.md" requires-python = ">=3.10" dependencies = [ - "relationalai==1.15.0", + "relationalai==1.21.1", "pandas>=2.0.0", ] diff --git a/v1/cybersecurity-attack-paths/runbook.md b/v1/cybersecurity-attack-paths/runbook.md index 10a75b5d..8f37ea6b 100644 --- a/v1/cybersecurity-attack-paths/runbook.md +++ b/v1/cybersecurity-attack-paths/runbook.md @@ -6,7 +6,7 @@ A security team wants to see how an external attacker could chain techniques to Asset graph (12 assets, 16 technique-tagged steps: exploit / cred / pivot) │ ▼ -/rai-graph-analysis — Path enumeration (multi-edge, relationalai>=1.15) +/rai-graph-analysis — Path enumeration (multi-edge, relationalai>=1.21) • kill-chain signature: exploit (from an internet-facing asset), then credential reuse, then 1-2 lateral pivots, into a crown jewel -> 3 chains • point query: every route from Public Web Server to Customer Database @@ -35,7 +35,7 @@ Each prompt is pasted into a fresh agent session loaded with the named `/rai-*` **Prompt:** /rai-graph-analysis Which attack chains follow the full kill-chain signature — an exploit step starting from an internet-facing asset, then a credential-reuse step, then one or two lateral pivots — and end at a crown jewel (an asset flagged `crown_jewel`)? Show the technique used at each hop. -**Response:** 3 kill-chains reach a crown jewel. VPN Gateway —exploit→ Jump Host —cred→ File Server —pivot→ Customer Database; Mail Relay —exploit→ Finance Workstation —cred→ Engineer Workstation —pivot→ Domain Controller; and a 4-hop chain Public Web Server —exploit→ Application Server —cred→ Jump Host —pivot→ Backup Server —pivot→ Customer Database. The technique order is enforced by the path pattern, so chains that move in a different order are correctly excluded. +**Response:** 3 kill-chains reach a crown jewel. VPN Gateway —Asset.exploit_to→ Jump Host —Asset.cred_to→ File Server —Asset.pivot_to→ Customer Database; Mail Relay —Asset.exploit_to→ Finance Workstation —Asset.cred_to→ Engineer Workstation —Asset.pivot_to→ Domain Controller; and a 4-hop chain Public Web Server —Asset.exploit_to→ Application Server —Asset.cred_to→ Jump Host —Asset.pivot_to→ Backup Server —Asset.pivot_to→ Customer Database. The technique order is enforced by the path pattern, so chains that move in a different order are correctly excluded. ## 4. All routes between one entry and one crown jewel