From 75c3e0e925df2d47ba3af86be9f0e2004d5424f0 Mon Sep 17 00:00:00 2001 From: garrytan Date: Tue, 3 Nov 2026 14:45:14 +0000 Subject: [PATCH] fix(deps): Playwright 1.64.1 (Chromium 156) for CVE-2026-106378 (#3907) Playwright 1.62.1 bundled Chromium 151.0.7922.34, which lacks the macOS GPU-process TCC fix (Chromium 0c2f3e54b265, crbug.com/507596239). 1.64.1 bundles 156.0.8078.4, which has it. The playwright-core patch is regenerated from our 1.62.1 patch: the launch windowsHide and TcpPortRandomizationWin hunks carry over unchanged; the taskkill hunk is dropped because 1.64 passes windowsHide there upstream. Co-authored-by: merit-blake <265362871+merit-blake@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- BROWSER.md | 2 +- browse/test/cookie-import-native-qualification.ts | 2 +- browse/test/cookie-import-native.test.ts | 2 +- browse/test/playwright-core-patch.test.ts | 14 +++++++------- browse/test/security-audit-r2.test.ts | 2 +- bun.lock | 10 ++++------ package.json | 4 ++-- ...@1.62.1.patch => playwright-core@1.64.1.patch} | 15 +++------------ scripts/typecheck-test-baseline.json | 1 - test/setup-playwright-platform.test.ts | 4 ++-- 10 files changed, 22 insertions(+), 34 deletions(-) rename patches/{playwright-core@1.62.1.patch => playwright-core@1.64.1.patch} (56%) diff --git a/BROWSER.md b/BROWSER.md index 69be0410d..5b773d782 100644 --- a/BROWSER.md +++ b/BROWSER.md @@ -1380,7 +1380,7 @@ browser's trust store and updates. eight eMudhra intermediate CAs by CRLSet on 2026-10-17, the day their key compromise was reported, and scheduled the removal of their root, emSign ECC Root CA - G3, from the Chrome Root Store for 2026-10-27. The root store built -into gstack's pinned Playwright 1.62.1 (Chromium 151) still trusts that root, so +into gstack's pinned Playwright 1.64.1 (Chromium 156) still trusts that root, so the headless browser accepts certificates those intermediates issued. That lasts until gstack moves to the first Playwright release whose Chromium is 158.0.8115.0 or later, the first Chrome for Testing build measured to refuse diff --git a/browse/test/cookie-import-native-qualification.ts b/browse/test/cookie-import-native-qualification.ts index 8c703c9fe..5ba173c60 100644 --- a/browse/test/cookie-import-native-qualification.ts +++ b/browse/test/cookie-import-native-qualification.ts @@ -66,7 +66,7 @@ const nodeInfo = JSON.parse(nodeProbe.stdout); if (nodeInfo.architecture !== process.arch || !['x64', 'arm64'].includes(process.arch)) incomplete('runtime_architecture_mismatch'); const require = createRequire(import.meta.url); const playwrightVersion = require('playwright/package.json').version; -if (playwrightVersion !== '1.62.1') incomplete('playwright_1_62_1_required'); +if (playwrightVersion !== '1.64.1') incomplete('playwright_1_64_1_required'); const versionProbe = spawnSync(path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe'), [ '-NoProfile', '-NonInteractive', '-Command', NATIVE_BROWSER_VERSION_COMMAND, ], { env: { ...environment, GSTACK_QUALIFY_BROWSER_EXE: executable }, encoding: 'utf8', timeout: 10_000, windowsHide: true }); diff --git a/browse/test/cookie-import-native.test.ts b/browse/test/cookie-import-native.test.ts index 11c462355..013d00da1 100644 --- a/browse/test/cookie-import-native.test.ts +++ b/browse/test/cookie-import-native.test.ts @@ -213,7 +213,7 @@ async function qualifiedAdapterFixture() { const receipt = { browserName: 'Edge', architecture: runtime.architecture, windowsRelease: release(), executableSha256: '0'.repeat(64), nodeVersion: runtime.version, bunVersion: Bun.version, - playwrightVersion: '1.62.1', sourceHashes, + playwrightVersion: '1.64.1', sourceHashes, }; const virtualRoot = `C:\\gstack-${path.basename(fixture)}`; const windowsEnv = { LOCALAPPDATA: `${virtualRoot}\\Local`, PROGRAMFILES: `${virtualRoot}\\Apps`, 'PROGRAMFILES(X86)': `${virtualRoot}\\Apps32` }; diff --git a/browse/test/playwright-core-patch.test.ts b/browse/test/playwright-core-patch.test.ts index c160dbe00..50381324a 100644 --- a/browse/test/playwright-core-patch.test.ts +++ b/browse/test/playwright-core-patch.test.ts @@ -1,8 +1,10 @@ /** - * #2160/#1989: playwright-core is bun-patched to pass windowsHide at its two + * #2160/#1989: playwright-core must pass windowsHide at its two * Windows-visible child_process sites — the browser launch spawn (Node * defaults windowsHide to FALSE for spawn, so browser children could flash a - * console window) and the force-kill taskkill spawnSync. This is the repo's + * console window) and the force-kill taskkill spawnSync. The launch site is + * bun-patched; the taskkill site is upstream since Playwright 1.64 (#3907), + * so only the installed-tree check below covers it. This is the repo's * first patchedDependencies entry; these static checks pin the three-legged * coherence (patch file ↔ package.json ↔ installed tree) so a playwright * bump that forgets to re-target the patch fails CI instead of silently @@ -35,15 +37,13 @@ describe('playwright-core windowsHide patch (#2160, #1989)', () => { expect(pkg.patchedDependencies[key]).toBe(`patches/playwright-core@${version}.patch`); }); - test('the patch file exists and carries both windowsHide sites', () => { + test('the patch file exists and carries the launch windowsHide site', () => { const version = installedPlaywrightCoreVersion(); const patchPath = path.join(ROOT, 'patches', `playwright-core@${version}.patch`); expect(fs.existsSync(patchPath)).toBe(true); const patch = fs.readFileSync(patchPath, 'utf-8'); // Launch spawnOptions site. expect(patch).toContain('+ windowsHide: true,'); - // taskkill force-kill site. - expect(patch).toContain('shell: true, windowsHide: true'); }); test('bun.lock is coherent: the lockfile records the patched dependency', () => { @@ -63,10 +63,10 @@ describe('playwright-core windowsHide patch (#2160, #1989)', () => { // Windows Free Tests 40656299153 and 41056150287 (cookie-picker-ui, serial retry passed): goto failed with // net::ERR_NO_BUFFER_SPACE on 127.0.0.1. Chromium's TcpPortRandomizationWin raises WSAENOBUFS with ports free; -// microsoft/playwright#43605 disables it in 1.65, and the 1.62.1 patch backports it; drop this block at 1.65. +// microsoft/playwright#43605 disables it in 1.65, and the 1.64.1 patch backports it; drop this block at 1.65. describe('playwright-core TcpPortRandomizationWin backport (microsoft/playwright#43605)', () => { test('the patch and the installed tree disable the feature in the --disable-features list', () => { - expect(fs.readFileSync(path.join(ROOT, 'patches', 'playwright-core@1.62.1.patch'), 'utf-8')).toContain('+ "TcpPortRandomizationWin"'); + expect(fs.readFileSync(path.join(ROOT, 'patches', `playwright-core@${installedPlaywrightCoreVersion()}.patch`), 'utf-8')).toContain('+ "TcpPortRandomizationWin"'); const bundle = fs.readFileSync(path.join(ROOT, 'node_modules', 'playwright-core', 'lib', 'coreBundle.js'), 'utf-8'); const list = bundle.slice(bundle.indexOf('disabledFeatures = ['), bundle.indexOf('].filter(Boolean);', bundle.indexOf('disabledFeatures = ['))); expect(list).toContain('"TcpPortRandomizationWin"'); diff --git a/browse/test/security-audit-r2.test.ts b/browse/test/security-audit-r2.test.ts index cd123f5db..9db45e9a1 100644 --- a/browse/test/security-audit-r2.test.ts +++ b/browse/test/security-audit-r2.test.ts @@ -251,7 +251,7 @@ describe('cookie-import domain validation', () => { reset: 'not_requested', verification: { verified: false, reason: 'not_requested' }, message: 'Cookie copy complete.', }); let currentUrl = 'https://example.test'; - const page = { url: () => currentUrl, isClosed: () => false }; + const page = { url: () => currentUrl, isClosed: () => false } as any; const session = { getPage: () => page, getActiveFrameOrPage: () => page, getFrame: () => null } as any; const manager = { trackCookieImportDomains() {} } as any; try { diff --git a/bun.lock b/bun.lock index a32b356bc..d3d3ffbb5 100644 --- a/bun.lock +++ b/bun.lock @@ -13,7 +13,7 @@ "html-to-docx": "1.8.0", "html-to-vdom": "0.7.0", "marked": "^18.1.1", - "playwright": "^1.62.1", + "playwright": "^1.64.1", "sharp": "^0.35.4", "socks": "^2.8.10", "xterm": "^5.3.0", @@ -28,7 +28,7 @@ }, }, "patchedDependencies": { - "playwright-core@1.62.1": "patches/playwright-core@1.62.1.patch", + "playwright-core@1.64.1": "patches/playwright-core@1.64.1.patch", }, "overrides": { "@hono/node-server": "2.1.6", @@ -335,8 +335,6 @@ "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], - "fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="], - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], @@ -469,9 +467,9 @@ "platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="], - "playwright": ["playwright@1.62.1", "", { "dependencies": { "playwright-core": "1.62.1" }, "optionalDependencies": { "fsevents": "2.3.2" }, "bin": { "playwright": "cli.js" } }, "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg=="], + "playwright": ["playwright@1.64.1", "", { "dependencies": { "playwright-core": "1.64.1" }, "bin": { "playwright": "cli.js" } }, "sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA=="], - "playwright-core": ["playwright-core@1.62.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw=="], + "playwright-core": ["playwright-core@1.64.1", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ=="], "prettier": ["prettier@3.9.10", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA=="], diff --git a/package.json b/package.json index d6c71b54c..be65a5ebe 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "html-to-docx": "1.8.0", "html-to-vdom": "0.7.0", "marked": "^18.1.1", - "playwright": "^1.62.1", + "playwright": "^1.64.1", "sharp": "^0.35.4", "socks": "^2.8.10", "xterm": "^5.3.0", @@ -115,6 +115,6 @@ "sharp": "0.35.5" }, "patchedDependencies": { - "playwright-core@1.62.1": "patches/playwright-core@1.62.1.patch" + "playwright-core@1.64.1": "patches/playwright-core@1.64.1.patch" } } diff --git a/patches/playwright-core@1.62.1.patch b/patches/playwright-core@1.64.1.patch similarity index 56% rename from patches/playwright-core@1.62.1.patch rename to patches/playwright-core@1.64.1.patch index e301f1bf0..41b76c9e6 100644 --- a/patches/playwright-core@1.62.1.patch +++ b/patches/playwright-core@1.64.1.patch @@ -1,8 +1,8 @@ diff --git a/lib/coreBundle.js b/lib/coreBundle.js -index d9041dd0f009072511a7d808dec83121ff29e127..542acd31dd7e8ae084de952dc42ec63d0caba849 100644 +index 6893f1cf133efec2c5dccc35c77cc7a53870ccbc..7990e202c5b574ca5ea4c3e73c15c2da43a5b980 100644 --- a/lib/coreBundle.js +++ b/lib/coreBundle.js -@@ -8906,6 +8906,10 @@ async function launchProcess(options) { +@@ -10031,6 +10031,10 @@ async function launchProcess(options) { env: options.env, cwd: options.cwd, shell: options.shell, @@ -13,16 +13,7 @@ index d9041dd0f009072511a7d808dec83121ff29e127..542acd31dd7e8ae084de952dc42ec63d stdio }; const spawnedProcess = childProcess.spawn(options.command, options.args || [], spawnOptions); -@@ -8985,7 +8989,7 @@ async function launchProcess(options) { - options.log(`[pid=${spawnedProcess.pid}] `); - try { - if (process.platform === "win32") { -- const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true }); -+ const taskkillProcess = childProcess.spawnSync(`taskkill /pid ${spawnedProcess.pid} /T /F`, { shell: true, windowsHide: true }); - const [stdout2, stderr2] = [taskkillProcess.stdout.toString(), taskkillProcess.stderr.toString()]; - if (stdout2) - options.log(`[pid=${spawnedProcess.pid}] taskkill stdout: ${stdout2}`); -@@ -34634,7 +34638,10 @@ var init_chromiumSwitches = __esm({ +@@ -36204,7 +36208,10 @@ var init_chromiumSwitches = __esm({ // Disables forced sign-in in Edge. "msForceBrowserSignIn", // Disables updating the preferred version in LaunchServices preferences on mac. diff --git a/scripts/typecheck-test-baseline.json b/scripts/typecheck-test-baseline.json index 4d1c9dc9b..16f3bdea4 100644 --- a/scripts/typecheck-test-baseline.json +++ b/scripts/typecheck-test-baseline.json @@ -41,7 +41,6 @@ "browse/test/handoff.test.ts\tTS2554\tExpected 4 arguments, but got 3.": 2, "browse/test/pair-agent-e2e.test.ts\tTS2532\tObject is possibly 'undefined'.": 1, "browse/test/pty-inject-scan.test.ts\tTS2345\tArgument of type '{ Authorization?: undefined; } | { Authorization: string; }' is not assignable to parameter of type 'Record'. Type '{ Authorization?: undefined; }' is not assignable to type 'Record'. Property 'Authorization' is incompatible with index signature. Type 'undefined' is not assignable to type 'string'.": 1, - "browse/test/security-audit-r2.test.ts\tTS2769\tNo overload matches this call. The last overload gave the following error. Type '{ isClosed: () => boolean; url: () => string; }' is missing the following properties from type 'Page': evaluate, evaluateHandle, addInitScript, $, and 110 more.": 1, "browse/test/server-auth.test.ts\tTS2345\tArgument of type '{ Host: string; Origin: string; } | { Host?: undefined; Origin: string; } | { Authorization: string; Host: string; Origin?: undefined; }' is not assignable to parameter of type 'Record'. Type '{ Host?: undefined; Origin: string; }' is not assignable to type 'Record'. Property 'Host' is incompatible with index signature. Type 'undefined' is not assignable to type 'string'.": 1, "browse/test/server-factory.test.ts\tTS2769\tNo overload matches this call. The last overload gave the following error. Argument of type '\"local\"' is not assignable to parameter of type 'undefined'.": 1, "browse/test/server-proxy-fail-fast.test.ts\tTS2339\tProperty 'subarray' does not exist on type 'string | NonSharedBuffer'. Property 'subarray' does not exist on type 'string'.": 1, diff --git a/test/setup-playwright-platform.test.ts b/test/setup-playwright-platform.test.ts index d366a906a..5a736adf3 100644 --- a/test/setup-playwright-platform.test.ts +++ b/test/setup-playwright-platform.test.ts @@ -48,8 +48,8 @@ describe('Ubuntu fallback retains CPU architecture', () => { cwd: ROOT, env: { PATH: process.env.PATH, HOME: temp(), PLAYWRIGHT_HOST_PLATFORM_OVERRIDE: override }, encoding: 'utf8', timeout: 20_000, }); expect(dryRun.status, dryRun.stderr).toBe(0); - expect(dryRun.stdout).toContain(expected === 'arm64' ? 'chromium-linux-arm64.zip' : 'chrome-linux64.zip'); - expect(dryRun.stdout).not.toContain(expected === 'arm64' ? 'chrome-linux64.zip' : 'chromium-linux-arm64.zip'); + expect(dryRun.stdout).toContain(expected === 'arm64' ? 'chrome-linux-arm64.zip' : 'chrome-linux64.zip'); + expect(dryRun.stdout).not.toContain(expected === 'arm64' ? 'chrome-linux64.zip' : 'chrome-linux-arm64.zip'); }, 30_000); } for (const cpu of ['riscv64', 'i686', 'unknown']) test(`${cpu} reports unsupported instead of installing x64`, () => {