Skip to content

SAFETY-001: preserve callbacks and isolate local Firebase - #128

Merged
daliu merged 9 commits into
mainfrom
codex/issue-99-safety-callback-emulators
Jul 13, 2026
Merged

daliu merged 9 commits into
mainfrom
codex/issue-99-safety-callback-emulators

Conversation

@daliu

@daliu daliu commented Jul 13, 2026 •

Copy link
Copy Markdown
Contributor

Outcome

Preserve complete same-origin GitHub Pages callback routes and make local Firebase development fail closed onto a synthetic, loopback-only Firebase boundary.

Fixes #99

Security invariant

  • Browser development/test uses demo-mprc-local; isolated Functions and Rules harnesses use explicit demo-only project IDs.
  • Auth, Firestore, and Functions all target loopback emulators.
  • A connector setup failure stops local startup.
  • App Check, Analytics, and Sentry do not initialize locally.
  • App Check, Analytics, and Sentry do not initialize on an initial capability-bearing callback.
  • Both Pages documents apply strict-origin before any subresource, keeping callback path/query/fragment out of referrer headers.
  • The browser CSV export no longer bypasses the Functions emulator.
  • Stored callback targets must remain same-origin; malformed, outside-site, and normalized protocol-relative targets are discarded.
  • The bridge never proves payment, OAuth state, identity, or authorization.

Failure and compatibility behavior

  • If browser storage or history is unavailable, callback restoration fails safely.
  • If an emulator later stops, requests fail locally; there is no production fallback.
  • Production keeps its existing Firebase project and Functions endpoint behavior.
  • Ordinary configured production pages retain their existing Analytics/Sentry startup behavior; broader telemetry policy stays OBS-001A — Remove personal data and session replay from current monitoring and logs #111.
  • No schema, production data, Rules, provider configuration, or migration changes.
  • Revert this pull request to undo the source change. No data rollback is required.

Verification

Node 20:

  • Frontend: 4 suites / 47 tests passed.
  • Standalone SPA callback/referrer suite: 10/10 passed.
  • Focused Firebase/monitoring tests: 31/31 passed.
  • Functions: lint passed; 3 suites / 17 tests passed.
  • Changed runtime/test lint: 0 errors; ten pre-existing warnings remain in the legacy admin registrations page.
  • Diagnostic optimized build passed.
  • Java 17 Rules suite: 4 suites / 295 tests passed.
  • Demo-only Firebase CLI smoke: Auth, Firestore, and Functions reported ready on 127.0.0.1 ports 9099, 8080, and 5001.
  • git diff --check passed; 34 changed-document relative links resolved.

No production account, data, callback, email, Stripe/Strava call, monitoring provider, or Firebase deployment was used.

Officer impact

Officers receive a clearer preview stop condition: current previews are public/read-only review surfaces only. They must not sign in or use private, admin, signup, payment, email, or Strava behavior there.

Officer documentation

  • docs/officers/PUBLISH_AND_CHECK.md
  • Supporting current-truth updates in README.md, OPERATIONS_RUNBOOK.md, SYSTEM_DESIGN.md, SECURITY.md, and agent/issue maps.

Deployment evidence

  • Source changed: yes, exact head f7b63d401e89e4d259b28baec093aeab790cda62.
  • Tests passed locally: yes, as listed above.
  • Code merged: no — awaiting review/checks.
  • GitHub Pages published: no — merge not performed.
  • Netlify / runmprc.com: not verified; do not exercise Firebase/private behavior in previews.
  • Firebase deployed: no.
  • Outside provider configured or verified: no.
  • Production behavior verified: no.

Remaining work

@netlify

netlify Bot commented Jul 13, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for luminous-fox-7c393f ready!

Name Link
🔨 Latest commit f7b63d4
🔍 Latest deploy log https://app.netlify.com/projects/luminous-fox-7c393f/deploys/6a549a481afdd00008bd9fa2
😎 Deploy Preview https://deploy-preview-128--luminous-fox-7c393f.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@daliu

daliu commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Independent read-only review blocker (no files or GitHub ownership changed):

The SPA bridge now restores callback query/fragment state before app startup. #99 correctly suppresses Sentry on those initial capability-bearing callback URLs, but production FirebaseResources.initAnalytics() still initializes Firebase Analytics there. Firebase Analytics initialization sends the initial page view unless explicitly disabled, so Stripe session/confirmation capabilities or Strava code/state can be included in analytics URL data.

Please do not merge this revision. Smallest in-scope fix: apply the same initial capability-route plus query/fragment guard before Analytics isSupported / getAnalytics, then add focused production callback tests proving neither is called. This only prevents the new callback-preservation regression; #111 should continue to own broader hosted analytics/redaction/consent work.

@daliu

daliu commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Second independent read-only review blocker at exact head f97c2f4:

parseSameOriginTarget checks the URL origin before returning its normalized pathname. Dot-segment normalization can therefore turn an input into a protocol-relative returned target. For example, parsing /%2e%2e//attacker.example against https://runmprc.com returns //attacker.example instead of null; /.//attacker.example and /a/..//attacker.example have the same problem.

history.replaceState should currently throw on that cross-origin result and the caller catches it, so the browser restore fails safe. However, the parser violates #99's explicit protocol-relative rejection invariant and its own contract.

Smallest fix: after URL normalization, require the pathname to start with exactly one slash (reject //) before returning it. Add negative parser plus restore/state-clearing tests for these variants. Please re-run and re-review before merge.

@daliu

daliu commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Final independent read-only review: APPROVE exact head 6082bf7d0f526d2958564258706f528d378bfc5c.

  • Analytics and Sentry both fail closed before initialization on the three capability callback routes when query/fragment state is present; broader OBS-001A — Remove personal data and session replay from current monitoring and logs #111 work remains separate.
  • Normalized protocol-relative paths are rejected, including all three dot-segment variants; legitimate callback query/fragment restoration remains intact.
  • SPA navigation tests pass 10/10, diff/worktree are clean, and the final PR-body demo-project wording is accurate.
  • Exact-head GitHub CI jobs and Netlify preview are green.

No remaining code, security, scope, or documentation blocker found. This is independent Codex-agent review, not human approval; merge authority remains with the #99 owner.

@daliu
daliu marked this pull request as ready for review July 13, 2026 07:58
@daliu
daliu merged commit a237e1a into main Jul 13, 2026
7 checks passed
@daliu
daliu deleted the codex/issue-99-safety-callback-emulators branch July 13, 2026 07:58
@daliu

daliu commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

Final documentation-coordination blocker on head f7b63d4:

The callback ownership split is technically accurate, but the new catalog item OAUTH-001C overlaps live issue #88, which already owns Strava callback state/code validation and the server exchange lifecycle. The repository docs do not map OAUTH-001/OAUTH-001C to #88 and currently leave OAUTH-001C simply proposed; that can cause another agent to publish a duplicate despite the search-first rule.

Before merge, explicitly map OAUTH-001 to #88 and mark OAUTH-001C as represented within #88, or formally make #88 the tracker and split/cross-link its child. DATA-001A has no live duplicate in the current search. Exact-head checks are otherwise green.

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