Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions v1/cybersecurity-attack-paths/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the part of the PR comment:

no substantive changes needed — each was run end-to-end on 1.21.1 with its current (1.15-pinned) code and passes unchanged. Their pins/doc version references can move when there's a real reason to touch them.

Seems like we should update their pins too, right? Otherwise the AIs learning from these examples will think they need to run on older versions?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question for @cafzal: Is it better to leave these minimum bounds more permissive, or to keep things updated to encourage the AIs that are reading these files to use the latest version?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just focus on supporting 1.21+. we'll make that clear in skills repo readme and setup skill

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave this out of this PR - there are dozens of templates that may need updating.

- OS notes: works on macOS, Linux, and Windows; the Quickstart's virtual-environment activation command assumes macOS or Linux.

## Quickstart
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -176,7 +176,7 @@ See `cybersecurity_attack_paths.py` for the implementation and `runbook.md` to r
<details>
<summary>Why do I see <code>relationalai</code> version or path import errors?</summary>

- 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`.

</details>

Expand Down
16 changes: 6 additions & 10 deletions v1/cybersecurity-attack-paths/cybersecurity_attack_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -157,16 +157,12 @@ def load_csv(filename):
hop_df = hop_df.drop_duplicates(["path_id", "hop"]).sort_values(["path_id", "hop"])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to drop duplicates? There shouldn't be any, right?
And similarly I guess the data is also sorted by path_id, hop.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a question for @cafzal


# Reassemble each kill-chain: ordered asset names + the technique used at each hop.
def technique_label(raw):
# relationship labels arrive as e.g. "-<exploit_to>->"; 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}"
Expand All @@ -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()
Expand Down
2 changes: 1 addition & 1 deletion v1/cybersecurity-attack-paths/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand Down
4 changes: 2 additions & 2 deletions v1/cybersecurity-attack-paths/runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down
Loading