Skip to main content

subvectors

PyPI CI License: Apache-2.0 Python

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.4.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 observed against live AWS - each of those vectors links a committed transcript under observations/ holding the exact request and the verbatim response, so the claim is auditable without an AWS account. The generated Coverage block carries the exact documented/observed split.

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) and repo: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:

  1. Grammar — is the subject well-formed for its issuer (classic AND immutable GitHub formats)?
  2. Match semantics — does it satisfy the consumer's condition (AWS StringLike/StringEquals globbing, Azure FIC exact match + flexible expressions, GCP CEL)?
  3. Judgment — is the condition safe? Graded findings for the patterns that matter: pull_request subjects, unprotected refs, wildcarded repos/orgs, missing aud pinning. The graded patterns are a stable, citable vocabulary — see docs/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 - 133 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 12 15 5 10 8 12 62
gitlab 5 5 6 6 5 6 33
bitbucket 1 2 3 - - - 6
circleci 4 - 3 - - 6 13
terraform-cloud 3 3 1 - 6 6 19
Total 25 25 18 16 19 30 133

Suites:

  • bitbucket-aws 0.1.0 - 6 vectors
  • circleci-aws 0.1.0 - 7 vectors
  • circleci-gcp 0.1.0 - 6 vectors
  • github-aws 0.4.2 - 32 vectors
  • github-azure-flexible 0.1.0 - 8 vectors
  • github-azure 0.1.0 - 10 vectors
  • github-gcp 0.1.0 - 12 vectors
  • gitlab-aws 0.2.0 - 16 vectors
  • gitlab-azure-flexible 0.1.0 - 5 vectors
  • gitlab-azure 0.1.0 - 6 vectors
  • gitlab-gcp 0.1.0 - 6 vectors
  • terraform-aws 0.1.0 - 7 vectors
  • terraform-azure-flexible 0.1.0 - 6 vectors
  • terraform-gcp 0.1.0 - 6 vectors

Judgments: 30 safe - 45 caution - 36 dangerous - 22 ungraded (mechanical no-match / contrast vectors carry no safety grade).

Provenance: 122 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. See vectors/LICENSE.
  • Everything else (the reference matcher, schema, docs) — Apache-2.0. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

subvectors-0.4.2.tar.gz (131.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

subvectors-0.4.2-py3-none-any.whl (85.5 kB view details)

Uploaded Python 3

File details

Details for the file subvectors-0.4.2.tar.gz.

File metadata

  • Download URL: subvectors-0.4.2.tar.gz
  • Upload date:
  • Size: 131.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for subvectors-0.4.2.tar.gz
Algorithm Hash digest
SHA256 c8c41624f7bb8a5ecbd31a4c6a047a6b40a93396a23f2daa095dcf4ad70dd02b
MD5 6647b41ab3f63a4215366a601f80e722
BLAKE2b-256 374008f87b3c873e234cbc89c7fe3edc0ce56490b4cd8d146aae17871ae9f3d7

See more details on using hashes here.

Provenance

The following attestation bundles were made for subvectors-0.4.2.tar.gz:

Publisher: release.yml on Dashtid/subvectors

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file subvectors-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: subvectors-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 85.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for subvectors-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 796a345c6744868a90ba2c1745d62c04a362e89f538fa5926deac89fa836fcc6
MD5 6b9301d317ab57b9b82d9ddf29365d6f
BLAKE2b-256 a7cb214027340c9312af13340afc5a54d63d8137bfa7f66c7657b7c78e4343f5

See more details on using hashes here.

Provenance

The following attestation bundles were made for subvectors-0.4.2-py3-none-any.whl:

Publisher: release.yml on Dashtid/subvectors

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.4.2 This release

2 files

0.4.0

2 files

0.3.0

2 files

0.2.1

2 files

0.2.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page