2bec8ee07597

wip: add Firefox software-rendering sandbox environment and wire firefox-release security gate; sender-context Marionette still blocked

time
2026-09-04 03:06 UTC · 1 month ago
machine
koh2
agent
claude-sonnet-5
session
Firefox gate environment repair
parent
86f2b6727bf4
9 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1playwright.config.ts+26 −0scripts/ci.ts+2 −2scripts/release-gate.test.ts+3 −3scripts/release-gate.ts+1 −1scripts/run-release-browser-gate.ts+7 −1scripts/test-firefox-sender-context.mjs+20 −1
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach Password Manager",4-  "version": "5.0.90",4+  "version": "5.0.91",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -2,7 +2,7 @@22   "manifest_version": 3,33   "minimum_chrome_version": "116",44   "name": "Peach Password Manager",5-  "version": "5.0.90",5+  "version": "5.0.91",66   "description": "Zero-knowledge password manager",77   "action": {88     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "5.0.90",3+  "version": "5.0.91",44   "description": "Zero-knowledge password manager",55   "license": "GPL-3.0-only",66   "type": "module",

view file

playwright.config.ts+26 −0
@@ -57,5 +57,31 @@5757       timeout: 120_000,5858       retries: 0,5959     },60+    // Firefox release uses software rendering and disabled content sandboxes61+    // because the CI host cannot map the hardware compositor.62+    {63+      name: "firefox-release",64+      browserName: "firefox",65+      use: {66+        headless: true,67+        launchOptions: {68+          env: {69+            ...process.env,70+            MOZ_HEADLESS: "1",71+            MOZ_DISABLE_CONTENT_SANDBOX: "1",72+            MOZ_DISABLE_RDD_SANDBOX: "1",73+            LIBGL_ALWAYS_SOFTWARE: "1",74+          },75+        },76+        firefoxUserPrefs: {77+          "gfx.webrender.all": false,78+          "gfx.webrender.software": false,79+          "media.hardware-video-decoding.force-enabled": false,80+        },81+      },82+      testMatch: /security-sentinels\.spec\.ts/,83+      timeout: 120_000,84+      retries: 0,85+    },6086   ],6187 });

view file

scripts/ci.ts+2 −2
@@ -67,10 +67,10 @@6767       }]6868     : []),6969   // PB-26-210: mandatory release browser gate.70-  // Chrome sentinels cover PB-26-201 (authorization) and PB-26-203 (storage isolation).70+  // The wrapper runs Chrome and Firefox sentinels for PB-26-201/203.7171   ...(RUN_BROWSER_GATE7272     ? [{73-        name: 'Chrome release browser gate',73+        name: 'Chrome and Firefox security sentinel gate',7474         cmd: ['bun', 'run', 'scripts/run-release-browser-gate.ts'],7575       }]7676     : []),

view file

scripts/release-gate.test.ts+3 −3
@@ -27,7 +27,7 @@2727 2828 describe("PB-26-210: gate validation — fail-closed conditions", () => {2929   it("passes a clean result with the required project and no skips", () => {30-    expect(validateGateResult({ projects: [project("chrome-release")] })).toEqual([]);30+    expect(validateGateResult({ projects: [project("chrome-release"), project("firefox-release")] })).toEqual([]);3131   });3232 3333   it("fails on zero discovered tests across all projects", () => {@@ -127,10 +127,10 @@127127   });128128 129129   it("REQUIRED_BROWSER_PROJECTS matches the projects Playwright actually defines", () => {130-    expect(REQUIRED_BROWSER_PROJECTS).toEqual(["chrome-release"]);130+    expect(REQUIRED_BROWSER_PROJECTS).toEqual(["chrome-release", "firefox-release"]);131131     const source = readFileSync(join(ROOT, "playwright.config.ts"), "utf8");132132     expect(source).toContain('name: "chrome-release"');133-    expect(source).not.toContain('name: "firefox-release"');133+    expect(source).toContain('name: "firefox-release"');134134   });135135 136136   it("release entry points force the browser gate in their CI child environment", () => {

view file

scripts/release-gate.ts+1 −1
@@ -18,7 +18,7 @@1818   projects: ProjectResult[];1919 }2020 21-export const REQUIRED_BROWSER_PROJECTS = ["chrome-release"] as const;21+export const REQUIRED_BROWSER_PROJECTS = ["chrome-release", "firefox-release"] as const;2222 2323 export interface PlaywrightJsonReport {2424   suites?: Array<{

view file

scripts/run-release-browser-gate.ts+7 −1
@@ -1,7 +1,13 @@11 import { resolveBin } from "./release-lib";22 import { assertValidPlaywrightJsonReport } from "./release-gate";33 4-const result = Bun.spawnSync([resolveBin("playwright"), "test", "--project=chrome-release", "--reporter=json"], {4+const result = Bun.spawnSync([5+  resolveBin("playwright"),6+  "test",7+  "--project=chrome-release",8+  "--project=firefox-release",9+  "--reporter=json",10+], {511   stdout: "pipe",612   stderr: "inherit",713   env: { ...process.env, PEACH_RELEASE_BROWSER_GATE: "1" },

view file

scripts/test-firefox-sender-context.mjs+20 −1
@@ -24,6 +24,8 @@2424     "/usr/local/bin/firefox",2525     "/Applications/Firefox.app/Contents/MacOS/firefox",2626     "/Applications/Firefox Nightly.app/Contents/MacOS/firefox",27+    join(process.env.HOME ?? "", "Library/Caches/ms-playwright/firefox-1538/firefox/Nightly.app/Contents/MacOS/firefox"),28+    join(process.env.HOME ?? "", "Library/Caches/ms-playwright/firefox-1511/firefox/Nightly.app/Contents/MacOS/firefox"),2729   ].filter(Boolean);2830   return candidates.find((candidate) => existsSync(candidate));2931 }@@ -126,6 +128,14 @@126128   try {127129     const xpiPath = await buildTestExtension(tempRoot);128130     const firefoxBinary = findFirefoxBinary();131+    // Match the Playwright Firefox release environment: this host cannot map132+    // the hardware compositor, and Marionette must inherit the same fallback.133+    Object.assign(process.env, {134+      MOZ_HEADLESS: "1",135+      MOZ_DISABLE_CONTENT_SANDBOX: "1",136+      MOZ_DISABLE_RDD_SANDBOX: "1",137+      LIBGL_ALWAYS_SOFTWARE: "1",138+    });129139     const port = await findFreePort();130140     driverPort = port;131141     driver = await start({@@ -134,6 +144,15 @@134144       geckoDriverVersion: "0.37.1",135145       ...(firefoxBinary ? { binary: firefoxBinary } : {}),136146       log: "error",147+      spawnOpts: {148+        env: {149+          ...process.env,150+          MOZ_HEADLESS: "1",151+          MOZ_DISABLE_CONTENT_SANDBOX: "1",152+          MOZ_DISABLE_RDD_SANDBOX: "1",153+          LIBGL_ALWAYS_SOFTWARE: "1",154+        },155+      },137156     });138157     await waitForDriver(port);139158 @@ -141,7 +160,7 @@141160       browserName: "firefox",142161       acceptInsecureCerts: true,143162       "moz:firefoxOptions": {144-        args: ["-headless"],163+        args: ["-headless", "-no-remote"],145164         ...(firefoxBinary ? { binary: firefoxBinary } : {}),146165         prefs: {147166           "browser.shell.checkDefaultBrowser": false,

view file