Skip to content

feat(vex): migrate OCI VEX discovery off openvex/discovery (support Cosign v3 / OCI 1.1 referrers) #10750

@DmitriyLewen

Description

@DmitriyLewen

Description

This proposes migrating off the openvex/discovery library and implementing OCI VEX discovery natively in Trivy.

Trivy discovers VEX attestations attached to OCI artifacts (--vex oci) through openvex/discovery. That library only looks for legacy .att-tag attestations (Cosign v2 default) and is effectively unmaintained. Starting with Cosign v3, cosign attest defaults to --new-bundle-format=true and stores the attestation as an OCI 1.1 referrer (the new Sigstore bundle format). As a result, attestations produced by a default cosign attest on Cosign v3 are not discovered and users get No VEX attestations found.

Reported in #10656 (Cosign v3.0.6, cosign attest --type openvex).

The migration moves discovery in-tree, adds support for the new bundle format, and keeps backward compatibility with the legacy .att path.

Root Cause

  1. No Cosign v3 / new bundle format support. openvex/discovery (the version we depend on) only resolves legacy .att-tag attestations. The OCI 1.1 referrer that Cosign v3 produces by default is not picked up. Confirmed in oras attached VEX attestation not found by Trivy #10656.
  2. openvex/discovery is effectively unmaintained. Over the last year the repo has merged only Dependabot/CI bumps — no features or bug fixes:
    • openvex/discovery#112 — bump cosign/v2 → cosign/v3@v3.0.3 — open since 2025-12-11, currently in merge conflict, no maintainer response (a "Are there plans to merge this?" ping on 2026-04-16 went unanswered).
    • openvex/discovery#127 — bump go-vex to v0.2.8 — open since 2026-04-16, 0 comments.
    • openvex/discovery#82 — request for registry auth options — open since 2025-05-26, 0 comments.
  3. Library logs are invisible. openvex/discovery exposes its own *slog.Logger via Agent.Options.Logger (default: JSON handler to stderr at INFO). Trivy does not reconfigure it (see pkg/vex/oci.go:40), so the library's DebugContext lines never surface through Trivy's --debug, and users can't self-diagnose.
  4. Registry auth not reused. The discovery path falls back to the default go-containerregistry keychain instead of Trivy's native registry auth. Confirmed in bug(vex): Trivy doesn't support auth for OCI images from private registries #8916: VEX discovery against a private registry fails with UNAUTHORIZED even though the image itself scans fine. The workaround — trivy registry login / docker login to populate the Docker config that the default keychain reads — confirms the mechanism: credentials passed via Trivy's own flags/env (TRIVY_USERNAME / TRIVY_PASSWORD) never reach discovery. Tracked upstream as Is it planned to add auth options? openvex/discovery#82 (auth options for ProbePurl).

Proposed Solution

Implement OCI VEX discovery natively in Trivy (replace the openvex/discovery dependency in pkg/vex/oci.go) and support both attestation layouts:

  • Legacy .att tag (Cosign v2 default) — keep working for the existing majority of users.
  • OCI 1.1 referrer + Sigstore bundle (Cosign v3 default) — list referrers, filter by artifact type, decode the bundle → DSSE → in-toto Statement → OpenVEX predicate.

Benefits of going in-tree:

  • Wire Trivy's own *slog.Logger into the discovery code (fixes (3)).
  • Reuse Trivy's existing registry auth / transport for private registries (addresses (4)).
  • Control over the format support timeline instead of waiting on an unmaintained upstream.

openvex/discovery is a thin wrapper over a couple of Cosign helpers (~50 lines of application logic), so moving it in-tree does not mean re-implementing heavy infrastructure.

Note: discovery is scoped to attestations attached via cosign attest (legacy .att or the new bundle referrer). Raw oras attach of an arbitrary in-toto blob is out of scope, as today.

Dependency Impact

Current state (go.mod + go mod why -m):

  • github.com/openvex/discoverydirect.
  • github.com/sigstore/cosign/v2 v2.6.2indirect, pulled only via openvex/discovery → probers/oci → cosign/v2/pkg/cosign.
  • github.com/sigstore/sigstore-go v1.1.4indirect, pulled via the same chain (cosign/v2 → sigstore-go/pkg/bundle).
  • github.com/google/go-containerregistry v0.21.6 — direct.
  • github.com/openvex/go-vex v0.2.7 — direct.
  • ⚠️ There is no cosign/v3 in the module graph today (the upstream v2→v3 bump is the still-open PR False positives on Alpine (GNU vs Busybox) #112).

After migration:

  • Drop github.com/openvex/discovery.
  • sigstore-go becomes a direct dependency (needed to decode the new bundle).
  • go-containerregistry (referrers/manifest/blob/tag fetch) and go-vex (parse the Statement into a VEX predicate) stay direct.
  • The Cosign dependency depends on the decision in Open Question 1.

Open Questions / Decisions Needed

  1. How to handle the Cosign dependency for the implementation?
    • A. Depend on cosign/v3 as a thin wrapper (uses cosign.FetchAttestationsForReference, ociremote.Referrers, ociremote.Bundle). Least code; effectively the swap that upstream PR False positives on Alpine (GNU vs Busybox) #112 attempts (cosign/v2 → cosign/v3).
    • B (I'm leaning towards this option.). Drop Cosign entirely and implement on go-containerregistry (referrers + tag/manifest/blob fetch) + sigstore-go (bundle decode) + go-vex (parse). More code, fewer/lighter deps, full control over auth and logging.
  2. New package name: reshape pkg/vex/oci.go into pkg/vex/oci/, or introduce pkg/vex/discovery/?

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    scan/vulnerabilityIssues relating to vulnerability scanningtarget/container-imageIssues relating to container image scanning

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions