Repository navigation
fix(auth): follow IAM's rules for policy variables in condition values - #541
Open
LeeroyHannigan wants to merge 2 commits into
Open
LeeroyHannigan wants to merge 2 commits into
LeeroyHannigan wants to merge 2 commits into
Conversation
…10-17 documents
A condition value in a 2008-10-17 document, or in one with no Version, is
compared as written: ${aws:username} is the literal text. Only a 2012-10-17
document expands variables, the same rule as Resource and NotResource.
In a 2012-10-17 document the escapes ${*}, ${?} and ${$} stand for the
literal characters. StringLike and ArnLike match the escaped character
literally while a * or ? written in the policy stays a wildcard, so a${*}
matches a* and not abc. The result of an escape is not expanded again.
A variable with no default whose key the request does not carry makes the
value match nothing: a positive operator is false against it and a negated
operator holds.
The document's choice reaches condition evaluation through a flag on
evaluate_statement_conditions and evaluate_condition_match; the identity,
boundary and session policy paths pass their own document's Version. The
trust policy path keeps expanding whatever the Version.
Measured with the IAM policy simulator on 2026-10-24, nineteen cells, one
evaluator test each. Closes #534.
… for string and ARN operators
Two rules measured on IAM after the condition-variable change:
- A trust policy follows the same Version rule as an identity policy.
With AssumeRole on fresh roles, a 2008-10-17 trust policy, and one
with no Version, compares ${aws:PrincipalArn} in a condition as
literal text, and a 2012-10-17 one expands it. The trust evaluator
expanded variables in every trust policy.
- NumericEquals and Bool compare a value written as a variable as
written. Only the string and ARN operators now expand variables, with
or without a set qualifier or IfExists; the date, address and Null
operators are treated as the numeric ones.
Tests pin both, and the measured cases for a wildcard inside a
substituted value, IfExists and the set qualifiers with an unresolved
variable. The security model states the operator rule, the trust
policy rule, and how a negated operator behaves for a caller without
the key.
LeeroyHannigan
requested review from
amrith,
c33howard,
jcshepherd,
pdf-amzn,
robinnsc and
yesyayen
as code owners
October 25, 2026 01:00
This branch has not been deployed
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.
Fixes #534. Stacked on #535: this branch builds on
fix/resource-policy-variablesand reuses itsvariables.rsmodule, so the diff below is the two commits on top of it.Summary
Policy variables in condition values now follow IAM's rules, each measured with the IAM policy simulator or, for trust policies, with AssumeRole:
2012-10-17document expands variables in condition values. A2008-10-17document, or one with noVersion, compares${aws:username}as the literal text, and the default form too. This applies to identity policies, permissions boundaries, session policies and trust policies.${*},${?}and${$}expand to the literal characters. InStringLikeandArnLike(andArnEquals) the escaped character, and any character that comes from a substituted value, is matched literally, while a*or?written in the policy is a wildcard.${key, 'default'}expands todefaultwhen the key is absent.IfExistsand the set qualifiers following the same rule. Before this change the variable was left as literal text and compared.NumericEqualsorBoolvalue written as a variable is compared as written; the date, address andNulloperators are treated the same way.dynamodb:LeadingKeysis never a variable (from fix(auth): expand policy variables in Resource and NotResource #535).evaluate_conditionkeeps its signature for its existing callers and expands as a2012-10-17document does;evaluate_condition_withandevaluate_statement_conditionstake the document's choice, which the evaluator passes from each identity policy, boundary and session policy and the trust evaluator from the trust policy'sVersion.Measurements
IAM policy simulator, 24 and 25 October 2026, action
dynamodb:GetItemonarn:aws:dynamodb:us-east-1:123456789012:table/blue,Allow dynamodb:*on*besideDeny dynamodb:GetItemon*with the condition below. Keys areaws:PrincipalTag/team(team),aws:ResourceTag/owner(owner) andaws:SourceArnunless named.StringLiketeama${*}abca*StringLiketeama*abcStringEqualsteam${?}?StringLiketeama${?}aba?StringEqualsteam${$}$StringEqualsteama${$}{x}a${x}StringEqualsteam${*}*${*}StringEqualsowner${aws:username}alice, owneralicealice, owner${aws:username}alice, owner${aws:username}ArnLikeSourceArnuser/a${*}user/abcuser/a*StringEqualsowner${aws:username, 'anon'}anonanonStringEqualsowner${aws:username}${aws:username}StringNotEqualsowner${aws:username}aliceStringLikeowner${aws:PrincipalTag/team}a*, ownerabca*, ownera*ArnLikeorArnEqualsSourceArnuser/${aws:PrincipalTag/name}alice, SourceArnuser/aliceal*, SourceArnuser/aliceStringEqualsIfExistsowner${aws:username}alice, no userStringNotEqualsIfExistssamealice, no userForAllValues:StringNotEqualsaws:TagKeys${aws:username}[a, b], no userForAnyValue:StringEqualssame[a, b], no userNumericEqualsaws:MultiFactorAuthAge${aws:PrincipalTag/age}300, MultiFactorAuthAge 300Boolaws:SecureTransport${aws:PrincipalTag/flag}true, SecureTransport trueTrust policies, AssumeRole on fresh roles whose trust policy allows the caller's role under the condition on
aws:PrincipalArnagainst${aws:PrincipalArn}:StringEqualsStringNotEqualsStringEqualsStringNotEqualsTesting
explicit_deny_matches_arcpaths.*or?read as a wildcard (6), an unresolved variable left as text (5), the trust policy'sVersionignored (2), every operator expanding (2).cargo fmt --check,cargo clippy --workspace --all-targets -- -D warnings, andcargo test --workspace --exclude extenddb-storage-cassandra: 1,856 passed, 0 failed.cargo test -p extenddb-authon 1.88.0: 305 passed.Behaviour change for existing policies
2008-10-17policy, or one with noVersion, no longer expands; trust policies included.${...}text: a negated operator now holds. AnAllowwritten asStringNotEqualsagainst${aws:PrincipalTag/team}grants to a caller without that tag; aNullcondition on the tag requires it.Bool, address orNullvalue written as a variable is no longer expanded.Merging with #471 and #522
Arn*arms ofcompare_single. Resolved, each arm readsis_arn(context_value) && value.arn_matches(context_value)(negated for theNotforms), with theUnresolvedearly return before it.evaluate_condition_match, which this branch also changes.request_tag_tests.rsthere callsevaluate_statement_conditionswith two arguments and gains theexpand_variablesargument. Itsreads_unresolvable_variableshould readexpand_variables && ..., so a2008-10-17document whose value holds${aws:RequestTag/x}as text is not treated as reading an unresolvable key.Not changed here
A variable that supplies a whole ARN in an
Arn*operator (ArnLikeon${aws:PrincipalTag/src}): the policy simulator refuses such a policy as invalid input, so IAM's evaluation of it is not measurable there. ExtendDB evaluates it with the substituted text matched literally.Checklist
cargo fmt,cargo clippycleandocs/manuals/10-security-model.md)