Skip to content

Handle pagination of the Docker Scout vulnerability API #169

Description

@LarsLaskowski

Builds on the vulnerability rework in PR #164. Read CLAUDE.md and .github/instructions/csharp.instructions.md first — the code-style rules there (regions, XML docs on everything, == false instead of !, aligned wrapped parameters, MSTest assertion messages, reihitsu-format ./ before building) are binding.

Background

src/DockerUpdateGuard/Vulnerabilities/DockerScoutVulnerabilityProvider.cs fetches
GET {DockerScoutBaseUrl}/v1/repositories/{ns}/{repo}/tags/{tag}/vulnerabilities
and deserializes the response into Vulnerabilities/Data/ScoutVulnerabilityResponse.cs, which only reads the vulnerabilities array. If the API paginates (common for images with hundreds of CVEs), everything after the first page is silently dropped — the stored finding count would be wrong without any error.

Task

  1. Verify the actual response shape first. Call the endpoint against a real repository with many CVEs (any public image with a large CVE count) using the configured Docker Hub credentials flow, and inspect the raw JSON. Document what you find in the PR description. Expected possibilities:
    • a cursor/link field in the body (e.g. next, page, has_more),
    • RFC 5988 Link headers,
    • or no pagination at all (everything in one response).
  2. If paginated: extend ScoutVulnerabilityResponse with the pagination field(s) ([JsonPropertyName]-annotated, internal sealed record, one type per file) and loop in FetchVulnerabilitiesAsync until the last page, aggregating advisories into one list before MapAdvisories. Guard against infinite loops with a hard page cap (e.g. 50 pages) — if the cap is hit, log a warning (add a new LoggerMessage in DockerScoutVulnerabilityProviderLogging.cs; next free EventId after 3417) and return what was collected as Succeeded (partial data is better than none, but it must be visible in the logs).
  3. If not paginated: close this issue with a comment containing the verified evidence (raw response structure, image used, CVE count) — do not add speculative code.

Testing (when pagination exists)

  • Extend src/Tests/DockerUpdateGuard.Tests/DockerScoutVulnerabilityProviderTests.cs. The tests use SequenceHttpMessageHandler (Tests/Helper/), which queues multiple responses per URL — queue page 1 (with a next-page marker) and page 2 (without), assert the merged advisory count and that the observed requests hit the expected page URLs.
  • Add a test for the page-cap warning path (queue responses that always advertise a next page, assert the cap stops the loop and the warning EventId is logged via TestLogger).
  • Follow {Class}{Scenario}{ExpectedResult} naming with assertion messages.

Acceptance criteria

  • Response shape verified against the live API and documented.
  • All pages are fetched and merged (or the issue is closed with evidence that pagination does not exist).
  • Page cap + warning log prevent infinite loops.
  • Token caching and the single 401 retry still work across pages (covered by tests).
  • reihitsu-format ./ clean, dotnet build DockerUpdateGuard.slnx -c Release clean, all tests pass.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions