Skip to content

[BUG] DockerHub:Registry has an unclear, partly hidden meaning and is barely validated #268

Open

Description

@LarsLaskowski

Summary

The README describes DockerUpdateGuard:DockerHub:Registry as the "Registry host handled by the Docker Hub integration", but the key does not decide which registries the Docker Hub client serves: that host list is hard-coded. In reality it does two unrelated things: it gates the Docker Hub account listing (and the current-user lookup), and, when set to an absolute URL whose host is not a Docker Hub host, it silently replaces the Docker Hub web API base address given by ApiBaseUrl. Validation only checks that it is non-empty. Operators cannot predict the effect of the key, and a value meant as "my registry" quietly breaks discovery or redirects authenticated Docker Hub API traffic.

Area

  • docs/areas/registries.md (added in [Docs] Describe the current behavior by area #263): Choice of client per registry, Docker Hub account discovery, Configuration reload, Limits and errors
  • Security area: None directly. Note that the redirect of the API base address also redirects the bearer token derived from DockerHub:Pat (Secrets in configuration), which is one more reason to remove that path.

Current behavior

  • Default and description: src/DockerUpdateGuard/Configuration/DockerHubOptions.cs:10-13 (Registry = "docker.io", summary "Registry host name"); README.md:108 ("Registry host handled by the Docker Hub integration", required).
  • Which registries the Docker Hub client serves is fixed in IsSupportedDockerHubHost (src/DockerUpdateGuard/DockerHub/DockerHubClient.cs:354-364: docker.io, index.docker.io, registry-1.docker.io, hub.docker.com, docker.com, www.docker.com) and evaluated against the image reference's registry (CanHandle :1334-1337, GetTagAsync :1189, …). DockerHub:Registry plays no role there.
  • Gate for account discovery: GetCurrentUserAsync (:1020-1025) and GetRepositoriesAsync (:1068-1073) answer unsupported when DockerHub:Registry is not a Docker Hub host; DockerHubAccountImageDiscoveryService (src/DockerUpdateGuard/Images/DockerHubAccountImageDiscoveryService.cs:268-275) then ends the run without changes. Discovered repositories are always stored under registry docker.io (ParseRepository, DockerHubClient.cs:321-322), whatever Registry says.
  • Hidden override of the API base address: DockerHubClient.GetBaseUri (:127-144):
    if (Uri.TryCreate(options.Registry, UriKind.Absolute, out var absoluteUri))
    {
        if (IsSupportedDockerHubHost(absoluteUri.Host)) { return apiBaseUri; }
        return absoluteUri;   // Registry replaces ApiBaseUrl
    }
    used once at startup for HttpClient.BaseAddress (src/DockerUpdateGuard/ServiceCollectionExtensions.cs:73-79).
  • Validation: src/DockerUpdateGuard/Configuration/DockerUpdateGuardOptionsValidator.cs:69-72 only requires a non-empty value.

Failure scenarios

  1. DockerHub:Registry = ghcr.io (an operator thinking "the registry I use"): account discovery is silently unsupported forever; the only trace is a log entry.
  2. DockerHub:Registry = https://registry.example.internal (absolute URL): account discovery is unsupported and every Docker Hub web API call (tags, repositories, login with the PAT) goes to https://registry.example.internal/ instead of ApiBaseUrl, without any warning.

Expected behavior

Decision (product manager): DockerHub:Registry has one clear meaning. It only identifies the registry host for Docker Hub account discovery. The Docker Hub web API base address is set only through DockerHub:ApiBaseUrl.

  • DockerHub:Registry never changes any request URL; GetBaseUri returns ApiBaseUrl regardless of Registry.
  • Validation at startup: Registry must be a host name (not a URL, no scheme, path or port) that is one of the Docker Hub hosts the client supports, compared case-insensitively. Any other value fails startup with a message naming the key and the allowed values (for example "'DockerUpdateGuard:DockerHub:Registry' must be a Docker Hub registry host (docker.io, index.docker.io, registry-1.docker.io, hub.docker.com)").
  • The value is trimmed; the default stays docker.io.
  • The account listing's unsupported branch for a non-Docker-Hub Registry stays as a defensive check for a reloaded configuration, but is no longer a normal, silent operating state.
  • README, the options XML doc and the area document describe the key with this meaning.

Acceptance criteria

  • DockerHubClient.GetBaseUri returns ApiBaseUrl for every Registry value, including an absolute non-Docker-Hub URL.
  • DockerUpdateGuardOptionsValidator rejects Registry values that are absolute URLs, contain a path or port, or are not Docker Hub hosts; accepts docker.io (any case, surrounding whitespace) and the other Docker Hub hosts.
  • Unit tests (MSTest, names per docs/UNIT_TESTS.md), for example DockerHubClientGetBaseUriAbsoluteNonDockerHubRegistryReturnsApiBaseUrl, DockerUpdateGuardOptionsValidatorDockerHubRegistryNonDockerHubHostFailsValidation, DockerUpdateGuardOptionsValidatorDockerHubRegistryAbsoluteUrlFailsValidation, DockerUpdateGuardOptionsValidatorDockerHubRegistryDockerHubHostPassesValidation; existing DockerHubClientGetBaseUri… tests (src/Tests/DockerUpdateGuard.Tests/DockerHubClientTests.cs:73, :91) are adjusted to the new rule.
  • README configuration reference (README.md:108-109): Registry described as "Docker Hub registry host used for account discovery (a Docker Hub host such as docker.io)"; ApiBaseUrl as the only way to change the Docker Hub web API address.
  • DockerHubOptions XML docs updated accordingly.
  • Format, build, tests, Analyzer gate and Coverage gate from .squad/stack.md pass.
  • The area document docs/areas/registries.md describes the new behavior and its Known gaps entry for this issue is removed.

Implementation notes

  • Affected: DockerHubClient.GetBaseUri (simplify to new Uri(options.ApiBaseUrl) or remove it and use ApiBaseUrl directly in ServiceCollectionExtensions), DockerUpdateGuardOptionsValidator.ValidateDockerHubOptions, DockerHubOptions, README.md, possibly docs/ARCHITECTURE.md (Configuration model) and INSTALL.md if they mention the key.
  • Reuse the Docker Hub host list for validation instead of duplicating it: expose a static check (for example make DockerHubClient.SupportsRegistry the single source, or move the list to a small static helper used by both the client and the validator). docker.com / www.docker.com are in the client's list today but are not registries. Recommendation: accept the full existing list to avoid breaking existing configurations.
  • Breaking change for operators who used an absolute URL in Registry to point the web API somewhere else: startup now fails with a clear message, and they move the URL to ApiBaseUrl. Mention this in the PR description.
  • src/Tests/DockerUpdateGuard.Tests/DockerUpdateGuardOptionsValidatorTests.cs:403-407 already asserts that the development configuration sample uses a supported Docker Hub registry; keep it green.
  • appsettings.json keeps "Registry": "docker.io"; no migration.
  • Must not change: which registries the Docker Hub client serves (the fixed host list, evaluated on the image reference); discovered repositories stay stored under docker.io; ApiBaseUrl stays fixed at startup.

Related


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions