Skip to content

fix(uploads): phase 1 — shadow auth on /uploads/ + noindex - #530

Merged
MCERQUA merged 1 commit into
mainfrom
fix/sec-062-uploads-shadow
Oct 6, 2026
Merged

MCERQUA merged 1 commit into
mainfrom
fix/sec-062-uploads-shadow

Conversation

@MCERQUA

@MCERQUA MCERQUA commented Oct 6, 2026

Copy link
Copy Markdown
Owner

/uploads/<file> is served without checking the session. Requiring a login outright would break legitimate machine consumers (scripts fetching files over HTTP, MMS providers fetching media), so this is phase 1 of a staged change, controlled by UPLOADS_AUTH_MODE and active only when CANVAS_REQUIRE_AUTH=true:

  • shadow (default): behaviour unchanged, but every request that would be denied is appended to uploads/.uploads-auth-shadow.jsonl (a dotfile, never served), so the real consumers can be measured before anything is enforced.
  • enforce: no valid Clerk session → 401 (browsers get the login redirect canvas pages already use), decided before revealing whether the file exists.
  • off: unchanged.

Every served upload now also carries X-Robots-Tag: noindex, nofollow, noarchive.

Self-hosted installs (auth off) are unaffected.

/uploads/<file> is served without checking the session. A plain login gate
would break legitimate machine consumers (scripts fetching files over HTTP,
MMS providers fetching media), so this ships in phases, controlled by
UPLOADS_AUTH_MODE (only when CANVAS_REQUIRE_AUTH is on):
  shadow (default) - serve as before; log every request that WOULD be denied to
                     uploads/.uploads-auth-shadow.jsonl (dotfile, never served)
  enforce          - no Clerk session -> 401 (browsers: login redirect), decided
                     before revealing whether the file exists
  off              - unchanged
Every served upload now carries X-Robots-Tag: noindex, nofollow, noarchive.

Tests: 12 new (shadow logs anon / not signed-in, enforce 401 + redirect + no
existence oracle, signed-in served, off/auth-off unchanged, noindex, log not
served, log failure never breaks serving). Mutant without the check fails 4.
Full suite 765 passed, 26 skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MCERQUA
MCERQUA merged commit 24aec6d into main Oct 6, 2026
2 checks passed
@MCERQUA
MCERQUA deleted the fix/sec-062-uploads-shadow branch October 6, 2026 05:13
MCERQUA added a commit that referenced this pull request Oct 16, 2026
/uploads/<file> is served without checking the session. A plain login gate
would break legitimate machine consumers (scripts fetching files over HTTP,
MMS providers fetching media), so this ships in phases, controlled by
UPLOADS_AUTH_MODE (only when CANVAS_REQUIRE_AUTH is on):
  shadow (default) - serve as before; log every request that WOULD be denied to
                     uploads/.uploads-auth-shadow.jsonl (dotfile, never served)
  enforce          - no Clerk session -> 401 (browsers: login redirect), decided
                     before revealing whether the file exists
  off              - unchanged
Every served upload now carries X-Robots-Tag: noindex, nofollow, noarchive.

Tests: 12 new (shadow logs anon / not signed-in, enforce 401 + redirect + no
existence oracle, signed-in served, off/auth-off unchanged, noindex, log not
served, log failure never breaks serving). Mutant without the check fails 4.
Full suite 765 passed, 26 skipped.

Co-authored-by: Mike <mike@jam-bot.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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