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

scout/forge:scout/3398-every-forge-response-carries-cross-origi → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/3398-every-forge-response-carries-cross-origi was merged into main as 488af6d.

Review

Sign in to review this change.
2 files changed +12 −1
src/http/respond.ts +5 −1
@@ -138,7 +138,9 @@ const HSTS = "max-age=31536000";
138138 /**
139139 * The security floor every response the forge writes leaves with: never
140140 * sniffed into another type, never a referrer sent off-site, no powers a
141 * page does not use, https once https, and — unless the body is a document
141 * page does not use, https once https, no window another site opened or
142 * opens kept in reach (Cross-Origin-Opener-Policy; the sign-in and consent
143 * flows redirect and never read `window.opener`), and — unless the body is a document
142144 * that wrote its own policy — inert.
143145 *
144146 * A responder that already set one of these keeps its value: `page()` has a
@@ -158,6 +160,8 @@ export function securityFloor(response: Response): Response {
158160 headers.set("permissions-policy", PERMISSIONS_POLICY);
159161 if (!headers.has("strict-transport-security"))
160162 headers.set("strict-transport-security", HSTS);
163 if (!headers.has("cross-origin-opener-policy"))

Right default. same-origin, not same-origin-allow-popups, is correct because no forge page opens a popup that needs a handle back.

164 headers.set("cross-origin-opener-policy", "same-origin");
161165 // What left decides, not what was asked for: an HTML body writes the
162166 // only policy that can name its own nonce, and `default-src 'none'`
163167 // over a page would strip it of its stylesheet.
test/web.test.ts +7
@@ -814,6 +814,9 @@ function expectFloor(what: string, response: Response): void {
814814 expect(label + response.headers.get("strict-transport-security")).toBe(
815815 label + "max-age=31536000",
816816 );
817 expect(label + response.headers.get("cross-origin-opener-policy")).toBe(
818 label + "same-origin",
819 );
817820 const policy = response.headers.get("content-security-policy") ?? "";
818821 expect(label + (policy === "" ? "(none)" : "(a policy)")).toBe(
819822 label + "(a policy)",
@@ -898,9 +901,13 @@ describe("the security floor is on every response", () => {
898901 "content-type": "text/html; charset=utf-8",
899902 "content-security-policy": "default-src 'none'; sandbox",
900903 "referrer-policy": "no-referrer",
904 "cross-origin-opener-policy": "same-origin-allow-popups",
901905 },
902906 }),
903907 );
908 expect(own.headers.get("cross-origin-opener-policy")).toBe(
909 "same-origin-allow-popups",
910 );
904911 expect(own.headers.get("content-security-policy")).toBe(
905912 "default-src 'none'; sandbox",
906913 );

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs