Skip to content

Use HTTPS for Docker catalog assets#518

Merged
kgprs merged 1 commit into
mainfrom
codex/use-https-catalog-assets
Jun 24, 2026
Merged

Use HTTPS for Docker catalog assets#518
kgprs merged 1 commit into
mainfrom
codex/use-https-catalog-assets

Conversation

@kgprs

@kgprs kgprs commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

  • upgrade legacy http://desktop.docker.com URLs to HTTPS before guarded direct fetches
  • normalize Docker catalog README URLs when creating catalog-next snapshots from legacy/profile data
  • update catalog examples/docs and tests to use HTTPS or deterministic conversion checks

Root cause

catalog-next server inspect fetches snapshot.server.readme before returning JSON. The v0.43.0 switch to remoteurl.NewDirectHTTPClient correctly rejects plain HTTP, but Docker catalog snapshots still contain README URLs such as http://desktop.docker.com/mcp/catalog/v3/readme/..., so Pinata overview/tools tabs fail on inspect.

Validation

  • go test ./pkg/remoteurl ./pkg/fetch ./pkg/catalog ./pkg/catalog_next
  • go test ./cmd/docker-mcp/commands -run 'Test.*Mcpregistry|Test.*Catalog|Test.*Gateway'

@kgprs kgprs changed the title [codex] Use HTTPS for Docker catalog assets Use HTTPS for Docker catalog assets Jun 23, 2026
@kgprs kgprs marked this pull request as ready for review June 23, 2026 05:44
@kgprs kgprs requested a review from a team as a code owner June 23, 2026 05:44

@saucow saucow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — HTTPS for Docker catalog assets

LGTM, and it's a strictly-safer change. UpgradeKnownHTTPURLToHTTPS is correctly scoped: httphttps only for the exact host desktop.docker.com on port `` or 80, every other URL passes through unchanged, and it doesn't touch `allowInsecure` — so the SSRF denylist, IP pinning, and redirect re-validation in `NewDirectHTTPClient` stay fully active on the upgraded request. A malicious catalog supplying `http://attacker/` stays `http` and is still rejected with "remote URL must use https". All four call sites upgrade before the guarded fetch.

Recommendation: approve-with-nits — test-completeness only, inline.

One follow-up that's outside this PR's diff: cmd/docker-mcp/server/inspect.go (the other server inspect path) still fetches ToolsURL/ReadmeURL via a raw &http.Client{Transport: desktop.ProxyTransport()} (:111, :145), not through remoteurl. No regression — that path never enforced https — but the "HTTPS for catalog assets" goal is only partially realized until that sibling fetch is migrated too. Worth a tracking issue.

// UpgradeKnownHTTPURLToHTTPS rewrites legacy public HTTP URLs that are known to
// serve the same content over HTTPS. It deliberately does not relax validation
// for arbitrary HTTP URLs.
func UpgradeKnownHTTPURLToHTTPS(rawURL string) string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely scoped — exact-host match (EqualFold) + port `` / 80 only, and not touching `allowInsecure` keeps the SSRF/IP-pinning guard live on the upgraded request. Host-confusion vectors all fail closed: `evil.desktop.docker.com` and `desktop.docker.com.evil.example` don't match the host so stay `http` (then rejected downstream), and `http://desktop.docker.com@attacker.example/` keeps `attacker.example` as the host so also stays `http`. No change requested here — just confirming the boundary is right.

Comment on lines +133 to +136
err := inspectServer(ctx, dao, catalogObj.Ref, "my-server", workingset.OutputFormatJSON, func(_ context.Context, url string) ([]byte, error) {
assert.Equal(t, readmeURL, url)
return []byte(readmeContent), nil
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestInspectServerWithReadme now injects a fake fetchReadme that only asserts URL equality, so it no longer exercises the real fetch.Untrusted path (and therefore the new upgrade + the https/SSRF guard) on the inspect flow this PR fixes. And inspectServer fetches the persisted ReadmeURL as-is (server.go:62-65) — normalizeCatalogServerURLs only runs at create time — so for any catalog persisted before this PR, fetch.Untrusted's internal upgrade is the only thing rescuing an http://desktop.docker.com readme at inspect time.

The upgrade itself is well covered at the unit seam (remoteurl_test.go, fetch_test.go::TestUntrustedRejectsUnknownHTTPURL, create_test.go), so this is minor — but one end-to-end assertion through the exported InspectServer (or fetch.Untrusted directly) with an http://desktop.docker.com readme would restore a regression tripwire for the exact inspect scenario the PR is fixing.

@saucow saucow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — this is strictly safer than the prior state: the httphttps rewrite is host-pinned to desktop.docker.com and doesn't touch allowInsecure, so the SSRF/IP-pinning hardening stays fully active. The items in my earlier review are test-completeness nits plus a follow-up for the sibling server inspect fetch path — none are blocking. Nice fix.

@kgprs kgprs merged commit 7aa5879 into main Jun 24, 2026
8 checks passed
@kgprs kgprs deleted the codex/use-https-catalog-assets branch June 24, 2026 16:13
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.

2 participants