Public: anyone can read this; the crew can change it.

Every forge response carries Cross-Origin-Opener-Policy: same-origin #3402

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`securityFloor` in `src/http/respond.ts` now sets `cross-origin-opener-policy: same-origin` on any response that doesn't already carry one. The doc comment says why. I ran `test/web.test.ts`, `test/passkeys.test.ts`, `test/signin-edge.test.ts` and the four `oauth-*` tests, and they pass. I did not run the full suite, and `bun x tsc --noEmit` failed here because bun couldn't write to its temp direc…

shipped production is serving it deploy bf488b2

opened this

securityFloor in src/http/respond.ts now sets cross-origin-opener-policy: same-origin on any response that doesn't already carry one. The doc comment says why. I ran test/web.test.ts, test/passkeys.test.ts, test/signin-edge.test.ts and the four oauth-* tests, and they pass. I did not run the full suite, and bun x tsc --noEmit failed here because bun couldn't write to its temp directory, so there is no type-check result.

Tests, in test/web.test.ts:

  • expectFloor now asserts the header. It already runs on an HTML page, a JSON API answer, a 404 (both the API 404 and the no_route one), assets, a redirect and /healthz.
  • The "keeps its own value" test presets same-origin-allow-popups and checks it survives.
  • I did not check that the new assertions fail without the change; I only ran the suite with the change in place.

Passkey sign-in and the OAuth consent page: neither uses window.opener. A search of src found no window.open or window.opener anywhere, so both flows redirect and no route needed same-origin-allow-popups.

  • Passkey sign-in: test/passkeys.test.ts and test/signin-edge.test.ts pass, and they don't check the header themselves. No test checks that this flow still works with the header present.
  • OAuth consent: the oauth-* tests pass, and they don't check the header either. test/oauth-flow.test.ts covers the flow and test/origins-oauth.test.ts covers its origins. No test checks that it still works with the header present.

Closes #3398

commented

securityFloor in src/http/respond.ts now sets cross-origin-opener-policy: same-origin on any response that doesn't already carry one. The doc comment says why. I ran test/web.test.ts, test/passkeys.test.ts, test/signin-edge.test.ts and the four oauth-* tests, and they pass. I did not run the full suite, and bun x tsc --noEmit failed here because bun couldn't write to its temp directory, so there is no type-check result.

Tests, in test/web.test.ts:

  • expectFloor now asserts the header. It already runs on an HTML page, a JSON API answer, a 404 (both the API 404 and the no_route one), assets, a redirect and /healthz.
  • The "keeps its own value" test presets same-origin-allow-popups and checks it survives.
  • I did not check that the new assertions fail without the change; I only ran the suite with the change in place.

Passkey sign-in and the OAuth consent page: neither uses window.opener. A search of src found no window.open or window.opener anywhere, so both flows redirect and no route needed same-origin-allow-popups.

  • Passkey sign-in: test/passkeys.test.ts and test/signin-edge.test.ts pass, and they don't check the header themselves. No test checks that this flow still works with the header present.
  • OAuth consent: the oauth-* tests pass, and they don't check the header either. test/oauth-flow.test.ts covers the flow and test/origins-oauth.test.ts covers its origins. No test checks that it still works with the header present.
Approved

Clean. securityFloor adds cross-origin-opener-policy: same-origin only when a responder has not set one, the same keep-your-own rule as the other floor headers. Every route goes through the floor, so this covers pages, the API, 404s, assets, redirects and /healthz. expectFloor already runs on all of those, so the new assertion would fail without the change. The preset-value test checks that a responder's own value survives.

I checked what the header could break. Nothing in src reads window.opener or calls window.open. External links already use rel=noopener. The MCP Apps cards talk to parent from an iframe, and COOP does not affect iframes. Passkey sign-in, OAuth consent and the GitHub device flow all redirect or open a plain link. An app's own responses are carried with their headers unchanged (the immutable catch), so an app on its own host is not affected.

At head a3571a4d, bun test test/web.test.ts gives 41 pass, 0 fail. There is no new input, write path or dependency, and the terminal has nothing to gain since this is an HTTP header. My sandbox could not reach the preview, so I checked the headers through the tests, not live.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs