Skip to content

Referrers pagination is not followed; attestations beyond the first page are never fetched #196

Description

@bdehamer

Summary

When fetching the attestations for an image, the provider issues a single request to the registry's Referrers API and uses only that one response. If the registry returns the referrers list across multiple pages, the provider silently ignores every page after the first. As a result, an attestation that would satisfy a policy may never be fetched, producing a false negative (the image is treated as unsigned or as missing the required attestation even though a valid one exists).

Where

DoBundleFromName in pkg/fetcher/bundle.go retrieves referrers via go-containerregistry's remote.Referrers:

referrers, err := remote.Referrers(digest, opts...)
...
refManifest, err := referrers.IndexManifest()
...
descriptors := selectBundleDescriptors(refManifest.Manifests, PredicateType)

remote.Referrers (go-containerregistry v0.21.7) performs a single GET /v2/<name>/referrers/<digest> and reads exactly one image-index body. It does not inspect or follow the Link: <...>; rel="next" response header, so refManifest.Manifests only ever contains the first page of referrers.

Notably, the same library does implement Link-header pagination for tag listing and catalog listing (getNextPageURL / getNextPageURLForRegistry), but that pagination logic was never wired into the referrers code path.

Impact

  • False negatives. If the specific attestation required by policy is on a page after the first, it is never downloaded and never verified. Depending on the code path this surfaces as image_unsigned (when the first page contains no Sigstore bundles at all) or as a policy failure (a matching bundle exists but was never seen).
  • Nondeterministic and silent. Behavior depends on how many referrers an image has and on page ordering. Images with few referrers work fine; images with many can fail. No error is raised — the provider simply operates on a truncated list, which makes this hard to notice and hard to debug.
  • Registry-dependent, but not vendor-specific. Any registry that paginates the referrers response is affected. Azure Container Registry (ACR) is a notable registry that paginates and would trigger this today.

Is referrers pagination OCI-standard or registry-specific?

It is part of the OCI Distribution Specification — not a registry-specific extension. The Listing Referrers section (added in distribution-spec 1.1) states:

A Link header MUST be included in the response when the descriptor list cannot be returned in a single manifest. Each response is an image index with different descriptors in the manifests field. The Link header MUST be set according to RFC 5988 with the Relation Type rel="next".

So a registry that splits referrers across pages (like ACR) is behaving as a conformant OCI implementation, and a spec-compliant client must follow the rel="next" link until it is exhausted, accumulating the manifests from every page. Our provider currently does not, which is the bug.

Suggested direction

Follow the Link: ...; rel="next" header for the referrers endpoint and merge the manifests from all pages before selecting bundle descriptors. Because upstream go-containerregistry's remote.Referrers does not paginate, this likely means either:

  • driving the referrers request(s) directly and paginating over the Link header ourselves (validating that each next-page URL stays on the same registry, as the library already does for tags/catalog to avoid SSRF), or
  • upstreaming referrers pagination into go-containerregistry.

Regression coverage should include a registry that returns a paginated referrers response and assert that a bundle on a non-first page is fetched and verified.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions