iam-ape: resolve principal-based deny conditions (ARN/account/tag) (CIEM-685)#37
Open
yanas-orca wants to merge 4 commits into
Open
iam-ape: resolve principal-based deny conditions (ARN/account/tag) (CIEM-685)#37yanas-orca wants to merge 4 commits into
yanas-orca wants to merge 4 commits into
Conversation
Effective-permissions evaluation now resolves deny-statement conditions that depend solely on aws:PrincipalARN against the evaluated principal, instead of carrying them symbolically. A provable deny becomes unconditional (action removed); a provably inapplicable one is skipped; conditions referencing any other key are left untouched (prior behavior). Bump 1.1.7 -> 1.1.8; add tests.
There was a problem hiding this comment.
Orca Security Scan Summary
| Status | Check | Issues by priority | |
|---|---|---|---|
| Secrets | View in Orca | ||
| Vulnerabilities | View in Orca |
… + tag) Widen deny-condition resolution to aws:PrincipalARN, aws:PrincipalAccount and aws:PrincipalTag/*, and resolve the whole Condition BEFORE the policy expander splits multi-operator blocks into independent entries -- so AND semantics are preserved (a mixed account+arn deny like the real p-gh487uoz resolves correctly). SCPs are partitioned in __init__: the static baseline (incl. FullAWSAccess *) is expanded once; only principal-conditional Deny statements are resolved+expanded per principal, so no per-principal re-expansion of *. should_deny/boundary hot path is unchanged. Undecidable clauses (region/MFA/tags-absent/...) stay symbolic.
…reduce resolver complexity)
|
Tohar-orca
approved these changes
Jul 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Problem
iam-ape does not evaluate IAM conditions — it carries them symbolically. A conditional deny is treated as a partial deny: the action stays in the effective allow-set (with the condition merged in) rather than being removed. Only an unconditional deny fully removes an action.
That's correct for request-context conditions a static evaluator can't know (region, MFA, source IP, resource tags, time). But conditions keyed on the principal —
aws:PrincipalARN,aws:PrincipalAccount,aws:PrincipalTag/*— are decidable at evaluation time, because ape evaluates per-principal and holds the principal's entity object.Impact (CIEM-685): customer root SCPs deny
bedrock:Create/Update/DeleteGuardrailconditionally — e.g.ArnNotLike aws:PrincipalARN […SSO admin roles], and a second SCP withStringNotEquals aws:PrincipalAccount […] AND ArnNotLike aws:PrincipalARN […]. Because the denies are conditional, ape leaves the guardrail actions in a non-exempt role's effective allow-set, so the "unauthorized guardrail management" alert keeps firing even though the role is denied those actions in the cloud.Fix
Resolve deny conditions built only from known principal facts against the evaluated principal:
aws:PrincipalARN,aws:PrincipalAccount(parsed from the ARN),aws:PrincipalTag/<k>(from the principal's Tags).ArnLike/ArnNotLike/ArnEquals/ArnNotEquals+String*equivalents.Why resolve before expansion
The policy expander splits a multi-operator
Conditionblock into independent single-operator deny entries (expand_policy._append_action). Resolving after expansion would treatA AND Bas two separate denies and could strip an action based on one clause alone — wrongly closing alerts. So conditions are resolved on the whole statement, before expansion, preserving AND semantics (verified against the realp-gh487uozaccount+arn deny).How it stays fast
SCPs are partitioned in
__init__:FullAWSAccess *allow + any statement we can't resolve) is expanded once, as today;*.The
should_deny/ permission-boundary hot path is unchanged — resolved denies simply arrive unconditional.Tests
tests/test_principal_conditions.py(10 cases): unconditional removal; ARN condition applies/skips (non-exempt vs exempt); account condition applies/skips; mixed account+ARN AND-semantics (applies for non-allowed account, skips for allowed); principal-tag applies/skips; absent-tag stays symbolic; region condition stays symbolic; mixed principal+region stays symbolic.All 48 tests pass (incl. existing
test_e2e). black / isort / flake8 clean; mypy clean forevaluator.py. Version bumped 1.1.7 → 1.1.8.Validated on the customer's real SCPs (
p-6pwmb6if+p-gh487uoz): a non-exempt role loses the guardrail actions (alert clears); the exempt SSO admin roles keep them (alert correctly still fires for those — a separate rule-tuning question).Notes for reviewers (IAM team)
aws:PrincipalOrgID/OrgPaths(not inget-account-authorization-details),aws:username/userid(runtime-session ambiguity for roles), and all request-context keys (undecidable statically).