Skip to content

Vex purl encoding and grype match#17

Merged
rb3ckers merged 2 commits into
mainfrom
STAC-25019-vex-purl-encoding-and-grype-match
Jun 18, 2026
Merged

Vex purl encoding and grype match#17
rb3ckers merged 2 commits into
mainfrom
STAC-25019-vex-purl-encoding-and-grype-match

Conversation

@rb3ckers

Copy link
Copy Markdown
Contributor

Relaxing the requirement on the PURL repository_url encoding in the reports. They can simply use /, but the index.json must use the encoded version.

To be compatible with grype scans that we use internally, we also need to add an entry without the repository_url.

rb3ckers added 2 commits June 18, 2026 16:56
Two changes that together get VEX-based suppression working in both
Trivy and Grype against the docker-images CI scans:

1) Add a bare `pkg:oci/<name>` product entry per OCI statement.
   Grype's scan-time image PURL is the bare form -- it does not carry
   a `repository_url` qualifier -- so qualified-only product entries
   are invisible to Grype's statement matcher even when the VEX file
   is fed in directly via `--vex`. The qualified entries are kept and
   continue to scope statements per registry for Trivy's index lookup;
   the bare entry is what lets Grype apply the same statement.

2) Drop percent-encoding in VEX statement product/subcomponent @ids.
   PURL consumers (Trivy's statement matcher, Grype, vexctl) normalise
   qualifier values before comparing, so `repository_url=quay.io/...`
   and `repository_url=quay.io%2F...` are equivalent at match time.
   The unencoded form is the convention in other public vexhubs and
   is much easier to read and diff. The `index.json` lookup is the
   one place that does byte-level string match, so `build_index.py`
   continues to emit the canonical percent-encoded form there.

tools/build_index.py:
- _parse_purl now stores qualifier values decoded and accepts either
  encoded or unencoded input. The strict `_ensure_percent_encoded`
  rejection is removed -- canonicalisation happens on emit, not on
  ingest.
- index_id_for_purl no longer hard-requires `repository_url` for OCI
  PURLs. Bare `pkg:oci/<name>` products are valid and produce a bare
  index entry; qualified products produce a percent-encoded entry as
  before. Empty `repository_url` is still rejected.

pkg/oci/opentelemetry-target-allocator/scan.openvex.json,
pkg/oci/stackstate-k8s-agent/scan.openvex.json:
- Decode `%2F` in repository_url qualifier values.
- Decode `%2B` in subcomponent versions (e.g. v28.5.2+incompatible).
- Add a bare `pkg:oci/<name>` product entry per statement, with the
  same subcomponents as the qualified entries.

index.json: regenerated. Each OCI image now has three entries -- one
per registry-scoped product plus one bare -- alongside the existing
maven entry.
Grype reports CVE-2026-34040 and CVE-2026-33997 a second time under
their Go vulnerability database IDs (GO-2026-4887 and GO-2026-4883
respectively), because grype pulls from both NVD/GHSA and the Go
vulndb and only partially deduplicates findings whose primary ID
differs across databases. Statement matching is keyed on
`vulnerability.name` and `aliases[]`; without the GO-* aliases the
two Go-DB findings stayed unmatched even though their CVE-* twins
were filtered.

Add the Go IDs as aliases on the corresponding statements in
opentelemetry-target-allocator. The other docker/moby CVEs in this
file (CVE-2026-41567, CVE-2026-42306, CVE-2026-41568) do not have
Go vulndb entries yet, so no further aliasing is required.
@rb3ckers rb3ckers requested a review from a team as a code owner June 18, 2026 15:03
@rb3ckers rb3ckers merged commit 839bb24 into main Jun 18, 2026
1 check passed
@rb3ckers rb3ckers deleted the STAC-25019-vex-purl-encoding-and-grype-match branch June 18, 2026 16:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants