Repository navigation
v0.4.2 - AWS validates only the first value
AWS's role-creation guardrail validates only the first value of a GitHub-OIDC
sub condition. A scoped-to-all * is invisible to it in any other position.
The finding
iam:CreateRole was probed five ways, every result recorded as a committed
transcript under
observations/:
Trust policy sub condition (StringLike) |
Result |
|---|---|
["repo:octo-org/octo-repo:ref:refs/heads/main", "repo:octo-org/*"] |
accepted |
["repo:acme/x", "*"] |
accepted |
["*", "repo:acme/x"] |
rejected |
["*"] |
rejected |
| no condition at all | rejected |
Rows 2 and 3 hold the same two values in opposite order and get opposite
verdicts. AWS is not scanning the list for a non-scoped value — if it were, row 3
would pass on its second element. It reads element [0] and stops.
All four rejections return the identical MalformedPolicyDocument, saying the
policy "must evaluate, using StringEquals, StringLike or StringEqualsIgnoreCase,
token.actions.githubusercontent.com:sub or ...:job_workflow_ref which is not
scoped to all". That message describes a stricter check than AWS performs: the
2023 condition-less class is closed at creation, while a * smuggled into any
non-leading position of an OR-list passes.
Why it matters: IAM evaluates multiple values of one condition key with a logical
OR, so the loosest value sets the trust boundary at authentication time
regardless of where it sits in the list. Creation-time validation and run-time
evaluation disagree about which values matter.
This is the same first-value-only defect the corpus fed upstream to Checkov
(#7665) — here in the cloud
provider's own validator.
Provenance
Unchanged from 0.4.0 at 122 documented / 11 observed; github-aws suite
0.4.2. The flagship vector gh-aws-multivalue-loose-value-poisons-list now carries
all five creation probes plus the policy-simulator evaluation in its evidence, each
naming its transcript.
Harness
scripts/observe_aws.py gained a collision guard: ad-hoc probe labels are derived
from a hash of (operator, values), --label names a run explicitly, and
write_transcript refuses to overwrite a transcript that records a different
probe. Re-running the same probe still overwrites. Found the hard way — two ad-hoc
probes collided on one filename during this session and the first result survived
only because it had already been committed.
Install
pip install subvectors==0.4.2
The wheel ships the entire corpus. The vectors are CC0; everything else is
Apache-2.0. (0.4.1 exists in git history only.)