fix: allow Safari null origins on bridge login - #87
Merged
Conversation
Safari privacy-masks the same-origin login Origin as the literal string null, which the present-Origin check rejected. Treat null like an absent Origin and require the existing Sec-Fetch-Site or https Referer proof. Co-authored-by: Cursor <cursoragent@cursor.com>
This was referenced Aug 20, 2026
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.
Intent
Bridge login: treat Origin: null as absent - the captain's real Safari sends it.
Definitive captured evidence (2026-08-19 15:42:15, transparent header tap in front of the live service): the captain's iPhone Safari (iOS 18.7 UA, Version/26.6) submits the same-origin login form with Origin: null (the literal string - Safari privacy masking) plus Sec-Fetch-Site: same-origin, Sec-Fetch-Mode: navigate, no Referer. Current _origin_ok() in bin/fm-bridge-view.py takes the present-Origin branch, compares "null" against the expected origins, and 403s - so the captain has never once logged in from his phone.
Change, surgical: in _origin_ok(), treat a literal null Origin (case-insensitive) exactly like an ABSENT Origin, falling through to the existing same-origin proofs (Sec-Fetch-Site same-origin/none, else https Referer host match). A present Origin that is neither null nor matching stays rejected. Brief inline comment citing Safari's privacy masking; update the one relevant paragraph in docs/bridge-view.md.
Tests: replicate the captured request shape exactly (Origin: null + Sec-Fetch-Site: same-origin + form content type -> login succeeds with correct passcode); Origin: null with NO same-origin proof -> rejected; existing origin tests unchanged.
Scope is exactly this branch change - nothing else. After the PR is green and merged, firstmate redeploys and live-verifies; that redeploy is out of scope for this change.
What Changed
Origin: nullbridge login requests like an absent Origin, requiring existing Fetch Metadata or HTTPS Referer same-origin proof.nullOrigin behavior.nullOrigin requests.Risk Assessment
✅ Low: Captain, the change is tightly scoped, matches the authoritative intent, preserves the existing CSRF proof requirements, and adds focused regression coverage.
Testing
Inspected the complete three-file change, ran the focused bridge behavior suite successfully, and exercised the live HTTP login endpoint with positive and negative null-Origin requests; the captured transcript demonstrates the intended 303 login/session behavior and required 403 fail-closed behavior. No screenshot was captured because this is server-side request validation with no changed visual surface.
Evidence: Safari null-Origin login HTTP transcript
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
git diff --unified=80 6c2b65a5e8d72552dae701f03401ce665d159887..7956b3f13f24722d74a55bd0d4f4498577854871 -- bin/fm-bridge-view.py docs/bridge-view.md teststests/fm-bridge-view.test.shManual live bridge HTTP check: exact Safari request shape (Origin: null,Sec-Fetch-Site: same-origin,Sec-Fetch-Mode: navigate, no Referer, form content type) with correct passcodeManual negative live bridge HTTP check: mixed-caseOrigin: NuLlwithout Fetch Metadata or Referer proof✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.