Deferred hardening from #279.
The generic date tier in version_age_report.py issues an HTTP request to the package's source URL (read from build.ncl) to read its Last-Modified header. Because that URL is package-controlled metadata, an unconstrained fetch is an SSRF vector: a crafted source could point the CI runner at internal / metadata endpoints (169.254.169.254, RFC1918, loopback).
Ask: gate the fetch —
- allow
https:// only (and a host allowlist if practical);
- resolve the host and reject private / link-local / loopback addresses, re-checking after every redirect;
- cap redirects, response body size, and timeout.
Blast radius is limited today (the check runs read-only with no secrets), but the runner still has network position, so this should land before the source-URL tier is leaned on more heavily.
Part of the branch-promotion soak-gate work — criterion in gominimal/inbox#20, scope in gominimal/inbox#21.
Deferred hardening from #279.
The generic date tier in
version_age_report.pyissues an HTTP request to the package'ssourceURL (read frombuild.ncl) to read itsLast-Modifiedheader. Because that URL is package-controlled metadata, an unconstrained fetch is an SSRF vector: a craftedsourcecould point the CI runner at internal / metadata endpoints (169.254.169.254, RFC1918, loopback).Ask: gate the fetch —
https://only (and a host allowlist if practical);Blast radius is limited today (the check runs read-only with no secrets), but the runner still has network position, so this should land before the source-URL tier is leaned on more heavily.
Part of the branch-promotion soak-gate work — criterion in gominimal/inbox#20, scope in gominimal/inbox#21.