subvectors
Conformance vectors for OIDC trust subjects — the answer key for CI/CD OIDC trust decisions: a cited, versioned test-vector suite answering "does subject S satisfy trust condition C, and is C safe?"
When a CI pipeline authenticates to a cloud via OIDC (GitHub Actions to AWS/Azure/GCP today), the
entire security boundary is a string comparison: the token's sub claim versus an admin-written
matching rule — an AWS IAM trust-policy condition, an Azure federated-identity-credential (FIC)
subject, a GCP Workload Identity Federation attribute condition. Every security tool in this space
must re-implement that comparison and judge those rules. They re-figure it out alone, and they get
it wrong.
Status: v0.5.2 on PyPI - the corpus ships inside the wheel. An independent personal project, built on personal time and personal equipment. Every vector is source-cited, and the AWS tranche is now
observedagainst live AWS - each of those vectors links a committed transcript underobservations/holding the exact request and the verbatim response, so the claim is auditable without an AWS account. The generated Coverage block carries the exactdocumented/observedsplit.
The proof this is needed (verified 2026-07-04)
Checkov — one of the most widely used IaC security scanners — ships the only Azure FIC subject
check anywhere (CKV_AZURE_249). Read against its own source:
- It PASSES
repo:org/*(any repo in the org may assume the role) andrepo:org/repo:pull_request(unreviewed PR code may) — dangerous patterns waved through. - Its repo regex has no
@in the charset, so it will FAIL every valid immutable-format subject (repo:owner@123456/name@456789:...) — the format GitHub makes mandatory for repos created after 2026-07-15.
The formats churn (GitHub immutable claims, Azure flexible-FIC expressions in preview, per-issuer dialects from GitLab/Bitbucket/CircleCI), and every scanner re-derives the semantics from prose docs. A single maintained, cited corpus of test vectors fixes that for everyone.
What a vector looks like
{
"id": "gh-aws-0007",
"issuer": "github",
"subject": "repo:acme/webapp:pull_request",
"condition": { "consumer": "aws-stringlike", "pattern": "repo:acme/webapp:*" },
"expect": "match",
"judgment": {
"grade": "dangerous",
"reason": "pattern admits pull_request runs, which execute unmerged proposed changes rather than the protected default branch"
},
"sources": ["https://docs.github.com/en/actions/deployment/security-hardening-your-deployments"],
"status": "documented"
}
Three layers per vector:
- Grammar — is the subject well-formed for its issuer (classic AND immutable GitHub formats)?
- Match semantics — does it satisfy the consumer's condition (AWS
StringLike/StringEqualsglobbing, Azure FIC exact match + flexible expressions, GCP CEL)? - Judgment — is the condition safe? Graded findings for the patterns that matter:
pull_requestsubjects, unprotected refs, wildcarded repos/orgs, missingaudpinning. The graded patterns are a stable, citable vocabulary — seedocs/JUDGMENT-CATALOG.md.
Every vector carries a source citation and a provenance status — documented (derived from
primary documentation) or observed (recorded from a live exchange). The current split is
generated under Coverage rather than asserted here, so it cannot drift. A ~100-line
reference matcher (Python, pytest) passes the suite — it is a correctness oracle, not a product.
Coverage
14 suites - 147 vectors across 5 issuers and 6 consumer semantics.
Vectors by issuer x cloud-consumer semantics:
| Issuer | aws-stringlike | aws-stringequals | aws-all | azure-fic-exact | azure-fic-flexible | gcp-cel | Total |
|---|---|---|---|---|---|---|---|
github |
15 | 20 | 6 | 10 | 12 | 12 | 75 |
gitlab |
5 | 5 | 6 | 6 | 5 | 6 | 33 |
bitbucket |
1 | 2 | 3 | - | - | - | 6 |
circleci |
4 | - | 4 | - | - | 6 | 14 |
terraform-cloud |
3 | 3 | 1 | - | 6 | 6 | 19 |
| Total | 28 | 30 | 20 | 16 | 23 | 30 | 147 |
Suites:
bitbucket-aws0.1.0 - 6 vectorscircleci-aws0.2.0 - 8 vectorscircleci-gcp0.1.0 - 6 vectorsgithub-aws0.5.2 - 41 vectorsgithub-azure-flexible0.2.0 - 12 vectorsgithub-azure0.1.0 - 10 vectorsgithub-gcp0.1.0 - 12 vectorsgitlab-aws0.2.0 - 16 vectorsgitlab-azure-flexible0.1.0 - 5 vectorsgitlab-azure0.1.0 - 6 vectorsgitlab-gcp0.1.0 - 6 vectorsterraform-aws0.1.0 - 7 vectorsterraform-azure-flexible0.1.0 - 6 vectorsterraform-gcp0.1.0 - 6 vectors
Judgments: 32 safe - 48 caution - 43 dangerous - 24 ungraded (mechanical no-match / contrast vectors carry no safety grade).
Provenance: 136 documented - 11 observed.
Install
pip install subvectors
The wheel ships the entire corpus, so a consumer pins a versioned artifact instead of vendoring JSON by hand:
from subvectors import corpus
corpus.suite_names() # ['bitbucket-aws', ..., 'terraform-gcp']
corpus.load_suite("github-aws") # one suite, parsed
corpus.load_schema() # the JSON Schema the suites validate against
Prefer no dependency at all? The vectors are plain JSON under vectors/ —
clone and read. The corpus itself is CC0 (vectors/LICENSE).
Why an answer key instead of another scanner
Scanners in this space compete and get obsoleted: SpecterOps GitHound already maps workflow-to-cloud OIDC reach, Prowler shipped a GitHub provider (2026-07-02), Wiz and Datadog are converging. A test-vector suite does not compete with scanners — it grades them. Each new tool entering the space is a new consumer of the corpus, the way Wycheproof tests everyone's cryptography and the JSON-Schema-Test-Suite tests everyone's validators. Consumers keep their own matching code (no runtime dependency to trust) and import the vectors at test time.
Bugs the vectors expose in real tools get fixed by upstream PRs (Checkov's OIDC check family, Cartography's unparsed trust-policy conditions) — the distribution channel and the proof, in one.
Scope order
GitHub issuer + AWS consumer first, Azure FIC as the depth tranche (its exact-match and flexible-expression semantics are the least-tooled corner), then GCP CEL and non-GitHub issuers. Fully offline: JSON vectors + pytest, no cloud account required.
Why this project exists (for me)
Closes a specific, recurring gap: multi-cloud IAM / OIDC trust-boundary depth (AWS + Azure). Writing a falsifiable, cited test case about a trust rule forces genuinely understanding the rule — active-recall learning with a public artifact as the receipt.
License
Dual-licensed to maximize adoptability:
- Vector data (
vectors/) — CC0-1.0 (public-domain dedication). Embed the vectors in your tool's test suite with zero attribution or licensing friction — that frictionlessness is the point. Seevectors/LICENSE. - Everything else (the reference matcher, schema, docs) — Apache-2.0. See
LICENSE.
Metadata
Release files for subvectors 0.5.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| subvectors-0.5.2.tar.gz | 156.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| subvectors-0.5.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 251.0 kB
Release files / subvectors-0.5.2.tar.gz
| Download URL | subvectors-0.5.2.tar.gz |
|---|---|
| Size | 156.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2ece13f00a8f99d0b7bc4b3ebd715e834c6064107510c52ca70d6f80619e5a93
|
|
BLAKE2b-256 checksum How to use checksums |
db501c03953a09b5d0c565224e4dc4619e63d818d6987fec3a89de387af34786
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency logRelease files / subvectors-0.5.2-py3-none-any.whl
| Download URL | subvectors-0.5.2-py3-none-any.whl |
|---|---|
| Size | 94.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2a21dd403b1e92b1664c3f99920204989c7640f41b384f5a5bb30b39eac4d9e7
|
|
BLAKE2b-256 checksum How to use checksums |
5a5566846c18004741da847320823100185ce85d638b4066bfdd87959f9ff264
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.
Transparency log