Skip to content

fix: stop reporting unrelated repos as Docker image provenance - #2

Open
azaidelson wants to merge 3 commits into
mainfrom
fix/dockerhub-repo-relatedness
Open

fix: stop reporting unrelated repos as Docker image provenance#2
azaidelson wants to merge 3 commits into
mainfrom
fix/dockerhub-repo-relatedness

Conversation

@azaidelson

@azaidelson azaidelson commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Follow-up to aae52c1 (resolve official Docker Hub images to source repo, not build wrapper).

Two independent ways a wrong GitHub repo gets reported as an image's source. Three reviewers shaped this; the first pushed version of this PR was itself wrong and was replaced (history kept for the record).

1. Links cited in passing

Filtering Docker's meta repos out of official-image descriptions promoted whatever link came next — including prose asides. library/ubuntu has no source link, only official-images, repo-info, docs, and this, in a locale example:

[PostgreSQL has a good example of doing so](https://github.com/docker-library/postgres/blob/69bc540.../9.6/Dockerfile#L21-L24)

So on main today, ubuntu:24.04 reports github.com/docker-library/postgres. The failure that bites is a wrong-but-plausible repo whose tags coincidentally match the image tag, which yields a fabricated commit with a confidence score.

Rejected approach (first push): require the image name to appear in the owner or repo. Measured against live Docker Hub, this fixed ubuntu and destroyed six correct answers — the source repo frequently shares no substring with the image (eclipse-temurinadoptium/containers, r-baserocker-org/rocker, oraclelinuxoracle/container-images, rockylinux, amazoncorretto, prom/node-exporter). It also promoted worse repos, since it scans for the first related link: library/ros went from osrf/docker_images to ros2/demos, matched only because ros is a substring of ros2.

Shipped approach: accept a link as a source claim on any of three signals — the repo is named after the image, it belongs to the image's own Docker Hub namespace, or it is linked more than once. Descriptions link the source once per supported tag (oracle/container-images appears 18 times); a prose aside appears once.

Over 80 library and namespaced images this matches previous behaviour everywhere except three, all improvements:

image before after
ubuntu docker-library/postgres none
registry opencontainers/distribution-spec distribution/distribution
clickhouse/clickhouse-server moby/moby ClickHouse/ClickHouse

This also makes the meta-repo deny list load-bearing rather than cosmetic: docker-library/docs and friends are linked 2-6 times in nearly every official description, so a count-based rule would otherwise select them constantly.

2. Redirects turning guesses into answers

When the description yields nothing, resolution guesses name/name. check_github_repo_exists followed redirects, so GitHub's rename redirects turned wrong guesses into confident answers:

ubuntu/ubuntu  -> ubuntu-xx/ubuntu                 (personal repo)
debian/debian  -> Debian/.github                   (meta repo)
consul/consul  -> consuldemocracy/consuldemocracy  (unrelated project)

This is why ubuntu still reported a repo after fix 1 — the description path correctly gave up, then the guess "succeeded" via a redirect. It now requires the API to answer at exactly the requested path, ignoring case.

Measured cost: of 40 guesses, 5 resolve only via redirect; only nginxinc/nginx-prometheus-exporter (a genuine org rename) is one we lose — it returns nothing instead of nginx/nginx-prometheus-exporter. A rename and a squatted name are structurally identical over the API, so no rule separates them. For a provenance tool, no answer beats a wrong one. This is a separate commit (e40ef06) and can be dropped independently if you disagree with that trade.

Result

$ code-provenance --image ubuntu:24.04
  repo:       -
  status:     repo_not_found          # was: github.com/docker-library/postgres

$ code-provenance --image traefik:v3.6.0
  repo:       github.com/traefik/traefik
  commit:     06db5168c0d9
  status:     resolved / exact

Tests

Python 71 pass, Node 37 pass. New coverage for the namespaced path (never exercised before), repeatedly-linked sources, meta repos linked repeatedly, and redirect rejection. node/src/github.ts had no test file at all — node/src/github.test.ts is new. npm test now builds first, so src/ tests cannot silently go unrun.

Known remaining gaps (unchanged by this PR)

  • The -library-image wrapper filter is narrower than wrapper naming in the wild: redisredis/docker-library-redis, nginxnginxinc/docker-nginx, mariadbMariaDB/mariadb-docker still resolve to build wrappers rather than source.
  • The link regex terminates the repo capture on ., so repos with a dot in the name can never resolve (WASdev/ci.docker captures as WASdev/ci).

Filtering Docker's meta repos out of official-image descriptions promoted
whatever GitHub link happened to come next, including links that appear in
prose. library/ubuntu resolved to docker-library/postgres, linked inside a
locale example in its README:

  [2/5] Repo inferred: docker-library/postgres
  repo:       github.com/docker-library/postgres

Require the fallback candidate's owner or repo to contain the image name, and
return nothing when none does. Exact name matches are unaffected, and the
common wrapper naming still resolves (alpinelinux/docker-alpine,
MariaDB/mariadb-docker, CentOS/sig-cloud-instance-images).

Also treat docker-library/faq, docker/hub-feedback and docker/roadmap as meta
repos - they appear in the same descriptions and could win the fallback.

Adds node/src/github.test.ts, which had no coverage; the two implementations
were kept in sync by eye.
Reviewers found the name-substring gate rejected correct answers for a whole
class of images: the source repo often shares no substring with the image name.
Measured against live Docker Hub, it fixed ubuntu but destroyed six:

  oraclelinux      oracle/container-images                -> none
  rockylinux       rocky-linux/sig-cloud-instance-images  -> none
  eclipse-temurin  adoptium/containers                    -> none
  amazoncorretto   corretto/corretto-docker               -> none
  r-base           rocker-org/rocker                      -> none
  prom/node-exporter  prometheus/node_exporter            -> none

It also promoted worse repos, since it scans for the first *related* link:
library/ros went from osrf/docker_images to ros2/demos, matched only because
"ros" is a substring of "ros2".

Accept a link as a source claim on any of three signals instead: the repo is
named after the image, it belongs to the image's own Docker Hub namespace, or
it is linked more than once. Descriptions link the source once per supported
tag (oracle/container-images, 18 times) while a prose aside appears once.

Over 80 images this now matches the previous behaviour everywhere except three,
all improvements:

  ubuntu                        docker-library/postgres          -> none
  registry                      opencontainers/distribution-spec -> distribution/distribution
  clickhouse/clickhouse-server  moby/moby                        -> ClickHouse/ClickHouse

This also makes the meta-repo deny list load-bearing rather than cosmetic:
docker-library/docs and friends are linked 2-6 times in nearly every official
description, so a count-based rule would otherwise select them constantly.

Tests now cover the namespaced path, which the previous round never exercised,
and 'npm test' builds first so src/ tests cannot silently go unrun.
infer_repo_from_dockerhub guesses name/name (or namespace/name) when the
description yields nothing, and check_github_repo_exists followed redirects, so
GitHub's rename redirects turned wrong guesses into confident answers:

  ubuntu/ubuntu   -> ubuntu-xx/ubuntu                  (personal repo)
  debian/debian   -> Debian/.github                    (meta repo)
  consul/consul   -> consuldemocracy/consuldemocracy   (unrelated project)

This is why ubuntu:24.04 still reported a source repo after the previous
commit: the description path correctly gave up, then the guess "succeeded" via
a redirect. It now reports repo_not_found.

Require the API to answer at exactly the requested path, ignoring case. The
cost, measured over 40 guesses: 5 resolve only via a redirect, and of those
only nginxinc/nginx-prometheus-exporter (a genuine org rename) is one we lose -
it now returns nothing instead of nginx/nginx-prometheus-exporter. A rename and
a squatted name are structurally identical over the API, so no rule separates
them; for a provenance tool, no answer beats a wrong one.
@azaidelson azaidelson changed the title fix: don't report unrelated repos linked in passing as image source fix: stop reporting unrelated repos as Docker image provenance Jul 22, 2026
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.

1 participant