Files
felhom.eu/documentation/audits/version-travel-2026-09-26/A7/a7head.py
T

41 lines
2.0 KiB
Python

"""HEAD each ref@digest at its registry (anonymous token). HEAD is not a pull and does not count
against Docker Hub's pull limit. Prints one line per ref: status + registry answer."""
import json, sys, urllib.request, urllib.error, re
ACC = "application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json,application/vnd.docker.distribution.manifest.v2+json,application/vnd.oci.image.manifest.v1+json"
def split(ref):
parts = ref.split("/")
if len(parts) == 1 or ("." not in parts[0] and ":" not in parts[0] and parts[0] != "localhost"):
reg, repo = "registry-1.docker.io", ref
if "/" not in repo: repo = "library/" + repo
else:
reg, repo = parts[0], "/".join(parts[1:])
if reg == "docker.io": reg = "registry-1.docker.io"
repo = repo.split(":")[0] if ":" in repo.split("/")[-1] else repo
return reg, repo
def head(reg, repo, dig, token=None):
req = urllib.request.Request(f"https://{reg}/v2/{repo}/manifests/{dig}", method="HEAD", headers={"Accept": ACC})
if token: req.add_header("Authorization", "Bearer " + token)
try:
with urllib.request.urlopen(req, timeout=20) as r:
return r.status, r.headers
except urllib.error.HTTPError as e:
return e.code, e.headers
def token_for(hdrs, repo):
a = hdrs.get("www-authenticate") or ""
m = dict(re.findall(r'(\w+)="([^"]*)"', a))
if "realm" not in m: return None
url = f'{m["realm"]}?service={m.get("service","")}&scope=repository:{repo}:pull'
with urllib.request.urlopen(url, timeout=20) as r:
d = json.load(r)
return d.get("token") or d.get("access_token")
data = json.load(open(sys.argv[1]))
for ref, dig, srcs in data["with_digest"]:
reg, repo = split(ref)
try:
st, h = head(reg, repo, dig)
if st == 401:
st, h = head(reg, repo, dig, token_for(h, repo))
print(f"{st}\t{ref}@{dig[:19]}\t{','.join(srcs)}", flush=True)
except Exception as e:
print(f"ERR\t{ref}@{dig[:19]}\t{e}", flush=True)