fix(csp): let recordings play on the site, not just in the embed - #84
Merged
Conversation
media-src only allowed a remote origin on /embed/*, so every past live was CSP-blocked on pairux.com's own replay pages. The <video> rendered with the right src and the MP4 served fine — Chrome just refused the load with "MEDIA_ELEMENT_ERROR: Media load rejected by URL safety check", visible only in the console, so the player looked broken for no reason. Allow the Supabase Storage origin on every page instead, derived from the same NEXT_PUBLIC_SUPABASE_URL that builds the playback URLs. This is also narrower than what /embed/* had: a specific origin rather than all https:. Verified against prod /l/TER8XG in headless Chrome — blocked before the change; after it the recording loads (126.6s, 1280x720) and plays with no CSP violations. Adds the first CSP tests for the middleware, covering the media-src/embed divergence that let this through. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WFLwZtXE6iBJyJSgrsWqRM
ThreatCrush Security Scan67 finding(s) HIGH/CRITICAL: 13 | MEDIUM: 36 | LOW: 18
…and 17 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Past lives don't play anywhere on pairux.com —
/l/<code>, reached from/channels→ a channel → its history. Reported on https://pairux.com/l/TER8XG.Cause
media-srcinapps/web/src/middleware.tswas widened tohttps:only for/embed/*:Recordings are streamed from Supabase Storage (
https://<ref>.supabase.co/storage/v1/object/public/recordings/…), a cross-origin URL. So the same MP4 that plays in the embeddable player is blocked on the site's own pages.Nothing else was wrong: the
<video>renders with the correctsrc, and the file serves fine (200,video/mp4, 21.9 MB, range support). Chrome just refuses the load, and says so only in the console:That is why it reads as "the player is broken" rather than as a policy error.
The fix
One
media-srcfor every page, allowing the storage origin, derived from the sameNEXT_PUBLIC_SUPABASE_URLthat builds the playback URLs — with the project wildcard as a fallback so a missing var can't silently re-break replay. This is narrower than what/embed/*had: a specific origin instead of allhttps:.frame-ancestorskeeps its embed-only relaxation.Verification
Against prod
/l/TER8XGin headless Chrome:readyStatecurrentTimeadvancesPlus
vitest run src/middleware.test.ts(11 passed),tsc --noEmitclean,eslintclean.Also adds the first CSP tests for this middleware — there were none, which is how the embed/site divergence went unnoticed. One of them asserts the two surfaces agree on
media-src.🤖 Generated with Claude Code
https://claude.ai/code/session_01WFLwZtXE6iBJyJSgrsWqRM