OpenSourceOM attack path queries
Core ships six named queries. You run them from the CLI, the API, or the console’s query dropdown. There is no ad-hoc graph language in this version. What attack path analysis means, as a practice, is in Attack path analysis.
./om paths list
./om paths run internet-to-datastore
HTTP: GET /v1/graph/queries lists them.
GET /v1/graph/query?name=internet-to-datastore runs one.
The JSON result is query, summary, and paths (each path
is an array of nodes).
What a result looks like
CLI output is plain text. The HTTP body is JSON. Each path is the ordered list of nodes, starting at the internet node when the walk started there:
{
"query": "internet-to-datastore",
"summary": "Attack paths from the internet through workloads to datastores",
"paths": [
[
{ "id": "internet:global", "type": "Internet", "name": "Internet" },
{ "id": "aws:sg:sg-web", "type": "Network", "name": "sg-web" },
{ "id": "aws:ec2:i-web-1", "type": "Workload", "name": "web-1" },
{ "id": "aws:iam:role/AdminRole", "type": "Identity", "name": "AdminRole" },
{ "id": "aws:rds:prod-db", "type": "Datastore", "name": "prod-db" }
]
]
}
Walks follow every edge type, not only REACHABLE. A cycle is cut when the next
node is already on the path. Depth counts edges. The demo path
Internet → sg-web → web-1 → AdminRole → prod-db is four edges, so it fits inside the
datastore walk.
internet-to-workload
Recursive walk from internet:global along any edge type. A path is kept when it
ends on a Workload. Recursion stops before depth 6. At most 50 paths are returned.
Shorter paths that end on a workload earlier in the walk are included, so one exposed
instance can appear as its own path and again as a prefix of a longer one.
internet-to-datastore
Same walk, ending on a Datastore, and the path must include at least one
Workload. Recursion stops before depth 8. At most 50 paths are returned. This is
the query the demo prints after om scan demo. It does not look at
sensitivity. A log bucket and a customer database on the same shape of path both
appear.
internet-to-sensitive-datastore
The same walk as internet-to-datastore, with the same depth and path caps, kept
only when the datastore’s sensitivity property is a non-empty string after
trimming. A missing property and a blank value stay in internet-to-datastore and
drop out of this one. The value itself is not ranked: customer and
restricted both count. Collectors copy it from a tag or label named
sensitivity or data-class. The demo marks prod-db and
leaves the log and asset buckets unmarked.
public-datastore
Single-node results: datastores where public_access is true or
public_access_block is disabled. The match is the node type and
those properties, so a public S3 bucket and a public GCS bucket both appear. Limit 100.
public-s3-buckets is an alias and is not listed.
admin-identities
Single-node results: identities with admin_access set to true. Limit 100.
Collectors set that flag from role definitions and IAM bindings, described on each collector page.
admin-to-public-datastore
Pairs a public datastore (same property test as public-datastore) with an
identity that has admin_access and a CAN_ACCESS edge whose target is
that datastore. Limit 50. The returned node list is the datastore, then the identity.
toxic-s3-public-with-admin-role is an alias and is not listed.
Attack-path findings
Named queries answer a question you ask. om rules run and
POST /v1/rules/run also write the combination into the findings list. Control
rules run first. The attack-path rule then writes one finding for each existing
finding on an internet-reachable workload, paired with a datastore that workload can reach.
A workload is internet-reachable when a REACHABLE walk from
internet:global ends on it, with the same depth limit as
internet-to-datastore (recursion stops before depth 8). The source finding is a
VIOLATES edge whose target is that workload. Findings with
finding_type: attack_path are not sources, so the pass does not chain. Two
findings on the same workload produce two attack-path rows for the same datastore.
The workload reaches a datastore in either of two hops. A CAN_ACCESS edge from
the workload to the datastore is one. ASSUMES to an identity that itself has
CAN_ACCESS is the other. The demo path uses the second: Internet → sg-web →
web-1 → AdminRole → prod-db. When both hops reach the same datastore, the shorter path is
stored, which is the direct CAN_ACCESS edge. The finding description names the
source finding and the datastore, for example "Internet-exposed workload: web-1 is on a path
to prod-db."
path is that ordered list of node ids. The finding id is
finding:attack-path:{source-finding-id}:{datastore-id}.
finding_type is attack_path. normalized_score and
severity are copied from the source finding’s score, using the same bands as other rules. A
source with no score is stored as 75, severity high. The graph-context bonuses are not added
again. The VIOLATES edge points at the workload, so the findings list names that
workload as the affected resource. datastore_id and
source_finding_id are properties on the finding.
A reachable workload with no datastore hop does not get this finding. A hop whose workload
has no other finding does not either. Run om rules run attack-path on a graph
that has the edges and no findings, and the pass writes nothing.
om rules run with no id is different: cspm-internet-workload writes
a finding on every internet-reachable workload first, and the attack-path pass then pairs
that finding with each datastore the workload can reach. sensitivity does not
filter these rows. internet-to-sensitive-datastore remains the query that keeps
only marked datastores.
Re-running the rule deletes an attack-path finding whose hop or source finding is gone.
Other rules’ findings stay. om enrich cve does not write attack-path rows. Run
rules again after enrichment so a new CVE is paired with the datastore.
GET /v1/findings returns path on the row. The console prints those
ids on the card. SIEM export includes the same list.
Audit events on a path
om scan aws reads CloudTrail management events, om scan azure
reads administrative Activity Log events, and om scan gcp reads Admin Activity
audit logs, from the last 24 hours. A match is stored as
audit_events on the identity and the resource the event names. A match requires
both nodes to already be in the scan, and the resource has to sit on an exposed path: an
internet-reachable workload, something that workload assumes or can access, a public
datastore, or an identity that can access one. At most five events are kept on a node,
newest first. A failed lookup leaves the property off and the rest of the scan still ingests.
GET /v1/graph/query copies an event into audits when that
resource node is on the returned path. index is the path’s position in
paths. The same event stored on two nodes of one path is listed once.
om paths run prints the time, event name, principal, and resource under that
path. The console does the same. GetObject is an S3 data event and is not in
this slice. Entra ID sign-in logs and GCP Data Access logs are not collected. See the
AWS collector, the
Azure collector, and the
GCP collector.
Reading an empty result
An empty path list means the edges are not there, not that the account is safe. The walk
cannot start unless some edge leaves internet:global. That edge is missing when
an AWS instance has no public IPv4 or its security groups are closed, when an Azure NIC has
no public IP, when a GCE instance has no NAT IP, or when a pod is selected only by
ClusterIP Services. Check om graph stats and the collector page before treating
zero paths as a clean bill.
public-datastore and admin-identities do not need that edge. They
match properties. A public Azure storage account shows up in public-datastore
even when no VM is reachable from the internet.
Questions
Which attack path queries does OpenSourceOM run?
om paths list prints six names. internet-to-datastore walks from the internet node and keeps paths that include a workload and end on a datastore. internet-to-sensitive-datastore is that walk limited to datastores whose sensitivity property is set. The other four are internet-to-workload, public-datastore, admin-identities, and admin-to-public-datastore. public-s3-buckets and toxic-s3-public-with-admin-role still run as aliases.
How do I run an attack path query?
After a scan, run om paths run internet-to-datastore. The same named queries are available from GET /v1/graph/query and from the console dropdown.
When does a rules run write an attack-path finding?
After the control rules, om rules run writes one finding per existing finding on an internet-reachable workload that can reach a datastore. The finding stores the path as ordered node ids. GET /v1/findings returns that path on the row.
Where do cloud audit events show up on a path?
om scan aws stores recent CloudTrail management events, om scan azure stores recent Activity Log events, and om scan gcp stores recent Admin Activity audit logs, on the identity and resource they name. GET /v1/graph/query returns those events in audits when the resource node is on a path. om paths run prints the same lines under the path.
Related reading
Copyright © 2026 OpenSourceOM. Licensed under Apache-2.0.