Skip to content

[Scanning] Follow pagination links only on the original origin - #303

Merged
LarsLaskowski merged 20 commits into
mainfrom
claude/tender-dijkstra-9uouif
Oct 24, 2026
Merged

LarsLaskowski merged 20 commits into
mainfrom
claude/tender-dijkstra-9uouif

Conversation

@LarsLaskowski

@LarsLaskowski LarsLaskowski commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Pull Request

📖 Description

Docker Hub, the generic OCI tag listing and Docker Scout followed the "next page" link exactly as the remote side returned it, so a tampered response could send the Docker Hub bearer token to any host. A new shared PaginationLinkGuard now resolves each link against the current request (relative links included) and follows it only when scheme, host and port match the allowed origin. Any other link sends no request, logs a warning without credentials and makes the call return failed, so a truncated list is never reported as succeeded. The change also fixes root-relative Link targets on OCI registries such as ghcr.io, which failed on Linux. The rule is recorded in decision 0004 and described in the registries, vulnerabilities and telemetry area documents, ARCHITECTURE.md and the README.md configuration rows.

🎫 Issues

Closes #264

💬 Reviewer Notes

Start with src/DockerUpdateGuard/Infrastructure/PaginationLinkGuard.cs; the three call sites send exactly the Uri the guard returned. Docker Scout's real next link form could not be observed without credentials; the design fails closed, which decision 0004 records as residual risk. A mirror or proxy set as DockerHub:ApiBaseUrl must rewrite next-page links to its own origin.

📑 Test Plan

New PaginationLinkGuardTests pin the link forms Uri accepts (case, default port, scheme downgrade, relative, UNC and backslash forms, punycode). The Docker Hub, Docker Scout and OCI client tests cover cross-origin rejection with no request to the foreign host, no Authorization header leaving the configured origin, and no secrets in messages or logs. All tests pass, and changed-line coverage is 100 %.

✅ Checklist

  • I have added or updated Unit Tests for the change.
  • I have tested my changes.
  • I have run Format and Build from .squad/stack.md, and the Analyzer gate reports no diagnostic in a changed file.
  • The Coverage gate in .squad/stack.md passes (line coverage on new or changed production code and overall).
  • I have updated the project documentation (README.md, ARCHITECTURE.md) to reflect my changes.
  • I have read the CONTRIBUTING documentation and followed the project's code style guidelines.
  • New dependencies, if any, were added the way Dependencies in .squad/stack.md prescribes.
  • The C# rules in csharp.instructions.md are followed.

⏭ Next Steps

claude added 20 commits October 24, 2026 09:47
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EFH7zwwFh7RNHRvHugDK6
@sonarqubecloud

Copy link
Copy Markdown

@LarsLaskowski
LarsLaskowski marked this pull request as ready for review October 24, 2026 10:46
@LarsLaskowski
LarsLaskowski merged commit 4ba3473 into main Oct 24, 2026
4 checks passed
@LarsLaskowski
LarsLaskowski deleted the claude/tender-dijkstra-9uouif branch October 24, 2026 10:47
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.

[BUG] Pagination links are followed to any host, sending the Docker Hub bearer token along

2 participants