Files
gstack/browse/test/cookie-fixture-delete-lease.test.ts
T
Garry Tan a84b0b5b6d 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
2026-09-25 12:06:45 -04:00

257 lines
16 KiB
TypeScript

import { afterAll, expect, test } from 'bun:test';
import { chmodSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, renameSync, rmSync, rmdirSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { spawn, type ChildProcess } from 'node:child_process';
import { dlopen, FFIType, ptr } from 'bun:ffi';
import { createFixtureDeleteLease, deleteWithFixtureLease, FixtureDeleteError, type FixtureDeleteBackend } from './fixtures/native-cookie-delete-lease';
import { nativeCookieEnvironment } from '../src/cookie-import-native-worker';
const root = mkdtempSync(path.join(tmpdir(), 'delete-lease-'));
afterAll(() => rmSync(root, { recursive: true, force: true }));
const identity = { dev: 7n, ino: 9007199254740993n, mode: 0o100666n };
function model() {
let time = 0;
const calls: string[] = [];
const backend: FixtureDeleteBackend = {
open: () => { calls.push('open'); return 42; },
identity: () => { calls.push('identity'); return { ...identity, attributes: 0x80, filesystem: 'NTFS' }; },
dispose: () => { calls.push('dispose'); },
close: () => { calls.push('close'); },
absent: () => { calls.push('absent'); return true; },
};
const verify = () => { calls.push('verify'); };
const clock = { now: () => time, wait: (ms: number) => { calls.push(`wait:${ms}`); time += ms; } };
return { backend, calls, verify, clock, advance: (ms: number) => { time += ms; } };
}
test('an admitted identity is deleted once, closed, and checked for absence in that order', () => {
const f = model();
expect(deleteWithFixtureLease('owned', identity, 100, f.verify, f.backend, f.clock)).toEqual({ admissionProbes: 1, waitedMs: 0 });
expect(f.calls).toEqual(['verify', 'open', 'identity', 'verify', 'dispose', 'close', 'absent']);
});
test('sharing admission can settle inside the same deadline without retrying deletion', () => {
const f = model(); let attempts = 0;
f.backend.open = () => { f.calls.push('open'); if (++attempts < 3) throw new FixtureDeleteError('admission', 'owned', 32); return 42; };
expect(deleteWithFixtureLease('owned', identity, 100, f.verify, f.backend, f.clock)).toEqual({ admissionProbes: 3, waitedMs: 40 });
expect(f.calls.filter(call => call === 'dispose')).toHaveLength(1);
expect(f.calls.filter(call => call === 'close')).toHaveLength(1);
});
test('persistent sharing keeps its first error and performs zero deletes', () => {
const f = model(); const first = new FixtureDeleteError('admission', 'owned', 32); let attempts = 0;
f.backend.open = () => { attempts++; throw attempts === 1 ? first : new FixtureDeleteError('admission', 'owned', 32); };
let failure: unknown;
try { deleteWithFixtureLease('owned', identity, 40, f.verify, f.backend, f.clock); } catch (error) { failure = error; }
expect(failure).toBe(first);
expect(attempts).toBe(2);
expect(f.calls).not.toContain('dispose');
expect(f.calls).not.toContain('close');
});
test.each([2, 3, 5, 33, 50, 87])('admission error %s is never polled or deleted', code => {
const f = model(); const original = new FixtureDeleteError('admission', 'owned', code);
f.backend.open = () => { f.calls.push('open'); throw original; };
let failure: unknown;
try { deleteWithFixtureLease('owned', identity, 100, f.verify, f.backend, f.clock); } catch (error) { failure = error; }
expect(failure).toBe(original);
expect(f.calls).toEqual(['verify', 'open']);
});
test.each(['volume', 'inode', 'ReFS', 'readonly', 'directory', 'reparse', 'ancestor'])(
'a rejected %s identity closes its handle without deleting', defect => {
const f = model();
f.backend.identity = () => ({ ...identity, dev: defect === 'volume' ? 8n : identity.dev,
ino: defect === 'inode' ? 9007199254740992n : identity.ino, filesystem: defect === 'ReFS' ? 'ReFS' : 'NTFS',
attributes: { readonly: 1, directory: 16, reparse: 1024 }[defect] ?? 0x80 });
let checks = 0;
const verify = () => { if (++checks === 2 && defect === 'ancestor') throw new Error('Owned ancestor changed'); };
expect(() => deleteWithFixtureLease('owned', identity, 100, verify, f.backend, f.clock)).toThrow();
expect(f.calls.filter(call => call === 'close')).toHaveLength(1);
expect(f.calls).not.toContain('dispose');
});
test.each(['before_open', 'after_verify', 'after_identity', 'after_revalidation'])(
'expiration %s never grants late deletion', stage => {
const f = model(); let checks = 0;
if (stage === 'before_open') f.advance(100);
const verify = () => { checks++; if (stage === 'after_verify' && checks === 1 || stage === 'after_revalidation' && checks === 2) f.advance(100); };
const identify = f.backend.identity;
f.backend.identity = (handle, file) => { const result = identify(handle, file); if (stage === 'after_identity') f.advance(100); return result; };
expect(() => deleteWithFixtureLease('owned', identity, 100, verify, f.backend, f.clock)).toThrow('deadline');
expect(f.calls).not.toContain('dispose');
expect(f.calls.filter(call => call === 'close')).toHaveLength(stage.startsWith('before') || stage === 'after_verify' ? 0 : 1);
});
test('a disposition failure is not retried and survives close', () => {
const f = model(); const original = new FixtureDeleteError('disposition', 'owned', 32);
f.backend.dispose = () => { f.calls.push('dispose'); throw original; };
let failure: unknown;
try { deleteWithFixtureLease('owned', identity, 100, f.verify, f.backend, f.clock); } catch (error) { failure = error; }
expect(failure).toBe(original);
expect(f.calls.filter(call => call === 'dispose')).toHaveLength(1);
expect(f.calls.at(-1)).toBe('close');
expect(f.calls.some(call => call.startsWith('wait'))).toBe(false);
});
test.each(['close', 'absence'])('%s failure never reports completed removal', stage => {
const f = model();
if (stage === 'close') f.backend.close = () => { throw new FixtureDeleteError('close', 'owned', 6); };
else f.backend.absent = () => false;
expect(() => deleteWithFixtureLease('owned', identity, 100, f.verify, f.backend, f.clock)).toThrow(stage);
});
test('the actual Windows binding uses the checked handle, NTFS identity, and Ex flags without a fallback', () => {
const source = readFileSync(path.join(import.meta.dir, 'fixtures/native-cookie-delete-lease.ts'), 'utf8');
const start = source.indexOf('export function createFixtureDeleteLease(');
expect(start).toBeGreaterThan(0);
const body = new Bun.Transpiler({ loader: 'ts' }).transformSync(source.slice(start).replace('export function', 'function'));
const calls: string[] = [];
const api = {
CreateFileW(name: Buffer, access: number, share: number, security: unknown, creation: number, flags: number, template: number) {
calls.push('open');
expect(name.toString('utf16le')).toBe('namespaced-owned\0');
expect([access, share, security, creation, flags, template]).toEqual([0x10080, 3, null, 3, 0x00200000, 0]);
return 42;
},
GetLastError() { calls.push('last-error'); return 0; },
GetFileInformationByHandle(handle: number, info: Buffer) {
calls.push('identity'); expect(handle).toBe(42); expect(info.length).toBe(52);
info.writeUInt32LE(0x80, 0); info.writeUInt32LE(Number(identity.dev), 28);
info.writeUInt32LE(Number(identity.ino >> 32n), 44); info.writeUInt32LE(Number(identity.ino & 0xffffffffn), 48);
return 1;
},
GetVolumeInformationByHandleW(handle: number, name: unknown, length: number, serial: unknown, component: unknown, flags: unknown, filesystem: Buffer, size: number) {
calls.push('filesystem'); expect([handle, name, length, serial, component, flags, size]).toEqual([42, null, 0, null, null, null, 64]);
expect(filesystem.length).toBe(128); filesystem.write('NTFS\0', 'utf16le'); return 1;
},
SetFileInformationByHandle(handle: number, kind: number, flags: Buffer, size: number) {
calls.push('dispose'); expect([handle, kind, flags.length, flags.readUInt32LE(0), size]).toEqual([42, 21, 4, 3, 4]); return 1;
},
CloseHandle(handle: number) { calls.push('close'); expect(handle).toBe(42); return 1; },
};
const factory = new Function('process', 'dlopen', 'FFIType', 'ptr', 'unlinkSync', 'lstatSync', 'toNamespacedPath',
'FixtureDeleteError', 'deleteWithFixtureLease', 'leaseClock', `${body}\nreturn createFixtureDeleteLease;`)(
{ platform: 'win32' }, (name: string) => { expect(name).toBe('kernel32.dll'); return { symbols: api, close() { calls.push('unload'); } }; },
{ ptr: 'ptr', u32: 'u32', u64: 'u64', i32: 'i32' }, (value: Buffer) => value,
() => { throw new Error('Regular files must not use a path-based fallback'); },
() => { calls.push('absence'); throw Object.assign(new Error('Absent'), { code: 'ENOENT' }); },
(file: string) => { expect(file).toBe('owned'); return 'namespaced-owned'; },
FixtureDeleteError, deleteWithFixtureLease, { now: () => 0, wait: () => { throw new Error('Unexpected wait'); } });
const lease = factory(100);
lease.unlink('owned', identity, () => { calls.push('verify'); });
lease.close();
expect(calls).toEqual(['verify', 'open', 'last-error', 'identity', 'filesystem', 'verify', 'dispose', 'close', 'absence', 'unload']);
});
test.skipIf(process.platform !== 'win32')('the native lease refuses a persistent no-delete-sharing holder', () => {
const file = path.join(root, 'persistent'); writeFileSync(file, 'fixture-only');
const expected = lstatSync(file, { bigint: true });
const kernel = dlopen('kernel32.dll', {
CreateFileW: { args: [FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.u64], returns: FFIType.u64 },
CloseHandle: { args: [FFIType.u64], returns: FFIType.i32 },
});
const name = Buffer.from(file + '\0', 'utf16le');
const handle = kernel.symbols.CreateFileW(ptr(name), 0x80000000, 3, null, 3, 0x80, 0);
const lease = createFixtureDeleteLease(performance.now() + 1_000);
try {
expect(BigInt(handle)).not.toBe(0xffffffffffffffffn);
expect(BigInt(handle)).not.toBe(0n);
let failure: unknown;
try { lease.unlink(file, expected, () => {}); } catch (error) { failure = error; }
expect(failure).toBeInstanceOf(FixtureDeleteError);
expect(failure).toMatchObject({ stage: 'admission', win32Error: 32, code: 'EBUSY' });
expect(readFileSync(file, 'utf8')).toBe('fixture-only');
expect(lstatSync(file, { bigint: true }).ino).toBe(expected.ino);
} finally { lease.close(); if (BigInt(handle) !== 0xffffffffffffffffn && BigInt(handle) !== 0n) kernel.symbols.CloseHandle(handle); kernel.close(); }
});
test.skipIf(process.platform !== 'win32')('a compatible reader retains its data while the admitted delete removes the namespace', () => {
const directory = mkdtempSync(path.join(root, 'reader-'));
const file = path.join(directory, 'data'); writeFileSync(file, 'fixture-only');
const expected = lstatSync(file, { bigint: true });
const kernel = dlopen('kernel32.dll', {
CreateFileW: { args: [FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.u64], returns: FFIType.u64 },
ReadFile: { args: [FFIType.u64, FFIType.ptr, FFIType.u32, FFIType.ptr, FFIType.ptr], returns: FFIType.i32 },
CloseHandle: { args: [FFIType.u64], returns: FFIType.i32 },
});
const name = Buffer.from(file + '\0', 'utf16le');
const handle = kernel.symbols.CreateFileW(ptr(name), 0x80000000, 7, null, 3, 0x80, 0);
const lease = createFixtureDeleteLease(performance.now() + 2_000);
try {
expect(BigInt(handle)).not.toBe(0xffffffffffffffffn);
expect(BigInt(handle)).not.toBe(0n);
let checks = 0;
lease.unlink(file, expected, () => { if (++checks === 2) expect(() => renameSync(file, path.join(directory, 'moved'))).toThrow(); });
expect(checks).toBe(2);
expect(existsSync(file)).toBe(false);
rmdirSync(directory);
const buffer = Buffer.alloc(32), bytes = Buffer.alloc(4);
expect(kernel.symbols.ReadFile(handle, ptr(buffer), buffer.length, ptr(bytes), null)).toBe(1);
expect(buffer.subarray(0, bytes.readUInt32LE()).toString()).toBe('fixture-only');
} finally { lease.close(); if (BigInt(handle) !== 0xffffffffffffffffn && BigInt(handle) !== 0n) kernel.symbols.CloseHandle(handle); kernel.close(); }
});
test.skipIf(process.platform !== 'win32')('an independently released holder admits one identity-bound deletion', async () => {
const directory = mkdtempSync(path.join(root, 'released-'));
const file = path.join(directory, 'held'), ready = path.join(directory, 'ready'), release = path.join(directory, 'release');
writeFileSync(file, 'fixture-only'); const expected = lstatSync(file, { bigint: true });
const script = `
const { dlopen, FFIType, ptr } = await import('bun:ffi');
const { existsSync, writeFileSync } = await import('node:fs');
const api = dlopen('kernel32.dll', {
CreateFileW: { args: [FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.ptr, FFIType.u32, FFIType.u32, FFIType.u64], returns: FFIType.u64 },
CloseHandle: { args: [FFIType.u64], returns: FFIType.i32 },
});
const name = Buffer.from(${JSON.stringify(file)} + '\\0', 'utf16le');
const handle = api.symbols.CreateFileW(ptr(name), 0x80000000, 3, null, 3, 0x80, 0);
if (BigInt(handle) === 0xffffffffffffffffn || BigInt(handle) === 0n) throw new Error('Holder open failed');
try {
writeFileSync(${JSON.stringify(ready)}, 'ready');
while (!existsSync(${JSON.stringify(release)})) await Bun.sleep(10);
} finally { if (!api.symbols.CloseHandle(handle)) throw new Error('Holder close failed'); api.close(); }
`;
const child: ChildProcess = spawn(process.execPath, ['--no-env-file', '--no-install', '--config=NUL', '-e', script],
{ env: nativeCookieEnvironment(process.env), stdio: 'ignore', windowsHide: true });
let spawnError: Error | undefined;
child.once('error', error => { spawnError = error; });
const closed = new Promise<void>(resolve => child.once('close', () => resolve()));
const timer = setTimeout(() => child.kill(), 5_000);
let lease: ReturnType<typeof createFixtureDeleteLease> | undefined;
try {
const deadline = performance.now() + 3_000;
while (!existsSync(ready) && child.exitCode === null && !spawnError && performance.now() < deadline) await Bun.sleep(10);
expect(spawnError).toBeUndefined();
expect(existsSync(ready)).toBe(true);
let blocked = 0;
lease = createFixtureDeleteLease(performance.now() + 2_000, () => { blocked++; writeFileSync(release, 'release'); });
lease.unlink(file, expected, () => {});
expect(blocked).toBe(1);
expect(existsSync(file)).toBe(false);
await closed;
expect(child.exitCode).toBe(0);
} finally { clearTimeout(timer); lease?.close(); child.kill(); await closed; }
}, 10_000);
test.skipIf(process.platform !== 'win32').each(['identity', 'readonly', 'reparse'])(
'the native lease refuses %s without removing the file or target', defect => {
const directory = mkdtempSync(path.join(root, 'refused-'));
const file = path.join(directory, 'data'); writeFileSync(file, 'fixture-only');
const expected = lstatSync(file, { bigint: true });
const destination = path.join(directory, 'destination');
if (defect === 'readonly') chmodSync(file, 0o444);
if (defect === 'reparse') {
unlinkSync(file); mkdirSync(destination); writeFileSync(path.join(destination, 'preserved'), 'fixture-only');
symlinkSync(destination, file, 'junction');
}
const lease = createFixtureDeleteLease(performance.now() + 2_000);
try {
expect(() => lease.unlink(file, defect === 'identity' ? { ...expected, ino: expected.ino + 1n } : expected, () => {})).toThrow();
expect(existsSync(file)).toBe(true);
if (defect === 'reparse') expect(readFileSync(path.join(destination, 'preserved'), 'utf8')).toBe('fixture-only');
else expect(readFileSync(file, 'utf8')).toBe('fixture-only');
} finally { lease.close(); if (defect === 'readonly') chmodSync(file, 0o666); }
});