v1.90.0.0 feat: make browser cookie imports explicit and safe (#2964)

* fix(browse): prepare reliable cookie import wave for validation

* ci: sequence quality and behavior for validation branch

* fix(browse): isolate Windows qualification and preserve native diagnostics

* test(browse): cover cookie workflow quality and isolate Windows user paths

* test(browse): trace native member startup and initialize fresh folders

* fix(browse): keep Windows member stdin alive through EOF

* fix(browse): latch native timeouts and compare contained Edge startup

* test(browse): verify native version metadata and actual Windows argv

* test(browse): qualify Dia import on isolated macOS CI

* fix(browse): require picker origin for session mutations

* fix(browse): bound credential reads through stream completion

* test(browse): inspect owned Windows process arguments natively

* test(evals): preserve passing coverage during cookie repair reruns

* test(browse): isolate Dia qualification in a fresh macOS account

* test(browse): pass bounded integer timeouts to native Mac probes

* test(browse): distinguish Windows profile initialization from containment

* test(browse): await descendant pipe readiness before parent exit

* test(browse): initialize and restore isolated macOS Keychain state

* test(browse): initialize Windows fixture folders before qualification

* test(ci): pin the same Node runtime across Windows checks

* test(browse): distinguish native macOS browser preflight stages

* test(browse): isolate Windows descendant console lifetime

* test(browse): preserve native receipts and identify fixture lock holders

* test(browse): prepare dependency resolution before native Mac worker startup

* test(ci): include lock and close checks in native diagnostics

* test(browse): preserve native owner probe stages and subprocess deadlines

* fix(browse): classify Chromium profile-in-use exit precisely

* test(browse): retain Mac qualification evidence through cleanup failures

* test(browse): bound Mac fixture paths and retire its owned user domain

* test(browse): accept vanished fixture entries without weakening cleanup

* test(browse): identify probe-created macOS user domains safely

* test(browse): observe Mac user domains without targeting them first

* test(browse): use passive fresh-user ownership throughout Mac qualification

* test(browse): distinguish profile and registered-home Keychain lookups

* test(browse): qualify Dia under one registered account home

* test(browse): identify Dia startup and owned process-group failures

* test(browse): classify bounded Dia startup diagnostics without leaking output

* fix(test): preserve native Mac sandboxing and reap owned browser children

* fix(browse): preserve Chromium sandboxing for native profile imports

* test(browse): inspect signed Mach-O architecture without launching Xcode tools

* test(browse): sample pending Dia startup and reap on all cleanup paths

* test(browse): compare protected Dia launches in fresh Bun and Node accounts

* test(browse): inspect isolated Mac GUI readiness without browser access

* v1.90.0.0 fix: bind cookie picker actions to their document

* test: validate cookie guards and fit nested launch fixtures

* ci: configure the bundled Chromium sandbox helper

* fix(browse): classify Playwright authentication timeouts

* test: retain bounded Windows lifecycle diagnostics

* test(cso): reuse bounded NTFS precision candidates

* test(review): handle explicit preservation choices safely

* test(browse): remove owned fixture directories with explicit primitives

* test(review): distinguish descriptive reuse from edit commitments

* test: admit only the approved unscored cookie workflow refusal

* test: keep the Office Hours judge mock export-complete

* fix: keep dependency-free CI planners independent of the model SDK

* test: observe the exact holder after a native fixture unlink failure

* fix: start seeded PTY observations at owned readiness

* test: acquire identity-bound Windows deletion admission before profile resets

* test: preserve qualified Git index bits without authorizing mutations
This commit is contained in:
Garry Tan authored and GitHub committed 2026-09-25 12:06:45 -04:00
1 parent 730a1017d1
commit a84b0b5b6d
111 files changed
+14996 -1057

No files matched your search

@@ -0,0 +1,94 @@
import { createHash } from 'node:crypto';
import { createReadStream } from 'node:fs';
import path from 'node:path';
export const NATIVE_QUALIFICATION_DATA = 'browse/src/cookie-import-native-qualification.json';
export const NATIVE_BROWSER_VERSION_COMMAND = '$ErrorActionPreference = "Stop"; [Diagnostics.FileVersionInfo]::GetVersionInfo($env:GSTACK_QUALIFY_BROWSER_EXE).ProductVersion';
export const NATIVE_CODE_INPUTS = Object.freeze([
'browse/src/cookie-import-browser.ts',
'browse/src/cookie-database.ts',
'browse/src/cookie-import-native.ts',
'browse/src/cookie-import-native-integrity.ts',
'browse/src/cookie-import-native-job.ts',
'browse/src/cookie-import-native-worker.ts',
'browse/src/bun-polyfill.cjs',
'browse/scripts/build-node-server.sh',
'.github/scripts/run-cookie-native-qualification.ps1',
'browse/dist/server-node.mjs',
'browse/dist/bun-polyfill.cjs',
'browse/test/cookie-import-native.test.ts',
'browse/test/cookie-import-native-job.test.ts',
'browse/test/cookie-import-native-qualification.ts',
'browse/test/fixtures/native-cookie-process.cjs',
'browse/test/fixtures/native-cookie-launch.cjs',
'browse/test/fixtures/native-cookie-process-observer.ts',
'browse/test/fixtures/native-cookie-file-owners.ts',
'browse/test/fixtures/native-cookie-remove-fixture.cjs',
'node_modules/playwright/package.json',
'node_modules/playwright/index.js',
'node_modules/playwright-core/package.json',
'node_modules/playwright-core/index.js',
'node_modules/playwright-core/lib/bootstrap.js',
'node_modules/playwright-core/lib/coreBundle.js',
'node_modules/playwright-core/lib/utilsBundle.js',
]);
export interface NativeQualifiedBuild {
browserName: 'Chrome' | 'Chromium' | 'Brave' | 'Edge';
architecture: 'x64' | 'arm64';
windowsRelease: string;
executableSha256: string;
nodeVersion: string;
bunVersion: string;
playwrightVersion: string;
sourceHashes: Record<string, string>;
}
async function readBoundedFile(file: string, deadline: number, maximumBytes: number): Promise<Buffer> {
if (!Number.isFinite(deadline) || Date.now() >= deadline) throw new Error('native_timeout');
const cancellation = new AbortController();
const timer = setTimeout(() => cancellation.abort(), Math.max(0, deadline - Date.now()));
const stream = createReadStream(file, { signal: cancellation.signal });
const chunks: Buffer[] = [];
let size = 0;
try {
for await (const chunk of stream) {
size += chunk.length;
if (size > maximumBytes) throw new Error('native_unqualified');
chunks.push(chunk);
}
if (Date.now() >= deadline) throw new Error('native_timeout');
return Buffer.concat(chunks);
} catch (error) {
if (cancellation.signal.aborted) throw new Error('native_timeout');
throw error;
} finally {
stream.destroy();
clearTimeout(timer);
}
}
export async function readNativeQualifications(root: string, deadline: number): Promise<NativeQualifiedBuild[]> {
const builds = JSON.parse((await readBoundedFile(path.join(root, NATIVE_QUALIFICATION_DATA), deadline, 1024 * 1024)).toString('utf8'));
if (!Array.isArray(builds) || builds.some(build => !build || typeof build !== 'object')) throw new Error('native_unqualified');
return builds;
}
export async function hashNativeFile(file: string, deadline: number): Promise<string> {
return createHash('sha256').update(await readBoundedFile(file, deadline, 64 * 1024 * 1024)).digest('hex');
}
export async function nativeCodeHashes(root: string, deadline: number): Promise<Record<string, string>> {
const hashes: Record<string, string> = {};
for (const file of NATIVE_CODE_INPUTS) hashes[file] = await hashNativeFile(path.join(root, file), deadline);
return hashes;
}
export function nativeCodeMatches(expected: unknown, actual: Record<string, string>): boolean {
if (!expected || typeof expected !== 'object' || Array.isArray(expected) || Object.keys(expected).length !== NATIVE_CODE_INPUTS.length) return false;
return NATIVE_CODE_INPUTS.every(file => {
const hash = (expected as Record<string, unknown>)[file];
return typeof hash === 'string' && /^[0-9a-f]{64}$/.test(hash) && hash === actual[file];
});
}