Scope camera access to QR Master instead of the whole site - #165
Merged
Conversation
The Permissions-Policy header granted camera=(self) on every response. The reason was one layer down: UtilityShellPage defaulted iframeAllow to "camera 'self'", so all 28 embedded utilities asked for a camera and the header had to allow it site-wide for the one tool that uses it. QR Master is that tool — it scans through html5-qrcode with facingMode: "environment". The other two getUserMedia hits under static/utility-apps are Emscripten's SDL shim in the metal-bending worker and in the OpenCascade glue, dead code in both builds. So: default the delegation to nothing, name the camera on QR Master, and set the header per path. 26 utility pages now render allow="", one asks for the camera, one for the microphone. The header is the part that actually enforces this. A same-origin iframe inherits any feature whose default allowlist is 'self' regardless of its allow attribute, so narrowing the attribute alone would have changed nothing; it encodes intent, and starts mattering the day an app is served cross-origin. Also fixes a live gap in the whisper-transcriber rules, which matched /utilities/whisper-transcriber/ but not /ru/utilities/whisper-transcriber/ or the other four locales — so the microphone was denied on five of six languages. Both tools now match an optional locale segment. Verified with path-to-regexp 6.3.0, the version behind Vercel's routing: the pattern matches all six locale paths and still rejects /docs/utilities/qr-master/, /utilities/qr-nameplate/ and a bogus /xx/ prefix. Whisper also no longer claims camera=(self) in its own rule; it only ever needed the microphone, and it carried camera along because these rules replace the global header rather than merge with it. Verified on a six-locale build: qr-master renders allow="camera 'self'" in both / and /ru/, whisper renders allow="microphone 'self'", and pipe-cutter — which used to inherit the camera — renders allow="". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
YurMil
added a commit
that referenced
this pull request
Aug 17, 2026
The Supabase check reports two different failures depending on where it runs, and the runbook only covered one of them. On a pull request it builds a database from the migration files and dies on public.profiles. On main it syncs to production and dies on "Remote migration versions not found in local migrations directory" — which it has been doing since at least 7 August, before any of the recent work. That second failure matters for task 1: nothing has synced to production for weeks, so db push is not going to apply the migration cleanly the way the document originally implied. Added the diagnosis, the migration list step, and the choice between capturing a remote-only version and discarding the record — with a warning against reaching for migration repair to silence the error, since that is how the drift got this far. Also frames the whole document around that drift: the repository and the production database disagree in both directions, and every task here is a symptom of it. Status table refreshed — #165 and #166 are merged, nanoid opened a new high alert today. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YurMil
added a commit
that referenced
this pull request
Aug 17, 2026
The Supabase check reports two different failures depending on where it runs, and the runbook only covered one of them. On a pull request it builds a database from the migration files and dies on public.profiles. On main it syncs to production and dies on "Remote migration versions not found in local migrations directory" — which it has been doing since at least 7 August, before any of the recent work. That second failure matters for task 1: nothing has synced to production for weeks, so db push is not going to apply the migration cleanly the way the document originally implied. Added the diagnosis, the migration list step, and the choice between capturing a remote-only version and discarding the record — with a warning against reaching for migration repair to silence the error, since that is how the drift got this far. Also frames the whole document around that drift: the repository and the production database disagree in both directions, and every task here is a symptom of it. Status table refreshed — #165 and #166 are merged, nanoid opened a new high alert today. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Why it was site-wide
Permissions-Policygrantedcamera=(self)on every response. The cause sat one layer down:UtilityShellPagedefaultediframeAllowto"camera 'self'", so all 28 embedded utilities requested a camera and the header had to permit it globally for the single tool that uses one.That tool is QR Master — it scans via
html5-qrcodewithfacingMode: "environment"(app.html:588). The only othergetUserMediahits understatic/utility-appsare Emscripten's SDL shim inside the metal-bending worker and the OpenCascade glue — dead code in both builds, sitting next to an audio MIME-type map.The change
iframeAllowdefaults to''; QR Master declarescamera 'self'for itself.Permissions-Policyis set per path: global goes back tocamera=(), QR Master's two paths getcamera=(self).camera=(self)from its own rule — it only ever needed the microphone, and carried the camera because these rules replace the global header rather than merge with it.The header is what enforces this. A same-origin iframe inherits any feature whose default allowlist is
'self'regardless of itsallowattribute, so narrowing the attribute alone would have changed nothing. It encodes intent, and starts mattering the day an app is served from another origin.Also fixes: the locale gap
The existing whisper rules matched
/utilities/whisper-transcriber/but not/ru/utilities/whisper-transcriber/or the other four locales — the shell page exists under all six. On those pages the global header applied, somicrophone=()reached the parent document and the same-origin iframe could not hold a capability its parent lacked. The microphone was denied on five of six languages.Both tools now match an optional locale segment. Verified against
path-to-regexp6.3.0, the version behind Vercel's routing:/utilities/x/:path*/:locale(ru|ua|de|es|et)?/utilities/x/:path*/utilities/qr-master//ru/utilities/qr-master//de/,/et/, …/docs/utilities/qr-master//utilities/qr-nameplate//xx/utilities/qr-master/Verification
Six-locale build, exit 0. Rendered
allowattributes:build/utilities/qr-master/index.htmlandbuild/ru/utilities/qr-master/index.html→allow="camera 'self'"build/utilities/whisper-transcriber/index.html→allow="microphone 'self'"build/utilities/pipe-cutter/index.html→allow=""(previously inherited the camera)Across all built utility pages: 26 with no delegation, 1 camera, 1 microphone.
vercel.jsonparses as valid JSON;pnpm typecheckclean; eslint on the changed files reports only the pre-existingset-state-in-effectwarnings.Worth a preview smoke test
Header rules that replace rather than merge is the convention already in this file, not something I could verify locally. Worth opening QR Master on the preview deploy — on
/utilities/qr-master/and on/ru/utilities/qr-master/— and confirming the scanner still gets the camera, plus whisper's microphone on a non-English page, which should now work for the first time.🤖 Generated with Claude Code