Repository navigation
Description
Finding — security: pkgs/cf-container-hello ships an unauthenticated Worker in front of a stale, unhardened crawler copy (SSRF / local-file fetch)
PR #935 (merged, head 92d0c6ee, commits 4a508974, 60656e73, 92d0c6ee) added the diagnostic probe package pkgs/cf-container-hello and kept it as the "known-good container reference". Two halves combine into an exposed fetch proxy:
- The Worker has no auth gate at all.
pkgs/cf-container-hello/src/index.ts(whole file, verified on maina2eef6f7) contains noAuthorization/ token check.fetch()routes by path prefix:/probe…forwards the request as-is to theCrawlerProbecontainer (getContainer(env.CRAWLER_PROBE, "probe-2").fetch(request)),/lite…and/name/…forward to other containers, and anything else goes to the hello container. - The container it fronts is the pre-hardening crawler.
pkgs/cf-container-hello/image/app.pyHandler.do_POST(~lines 76–103) doesurl = json.loads(self.rfile.read(length))["url"]thenurllib.request.urlopen(Request(url), timeout=20, context=CTX)— no scheme allowlist, no localhost/private-IP policy, no redirect re-validation, and returns the fetched page's extracted text (up to 20,000 chars) as JSON.
Contrast with the production crawler this was copied from: pkgs/lab-ops-hub/container/app.py carries an extensive validate_crawl_url SSRF policy (the #441 policy, hardened further in #516/#526/#529/#572/#593/#614 — public http(s) only, no localhost-style names, no non-global IP literals, redirect targets held to the same policy, container re-validates independently as defence-in-depth), and pkgs/lab-ops-hub/src/index.ts gates /api/* behind the API_TOKEN secret, fail-closed (503 when unconfigured, 401 when unauthenticated). The probe copy has none of either layer. The deployed image for the probe is also a stale fork: pkgs/cf-container-hello/image/container-image-v4.nix builds this 103-line app.py, while production moved to :v5 built from the hardened container/app.py (PR #935 body).
Verified locally (2026-10-26, this run)
Ran the exact committed pkgs/cf-container-hello/image/app.py on localhost and probed POST /crawl:
{"url":"file:///etc/hostname"}→ handler returned 200.urllibaccepted thefile://scheme and read the local file — there is no scheme validation anywhere in the handler. (Extracted text was empty for that file in the sandbox, but the fetch executed; on the container image the same path reads whatever the process can read and returns its text content.){"url":"http://127.0.0.1:8080/"}→ the loopback fetch was attempted with no policy block (it blocked the single-threaded server on itself until the client timed out). No localhost/private-range check exists.
Why it matters
PR #935's body says the clean-room Worker was deployed and used live for the container diagnosis, and the package is kept (README: "known-good container reference", wrangler.jsonc name cf-container-hello). If that deployment (workers.dev or any route) is still live, anyone on the internet has an unauthenticated SSRF/local-file fetch proxy that also burns container minutes. Even if it is currently undeployed, the committed source deploys that way by default — the next diagnostic session that runs npm run deploy re-exposes it.
This is a single-repo finding (code only): the production lab-ops-hub crawler is hardened and gated; only the probe copy is exposed.
Acceptance criteria
- Determine whether the
cf-container-helloWorker is currently deployed/routed anywhere; record the answer on this issue. If live and unauthenticated, take it down or gate it immediately. -
pkgs/cf-container-hello/src/index.ts: every route that forwards to a container (at minimum/probe) requires the same class of bearer-token gate lab-ops-hub uses, fail-closed when the secret is unset — or the forwarding routes are removed. -
pkgs/cf-container-hello/image/app.py: the stale crawler copy is eliminated as a divergence risk — either build the probe image from the production hardenedpkgs/lab-ops-hub/container/app.py, or port itsvalidate_crawl_urlpolicy verbatim (public http(s) only; nofile:/other schemes; no localhost/private/non-global targets; redirects re-validated). - A regression test/probe shows
file://,http://127.0.0.1/, a private-range IP literal, and a redirect to a private target are all rejected by the probe crawler, and an unauthenticatedPOST /probe/crawlis rejected by the Worker.
Activity
dezren39 commentedon Oct 26, 2026
🤖 Agent note · state: filed
10 remaining items
dezren39 commentedon Oct 26, 2026
🤖 Agent note · picked up
queue/1104-955-cf-container-hello-auth, worktree~/git/wt-1104style(cf-container-hello): ruff-format the #955 regression test
dezren39 commentedon Oct 26, 2026
🤖 Agent note · picked up → gate deployed live
~/git/wt-1104; this run added the missing regression test, committed, and opened PR fix(cf-container-hello): fail-closed API_TOKEN gate, drop stale crawler copy (#955) #978)Deployment status (acceptance #1): LIVE, was unauthenticated — now gated.
cf-container-hellodeployed (latest pre-fix deployment 2026-10-26T01:01Z), no custom domains, served atcf-container-hello.developing-today.workers.dev. Pre-fix unauthenticated probes:GET /probe/→ 200 from the crawler container;GET /worker-marker→ 200.10405 Method not allowed for this authentication scheme), so the exposure was closed by deploying the gated Worker from this PR's branch (version3a04cb82-1be0-4f75-a097-771f599f176e) and setting theAPI_TOKENsecret (generated this run, filed in Bitwarden Secrets Manager ascloudflare.cf-container-hello-api-token, mirroring the lab-ops-hub pattern).GET /unauth → 401;GET /probe/unauth → 401;POST /probe/crawlunauth → 401;GET /probe/with bearer → 200 from the hardened v5 crawler container;GET /worker-marker→ 200 (public by design, starts no container)./probe-prefixed path, so itsPOST /crawlroute 404s end-to-end (pre-existing path behaviour, unchanged by this fix); the SSRF policy itself is pinned by the regression tests against the production validator the probe image now runs.Remaining: PR #978 CI re-check after the treefmt fix, then merge under the standing criteria.
Merge pull request #978 from developing-today/queue/1104-955-cf-conta…
dezren39 commentedon Oct 26, 2026
🤖 Agent note · done
24261355); all checks green (treefmt, run, Nix flake evaluation) after a ruff-format fix to the new test.API_TOKEN(deployed live and verified: unauth 401 on/,/probe/,POST /probe/crawl; authed 200 from the hardened v5 crawler; token filed in Bitwarden ascloudflare.cf-container-hello-api-token); stale crawler copy deleted — CrawlerProbe runs the productionlab-ops-hub-crawlercontainer:v5; 11 regression tests pass. Live exposure closed by the deploy, not just by the merge.queue/1104-955-cf-container-hello-auth