Skip to content
This repository was archived by the owner on Oct 10, 2026. It is now read-only.

v0.5.2 - four findings, four sources

Choose a tag to compare

@Dashtid Dashtid released this 01 Sep 23:06
· 37 commits to main since this release
1ddc365

Four findings from an audit sweep, each verified against a primary source before
it was acted on. Two were errors in this corpus; one is an error in AWS's
documentation; one is a grammar bug that rejected a shape GitHub documents how to
mint.

The corpus was wrong about GitLab

It asserted that no ref_protected guard is expressible in a single-key sub
condition. It is. GitLab's ALLOWED_SUB_CLAIM_COMPONENTS
(app/models/project_ci_cd_setting.rb) is project_path, project_id, ref_type, ref, ref_protected, environment_protected, deployment_tier, configurable per
project through ci_id_token_sub_claim_components.

Two texts carried the false claim and are corrected. The sharper consequence is on
the Azure side: gl-flex-matches-any-branch concluded that because Azure flexible
FIC supports only the sub claim, no protection guard is possible. The Azure
limitation is real — the conclusion was not. Moving ref_protected inside the
sub routes around it. (pipeline_source has no such route; it is not an allowed
component, so that half of the claim stands.)

AWS's documented CircleCI policy does not work as written

AWS's OIDC condition-keys page shows, verbatim:

"oidc.circleci.com/org/12345:aud": "sts.amazonaws.com"

CircleCI documents the same claim as "The audience. By default, this is
ORGANIZATION_ID, a string containing a UUID that identifies the job's project's
organization."
Copy AWS's example, run a job with a default token, and the aud
StringEquals is false: the ANDed block fails and the credential is refused
however correct the project-id pin is.

Fails closed, so nothing is over-permitted — an availability break in a documented
example, silent at the STS call rather than at policy creation. New vector:
circleci-aws-docs-aud-contradiction-denies.

The grammar rejected a documented subject

parse_repo_segment required a trailing :, so repo:octo-org/octo-repo returned
None. GitHub's sub-customization docs give include_claim_keys: ["repo"] as the
template that "lets you grant cloud access to all the workflows in a specific
repository, across all branches/tags and environments" — which mints exactly that
bare form. Fixed; wildcards in the owner and repo positions are still rejected.

New vector gh-aws-repo-only-customized-sub-admits-everything grades it: a
wildcard-free StringEquals pin that admits every branch, tag, environment and
pull_request of the repository. The over-permission lives in what the subject
omits, so a scanner grading conditions by looking for * in the pattern sees
nothing to flag.

And one promise this repo could not keep

Found while fixing the above. github.py claimed any value containing * or ?
"is not a valid subject here and returns None". Only the owner and repo positions
exclude wildcards; repo:octo-org/octo-repo:* has always returned a concrete
segment. The promise was narrowed rather than the parser — a consumer grading the
corpus's most common dangerous pattern wants to know which repository it names.
Both behaviours are now pinned by tests.

Corpus

14 suites / 147 vectors; github-aws 0.5.2, circleci-aws 0.2.0. Provenance
unchanged at 11 observed. 378 tests.

Install

pip install subvectors==0.5.2

The wheel ships the entire corpus. The vectors are CC0; everything else is
Apache-2.0.