Background
PR #584 added detection for 37 of the 46 pathfinding.cloud privilege-escalation paths cloudsplaining was missing. 7 were deliberately left out — not as an oversight, but because they're a structural mismatch for cloudsplaining's analysis model. This issue tracks how to handle them properly.
The problem
cloudsplaining flags a policy when its expanded actions are a superset of a known method's action list (PRIVILEGE_ESCALATION_METHODS in cloudsplaining/shared/constants.py, matched in scan/policy_document.py). That works for self-contained escalations — e.g. iam:PassRole + lambda:CreateFunction + lambda:InvokeFunction, where the policy itself proves the escalation.
A class of pathfinding.cloud paths are instead target-dependent: the action is privilege escalation only if an existing resource already has a more-privileged role attached. cloudsplaining only sees the policy in front of it — it has no view of which roles exist, what they can do, or what's attached to which resource — so it cannot distinguish escalation from completely benign use. Flagging these on an action match alone would produce massive false positives.
The 7 held paths
| pathfinding path |
trigger action |
privilege escalation only if… |
sts-001 |
sts:AssumeRole |
the assumed role is more privileged than the caller |
ssm-001 |
ssm:StartSession |
the target EC2 instance has a privileged instance role |
ssm-002 |
ssm:SendCommand |
the target EC2 instance has a privileged instance role |
cloudformation-002 |
cloudformation:UpdateStack |
the existing stack has an admin service role |
apprunner-002 |
apprunner:UpdateService |
the existing service has a privileged role |
codebuild-002 |
codebuild:StartBuild |
the existing project has a privileged role (+ buildspec-override) |
codebuild-003 |
codebuild:StartBuildBatch |
the existing project has a privileged role (+ buildspec-override) |
Note: sts:AssumeRole is already surfaced today under Credentials Exposure (ACTIONS_THAT_RETURN_CREDENTIALS) — it's just not (and shouldn't be) labeled as guaranteed privilege escalation. Adding it as a standalone privesc method would also undo the precision intent of #581.
Why it's hard
cloudsplaining is fundamentally a single-policy, action-level analyzer. Deciding whether sts:AssumeRole / ssm:SendCommand / cloudformation:UpdateStack is actually privesc requires cross-resource reachability:
principal → action → target resource → attached role → that role's effective permissions
That's a graph / relationship problem, not a subset match.
Proposed direction (future — not now)
- Build a privilege-escalation reachability graph over the account authorization details (principals, roles, trust policies, resource→role attachments) so we can evaluate whether a target is genuinely more privileged before flagging.
- Interim option: surface these as a separate, lower-severity / informational signal ("potential privilege escalation if the target resource has a privileged role") kept clearly distinct from the High-severity
PrivilegeEscalation findings, so it doesn't drown out the precise detections.
References
Background
PR #584 added detection for 37 of the 46 pathfinding.cloud privilege-escalation paths cloudsplaining was missing. 7 were deliberately left out — not as an oversight, but because they're a structural mismatch for cloudsplaining's analysis model. This issue tracks how to handle them properly.
The problem
cloudsplaining flags a policy when its expanded actions are a superset of a known method's action list (
PRIVILEGE_ESCALATION_METHODSincloudsplaining/shared/constants.py, matched inscan/policy_document.py). That works for self-contained escalations — e.g.iam:PassRole + lambda:CreateFunction + lambda:InvokeFunction, where the policy itself proves the escalation.A class of pathfinding.cloud paths are instead target-dependent: the action is privilege escalation only if an existing resource already has a more-privileged role attached. cloudsplaining only sees the policy in front of it — it has no view of which roles exist, what they can do, or what's attached to which resource — so it cannot distinguish escalation from completely benign use. Flagging these on an action match alone would produce massive false positives.
The 7 held paths
sts-001sts:AssumeRolessm-001ssm:StartSessionssm-002ssm:SendCommandcloudformation-002cloudformation:UpdateStackapprunner-002apprunner:UpdateServicecodebuild-002codebuild:StartBuildcodebuild-003codebuild:StartBuildBatchNote:
sts:AssumeRoleis already surfaced today under Credentials Exposure (ACTIONS_THAT_RETURN_CREDENTIALS) — it's just not (and shouldn't be) labeled as guaranteed privilege escalation. Adding it as a standalone privesc method would also undo the precision intent of #581.Why it's hard
cloudsplaining is fundamentally a single-policy, action-level analyzer. Deciding whether
sts:AssumeRole/ssm:SendCommand/cloudformation:UpdateStackis actually privesc requires cross-resource reachability:That's a graph / relationship problem, not a subset match.
Proposed direction (future — not now)
PrivilegeEscalationfindings, so it doesn't drown out the precise detections.References
research/pathfinding-cloud/proposed-new-methods.json— all 46 paths with per-path false-positive rationaleresearch/pathfinding-cloud/INTEGRATION-ANALYSIS.md— gap analysissts:AssumeRolebundling fix (context for avoiding action-on-sight over-flagging)