Check D9 · execution family · automatic · one of the 11 Docker Desktop checks
The question this check asks: where do the links on this extension's listing go, and what scheme do they use? A Docker Desktop extension is a Docker Hub image, and its listing lives in the image's labels. Two of them carry every outbound link: com.docker.extension.publisher-url, which Docker requires, and com.docker.extension.additional-urls, optional, a JSON list of title-and-url pairs. D9 reads both and asks two things of each URL — is the scheme http or https, and is the host the publisher's own.
Docker's labels page puts it plainly: "Extensions use image labels to provide additional information such as a title, description, screenshots, and more." The links are part of that listing, and Docker Desktop renders them. Before 12 September 2024 it also acted on them without checking. The 4.34.2 release note reads, verbatim: "Fixed CVE-2024-8696 which allows RCE via crafted extension publisher-url/additional-urls which could be abused by a malicious extension." Docker's advisory the next day credits Cure53, rates the issue High (CNA CVSS 4.0 8.9; NVD rates it 9.8 Critical), and adds: "No existing extensions exploiting the vulnerabilities were found in the Extensions Marketplace." That last sentence is the whole of the incident record — there is none — and this page does not pretend otherwise. What the CVE established is that the field class is real: a label a publisher writes, rendered by the app on the developer's machine, was a remote-code-execution path.
The check still exists two years after the fix for a plain reason. RepoGates cannot see which Docker Desktop you run, and a listing that carries a javascript:, file: or data: URL has no honest reason to. The validation that stood in for Docker's manual review after January 2024 checked presence, not content — in Docker's words, "The validation checks if the extension's Dockerfile specifies all the required labels and if the metadata file is valid against the JSON schema file" — and since Docker paused new submissions on 16 June 2026 the labels on a sideloaded image have been reviewed by nobody at all. D9 is the check that reads them.
The labels come from the image's config blob, reached through the registry without a pull: a pull token, the OCI index, the linux/amd64 manifest, then the config. D9 takes the publisher URL and every entry of the additional-urls list and parses each. A URL whose scheme is present and is not http or https produces dde-url-scheme:<scheme> — HIGH, 15 points, one row per offending URL, with the report line "a listing URL uses the scheme: scheme — Docker Desktop before 4.34.2 opened these without checking; the RCE class". A URL whose host is not the publisher URL's own host or a subdomain of it, and is not github.com, docker.com, hub.docker.com or docs.docker.com, produces dde-url-external:<host> — INFO, no points, the first three distinct hosts only. The scoring table's evidence notes, in order:
"D9; a listing URL with a non-http(s) scheme — Docker Desktop before 4.34.2 opened publisher-url/additional-urls without checking (the RCE class)"
"D9; the listing links to a site other than the publisher's — informational"
The verdict engine is the one behind all 11 checks: any CRITICAL finding is a BLOCK, any HIGH or MEDIUM finding is a REVIEW, and a report with nothing above INFO is a PASS. The score is separate — 100 minus every fired penalty, floored at zero. A listing with one bad scheme and nothing else fired scores 85 with a REVIEW verdict; on the personal and balanced profiles a HIGH finding warns, and on the strict profile it blocks. On the preflight that is an ask before docker extension install runs. D9 feeds two dimensions, Open in an editor and Install and run, beside D6 and D7; a dimension takes the worst of its checks and reads PASS only when every one of them ran. When the labels could not be read at all — a Hub repository whose image the registry will not serve — D9 is listed as not checked with the reason "image labels unavailable", and that skip withholds PASS from both dimensions rather than passing them on silence.
The HIGH half has none to report, because it has never fired on the marketplace: in the 18 September 2026 measurement of all 50 listed extensions, run locally through the same code as /v1/dde/score, dde-url-scheme fired on none of the 50. The INFO half fired on 16 of the 50, and that is exactly why it is INFO: the ordinary reasons a listing links away from the publisher's own host — documentation on another domain, a community site, a company page beside the project page named as the publisher URL — are not evidence of anything, and a weight on them would have turned a third of the marketplace into REVIEW for nothing. None of the 16 notes moved a verdict; the run was 50 PASS, 0 REVIEW, 0 BLOCK.
Two edges of the logic, stated rather than hidden: a link to github.com or docker.com is never counted as external, however unrelated the repository; and a bare hostname with no scheme parses to neither a scheme nor a host, so it is neither flagged nor counted.
Three other listing fields have their own CVE history and D9 does not read them. The detailed-description and changelog labels, which Docker's labels page allows as "plain text or HTML", were the field behind two fixes: the 4.12.0 note of 1 September 2022, "Fix RCE via extension description/changelog which could be abused by a malicious extension", later assigned CVE-2023-0625, and the 4.34.2 note, "Fixed CVE-2024-8695 which allows RCE via crafted extension description/changelog which could be abused by a malicious extension" — the same field two years apart. The icon URL was behind CVE-2023-5166, fixed in 4.23.0 on 11 September 2023, "which allows Access Token theft via a crafted extension icon URL". None of the three is assessed on this tier. Nor does D9 follow a link: where an http(s) URL leads, whether the destination is what its title says, and whether the host has since changed hands are all outside it. It does not know which Docker Desktop you run, so it cannot tell you whether a bad scheme would execute or merely sit there. And there is no signature check on this platform: none of the 12 extensions sampled from the registry carries a cosign signature, so a signed label set is not something the marketplace offers to verify.
RepoGates assesses a Docker Desktop extension on request — through the API, the MCP server (platform="dde") and the preflight for docker extension install. It does not see an extension installed from Docker Desktop's own Extensions Marketplace tab, which is the app's own fetch; the board inside Docker Desktop is not built, and cannot be — its marketplace is a native window no browser extension sees. The image is never pulled and its code is not read: a PASS means the provenance and the declared install surface are clean, never that the backend or the host binaries were inspected.
The labels are in the registry, and reading them takes a few requests and no pull — the same path RepoGates walks. First the Hub's newest tag (not every extension has a latest tag; Tailscale's does not), then a pull token, then the manifest, then the config blob. For tailscale/docker-extension:
# 1. the newest tag on the Hub — 1.78.1 for Tailscale when this was written; there is no "latest"
TAG=$(curl -s 'https://hub.docker.com/v2/repositories/tailscale/docker-extension/tags?page_size=1&ordering=last_updated' | jq -r '.results[0].name')
# 2. a pull token (anonymous; add -u user:token for a Hub account)
TOKEN=$(curl -s 'https://auth.docker.io/token?service=registry.docker.io&scope=repository:tailscale/docker-extension:pull' | jq -r .token)
# 3. the manifest: the OCI index, its linux/amd64 entry, that manifest's config digest
ACCEPT='application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json'
M=$(curl -s -H "Authorization: Bearer $TOKEN" -H "Accept: $ACCEPT" \
https://registry-1.docker.io/v2/tailscale/docker-extension/manifests/$TAG \
| jq -r '.manifests[] | select(.platform.os=="linux" and .platform.architecture=="amd64") | .digest')
CONFIG=$(curl -s -H "Authorization: Bearer $TOKEN" -H "Accept: $ACCEPT" \
https://registry-1.docker.io/v2/tailscale/docker-extension/manifests/$M | jq -r .config.digest)
# 4. the config blob — the labels live under .config.Labels
curl -sL -H "Authorization: Bearer $TOKEN" \
https://registry-1.docker.io/v2/tailscale/docker-extension/blobs/$CONFIG \
| jq '.config.Labels | {publisher: ."com.docker.extension.publisher-url", additional: ."com.docker.extension.additional-urls"}'
If the manifest request answers a single manifest rather than an index, skip to its .config.digest. Each manifest request counts against the registry's anonymous budget of 100 an hour per address — add -D - to see the ratelimit-remaining header come down. The plain way reads the same labels but pulls the whole image first:
docker pull tailscale/docker-extension:$TAG
docker inspect tailscale/docker-extension:$TAG | jq '.[0].Config.Labels'
Then ask RepoGates for its reading. With a personal API token from the account page:
curl -s -H "Authorization: Bearer rgp1.…" \
https://api.repogates.com/v1/dde/score/tailscale/docker-extension \
| jq '.findings[] | select(.check | startswith("dde-url"))'
From an agent, check_repo("tailscale", "docker-extension", platform="dde") on the MCP server returns the same rows; with the plugin installed in Claude Code, a line that reads docker extension install tailscale/docker-extension is sent to the preflight before it runs, and a HIGH finding on it is an ask.
Is a listing that links to another site a problem? No. A link whose host is neither the publisher's own site nor github.com or docker.com is recorded as dde-url-external, INFO, no points — and in the 18 September 2026 measurement of all 50 listed extensions it fired on 16 of them, every one a note. Documentation on another domain, a community site, a company page beside a project page: ordinary reasons. Only a scheme other than http or https costs anything.
Docker fixed CVE-2024-8696 in 2024 — why still check the scheme? Because a listing that carries a javascript:, file: or data: URL has no honest reason to. A patched Docker Desktop no longer executes such a URL; the check flags the shape, not the exploit, and RepoGates cannot see which Docker Desktop you run. Docker's own advice for an install that cannot update promptly was to disable Docker Extensions as a workaround. On the 18 September 2026 measurement no listed extension carried a non-http(s) URL.
Does D9 read the description, the changelog or the icon? No. D9 reads two labels, com.docker.extension.publisher-url and com.docker.extension.additional-urls, and nothing else. The detailed-description and changelog labels — plain text or HTML, the field behind CVE-2023-0625 (fixed in 4.12.0) and CVE-2024-8695 (fixed in 4.34.2) — and the icon URL behind CVE-2023-5166 (fixed in 4.23.0) are not assessed on this tier, and this page says so.
D8 Source repository — the same two labels read for a different question: whether one of the links is a GitHub repository the image can be checked against · D5 Marketplace listing — whether Docker's frozen index carries this image at all, which decides who, if anyone, ever looked at its labels · D7 VM backend — the other execution-surface read from the same registry pass, the backend beside the Docker socket.
Add RepoGates to Chrome Pricing
Numbers on this page: the 18 September 2026 measurement of all 50 listed Docker Desktop extensions (50 PASS / 0 REVIEW / 0 BLOCK) and the 12-extension registry sample of the same day; Docker Desktop release notes for 4.12.0, 4.23.0 and 4.34.2 and Docker's security announcement of 13 September 2024; Docker's extension labels, validation and publish pages; weights from the product's scoring table.