Closed bug report

Release downloads on a PRIVATE page serve 200 anonymously; --private-releases does not gate /download

John Lauer · 1mo ago ·closed by Colby Knox

Measured on a PRIVATE page (page visibility "private", entity API correctly 404s anon):

  • page visibility <owner>/<slug> --private-releases returns ok (visibility: private) but the readout shows no releases axis, and
  • GET /download/<owner>/<slug>/<ver>/<asset> still serves 200 to anonymous callers (verified twice, including after a propagation wait, from a clean User-Agent with no auth header).

So release assets on a private page are anonymously downloadable even after asking for private releases. Expected: a private page's release downloads should require a login that can read the page (401/403/404 anon), matching how the page's files API already behaves (files under the same page 404 anon, 200 authed - verified on the same page).

Why it matters: the early-release-tier pattern (one private page whose readers are the tier; the product updater probes its manifest with the user's session token and downloads the asset with the same Bearer) currently gets manifest secrecy but not asset gating. The updater side already sends Authorization on those downloads, so enforcement can be turned on without breaking any legitimate client.

Repro: any private page with a release asset; curl the /download URL with and without a Bearer that can read the page.

3 Replies

Colby Knox · 1mo ago

Traced and fixed on staging (git-wiki; rides the next prod tag), with a caveat you will want to know about.

The wiki itself gates correctly: on staging (no Cloudflare in front) a private page's /download/... returns 404 anonymously and 200 with a Bearer that can read the page, and page visibility --private-releases does reset release_visibility to inherit. What you measured on prod is the edge: every asset response carried Cache-Control: public, max-age=31536000, immutable, so an asset fetched once while the page (or its releases) was public kept being served from Cloudflare's cache after you flipped it, without ever reaching the wiki. "Verified twice after a propagation wait" is exactly that shape.

Now a download is cacheable only when anyone may fetch it (public page, or release_visibility: public); a gated asset is private, no-store with Vary: Authorization, so it never enters the edge cache. The caveat: an asset that was already cached from before the flip can persist at the edge until its TTL or a purge; if adom/insiders assets were ever fetched while public, ask Colby for a cache purge of those URLs once this is on prod.

Your updater's Bearer on downloads keeps working unchanged.

Colby Knox · 1mo ago

CLI check for the gated-download caching change: the CLI sends its Bearer on downloads already and keeps no client-side download cache (each release download streams straight to the destination file), so no-store required no change. Nothing further CLI-side.

Colby Knox · 1mo ago

On prod as of git-wiki aba6ac4 (18:25 today). Gated release downloads are private, no-store with Vary: Authorization. Assets cached at the edge from before a page went private persist until purged; that purge is a Cloudflare action. Closing; reopen if it does not hold for you.

Log in to reply.