prowler-attack-paths-query
Generates openCypher queries for Prowler Attack Paths to identify cloud security risks.
Install
mkdir -p .claude/skills/prowler-attack-paths-query && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/5163" && unzip -o skill.zip -d .claude/skills/prowler-attack-paths-query && rm skill.zipInstalls to .claude/skills/prowler-attack-paths-query
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Creates Prowler Attack Paths openCypher queries for graph analysis (compatible with Neo4j and Neptune). Trigger: When creating or updating Attack Paths queries that detect privilege escalation paths, network exposure, or security misconfigurations in cloud environments.Key capabilities
- →Write openCypher queries for infrastructure graph analysis
- →Isolate nodes using AWSAccount anchor or automated labels
- →Chain graph traversals for privilege escalation detection
- →Validate query syntax for Neo4j and Amazon Neptune
How it works
It constructs graph traversal patterns by applying appropriate isolation labels or root anchors based on whether the query is predefined or custom.
Inputs & outputs
When to use prowler-attack-paths-query
- →Detect privilege escalation paths
- →Identify network exposure in AWS
- →Draft custom security misconfiguration queries
- →Verify query compatibility with Neo4j or Amazon Neptune
About this skill
Overview
Attack Paths queries are read-only openCypher queries over a Cartography-ingested cloud graph that detect privilege escalation chains, network exposure, and other graph-shaped security risks. Queries are written in openCypher Version 9 so they run on both Neo4j and Amazon Neptune sinks.
This skill is the concise, action-oriented reference for building queries. For the complete human-readable reference (graph model, list-typed and JSON-encoded properties, compatibility, and worked examples), see docs/developer-guide/attack-paths-queries.mdx.
Two query audiences
| Predefined queries | Custom queries | |
|---|---|---|
| Where they live | api/src/backend/api/attack_paths/queries/{provider}.py | User-supplied via the custom query API endpoint |
| Provider isolation | AWSAccount {id: $provider_uid} anchor + path connectivity | Automatic _Provider_{uuid} label injection by cypher_sanitizer.py |
| What to write | Chain every MATCH from the aws variable | Plain Cypher, no isolation boilerplate |
| Internal labels | Never use | Never use (system-injected) |
Predefined queries: every node must be reachable from the AWSAccount root via graph traversal. That is the isolation boundary.
Custom queries: write natural Cypher. The runner injects a _Provider_{uuid} label into every node pattern, and a post-query filter handles edge cases.
Input sources
Two sources for new queries:
-
pathfinding.cloud ID (e.g.
ECS-001,GLUE-001), the Datadog research catalogue. The aggregatedpaths.jsonis too large for WebFetch:# Fetch a single path by ID curl -s https://raw.githubusercontent.com/DataDog/pathfinding.cloud/main/docs/paths.json \ | jq '.[] | select(.id == "ecs-002")' # List all path IDs and names curl -s https://raw.githubusercontent.com/DataDog/pathfinding.cloud/main/docs/paths.json \ | jq -r '.[] | "\(.id): \(.name)"' # Filter by service prefix curl -s https://raw.githubusercontent.com/DataDog/pathfinding.cloud/main/docs/paths.json \ | jq -r '.[] | select(.id | startswith("ecs")) | "\(.id): \(.name)"'If
jqis unavailable, usepython3 -c "import json,sys; ...". -
Natural language description from the requester.
Query structure
Provider scoping parameter
| Parameter | Property | Used on | Purpose |
|---|---|---|---|
$provider_uid | id | AWSAccount | Scopes the query to a specific account |
The runner binds $provider_uid automatically. Every other node is isolated by path connectivity from the AWSAccount anchor.
Imports
from api.attack_paths.queries.types import (
AttackPathsQueryAttribution,
AttackPathsQueryDefinition,
AttackPathsQueryParameterDefinition,
)
from tasks.jobs.attack_paths.config import PROWLER_FINDING_LABEL
Always use PROWLER_FINDING_LABEL via f-string interpolation, never hardcode "ProwlerFinding".
Definition fields
- id: kebab-case
{provider}-{description}, e.g.aws-ec2-privesc-passrole-iam. - name: short, human-friendly label. Sourced queries append the reference ID:
"EC2 Instance Launch with Privileged Role (EC2-001)". - short_description: one sentence, no technical permissions.
- description: full technical explanation, plain text.
- provider:
aws,azure,gcp,kubernetes, orgithub. - cypher: f-string Cypher body. Literal
{/}are escaped as{{/}}. - parameters:
parameters=[]if none. - attribution: optional
AttackPathsQueryAttribution(text, link)for sourced queries.linkuses the lowercase ID.
Append the constant to the {PROVIDER}_QUERIES list at the bottom of the provider file.
Predefined query template
The canonical shape combines a principal walk, an optional target walk, deduplicated nodes, and a typed finding overlay:
AWS_{QUERY_NAME} = AttackPathsQueryDefinition(
id="aws-{kebab-case-name}",
name="{Label} ({REFERENCE_ID})",
short_description="{One sentence.}",
description="{Full technical explanation.}",
attribution=AttackPathsQueryAttribution(
text="pathfinding.cloud - {REFERENCE_ID} - {permission}",
link="https://pathfinding.cloud/paths/{reference_id_lowercase}",
),
provider="aws",
cypher=f"""
// Find principals with {permission}
MATCH path_principal = (aws:AWSAccount {{id: $provider_uid}})--(principal:AWSPrincipal)-[:POLICY]->(policy:AWSPolicy)-[:STATEMENT]->(stmt:AWSPolicyStatement {{effect: 'Allow'}})
MATCH (stmt)-[:HAS_ACTION]->(act:AWSPolicyStatementActionItem)
WHERE toLower(act.value) IN ['{permission_lowercase}', '{service}:*']
OR act.value = '*'
WITH DISTINCT aws, principal, stmt, path_principal
// Pre-aggregate the statement's resource values (see "Avoiding cartesian products")
MATCH (stmt)-[:HAS_RESOURCE]->(res:AWSPolicyStatementResourceItem)
WITH aws, principal, path_principal, collect(DISTINCT res.value) AS res_values
WITH aws, principal, path_principal, res_values, ('*' IN res_values) AS res_wildcard
// Target policies attached to the principal, matched once against the resource list
MATCH path_target = (aws)--(target_policy:AWSPolicy)--(principal)
WITH path_principal, path_target, res_values, res_wildcard, target_policy.arn AS parn
WHERE parn CONTAINS $provider_uid
AND (res_wildcard OR size([rv IN res_values WHERE parn CONTAINS rv]) > 0)
WITH DISTINCT path_principal, path_target
WITH collect(path_principal) + collect(path_target) AS paths
UNWIND paths AS p
UNWIND nodes(p) AS n
WITH paths, collect(DISTINCT n) AS unique_nodes
UNWIND unique_nodes AS n
OPTIONAL MATCH (n)-[pfr:HAS_FINDING]-(pf:{PROWLER_FINDING_LABEL} {{status: 'FAIL'}})
RETURN paths, collect(DISTINCT pf) as dpf, collect(DISTINCT pfr) as dpfr
""",
parameters=[],
)
Key points:
- The principal walk types the
POLICYandSTATEMENThops. Both are low-fan-out (each principal has a handful of policies; each policy a handful of statements), so the typed edge lets the planner cost a cheap inline filter. - The
(aws)--hub hops stay anonymous.AWSAccountis a high-degree node that fans out to every principal, role, policy, and resource in the account; typing those edges forces the planner to enumerate from the hub and collapses performance on multi-tenant Neptune. - Other relationship types appear only where the file's existing queries already use one (
TRUSTS_AWS_PRINCIPAL,STS_ASSUMEROLE_ALLOW,MEMBER_AWS_GROUP,HAS_EXECUTION_ROLE). - The finding probe is typed
:HAS_FINDINGand left undirected. The type lets Neptune apply an inline edge filter; the lack of direction matches the convention of the rest of the file. - Collapse duplicate rows after each permission gate with
WITH DISTINCT, carrying only the variables needed by later clauses. - Each
HAS_*traversal is its ownMATCHclause with aWHEREon the child item node.WITH DISTINCT path_principal, path_targetprecedescollect(path...)to dedupe the row multiplication produced by the joins. - The
RETURNshapepaths, dpf, dpfris the contract the serializer and visualiser depend on. Do not change it.
Avoiding cartesian products
Matching a target set (AWSRole, AWSUser, AWSGroup) and then filtering each target against a statement's HAS_RESOURCE items in a separate, unconnected MATCH builds a cartesian product: every target is paired with every resource item before the filter runs. On accounts with many principals this errors or times out. Pre-aggregate the resource values into a list, then match each target once:
// Pre-aggregate the statement's resource values into a list
MATCH (stmt)-[:HAS_RESOURCE]->(res:AWSPolicyStatementResourceItem)
WITH aws, path_principal, collect(DISTINCT res.value) AS res_values
WITH aws, path_principal, res_values, ('*' IN res_values) AS res_wildcard
// Match each target once; bind name/arn to locals so the predicate reads them once
MATCH path_target = (aws)--(target_role:AWSRole)
WITH path_principal, path_target, res_values, res_wildcard,
target_role.name AS rname, target_role.arn AS rarn
WHERE res_wildcard
OR size([rv IN res_values WHERE rv CONTAINS rname OR rarn CONTAINS rv]) > 0
- Aggregate resources before matching targets; cost becomes
targets + resources, nottargets × resources. This is a pure rewrite, the result set is identical. ('*' IN res_values)short-circuits the wildcard grant so the list scan runs only when needed.- Bind
target.name/target.arnto locals so the list comprehension reads them once per target, not once per resource value. size([...]) > 0is the Neptune-compatible form ofany()(see "openCypher compatibility").- Two-statement queries aggregate each statement's resources into its own list (
res_values,res2_values) and combine the twosize([...]) > 0checks withAND. - Targets already constrained by a relationship (
STS_ASSUMEROLE_ALLOW,TRUSTS_AWS_PRINCIPAL) need no aggregation: the relationship already bounds the set.
Privilege escalation sub-patterns
Four path_target shapes cover the common attack types. Each shares the canonical template's path_principal, deduplication tail, and RETURN; only the path_target MATCH and it
Content truncated.
When not to use it
- →Non-graph based security analysis
- →General SQL database queries
Prerequisites
Limitations
- →Requires graph data to be in Cartography format
- →Complex traversals must explicitly respect the AWSAccount root
How it compares
It automates the inclusion of required isolation boilerplate and schema-specific labels for cloud infrastructure analysis.
Compared to similar skills
prowler-attack-paths-query side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| prowler-attack-paths-query (this skill) | 1 | 2mo | Review | Advanced |
| supabase-rls-policy-generator | 11 | 9mo | No flags | Advanced |
| sqlmap-database-penetration-testing | 4 | 6mo | Review | Advanced |
| data-safety-auditor | 3 | 7mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by prowler-cloud
View all by prowler-cloud →You might also like
supabase-rls-policy-generator
hopeoverture
This skill should be used when the user requests to generate, create, or add Row-Level Security (RLS) policies for Supabase databases in multi-tenant or role-based applications. It generates comprehensive RLS policies using auth.uid(), auth.jwt() claims, and role-based access patterns. Trigger terms include RLS, row level security, supabase security, generate policies, auth policies, multi-tenant security, role-based access, database security policies, supabase permissions, tenant isolation.
sqlmap-database-penetration-testing
davila7
This skill should be used when the user asks to "automate SQL injection testing," "enumerate database structure," "extract database credentials using sqlmap," "dump tables and columns from a vulnerable database," or "perform automated database penetration testing." It provides comprehensive guidance for using SQLMap to detect and exploit SQL injection vulnerabilities.
data-safety-auditor
ananddtyagi
Comprehensive data safety auditor for Vue 3 + Pinia + IndexedDB + PouchDB applications. Detects data loss risks, sync issues, race conditions, and browser-specific vulnerabilities with actionable remediation guidance.
detecting-sql-injection-vulnerabilities
jeremylongshore
Detect and analyze SQL injection vulnerabilities in application code and database queries. Use when you need to scan code for SQL injection risks, review query construction, validate input sanitization, or implement secure query patterns. Trigger with phrases like "detect SQL injection", "scan for SQLi vulnerabilities", "review database queries", or "check SQL security".
row-level-security
dadbodgeoff
Implement PostgreSQL Row Level Security (RLS) for multi-tenant SaaS applications. Use when building apps where users should only see their own data, or when implementing organization-based data isolation.
supabase-data-handling
jeremylongshore
Implement Supabase PII handling, data retention, and GDPR/CCPA compliance patterns. Use when handling sensitive data, implementing data redaction, configuring retention policies, or ensuring compliance with privacy regulations for Supabase integrations. Trigger with phrases like "supabase data", "supabase PII", "supabase GDPR", "supabase data retention", "supabase privacy", "supabase CCPA".