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
2026-09-25 12:06:45 -04:00
committed by GitHub
parent 730a1017d1
commit a84b0b5b6d
111 changed files with 14996 additions and 1057 deletions
@@ -0,0 +1,13 @@
{
"schema_version": 1,
"test_name": "setup-browser-cookies/SKILL.md workflow",
"prompt_sha256": "7f7f76f49912818d51f25c86d686aab6ad2c95ccdea60ac912c1e4c8b6936e5f",
"prompt_bytes": 10159,
"model": "claude-fable-5-1",
"max_tokens": 8192,
"thresholds": { "clarity": 4, "completeness": 3, "actionability": 4 },
"approved_by": "garrytan",
"approved_at": "2026-09-24",
"approval_url": "https://github.com/garrytan/gstack/pull/2964#issuecomment-5822514476",
"reason": "Maintainer-approved manual review of this exact cookie workflow after empty provider refusals. No automated quality score or pass credit; other errors and changed inputs remain blocking."
}
+22
View File
@@ -0,0 +1,22 @@
# Cookie workflow manual-review exception
`cookie-workflow-manual-review.json` records the maintainer's approval for one
exact cookie-workflow judge request. It is not generated content. Do not update
its hash, model, budget, thresholds, or provenance just to make a changed test
pass; a changed request needs a new explicit review and approval.
The ordinary judge request still runs. Only an explicit provider refusal with
complete request/response identifiers, zero output tokens, and no text blocks
can use this approval. Low scores, malformed output or evidence, other errors,
timeouts, and late or superseded attempts remain failures. Every other case
remains subject to its existing gate.
Manual acceptance is first-attempt-only: a retry refusal cannot erase an earlier
scored failure, timeout, or other error. Normal configured retries are unchanged.
The collector preserves `passed: false`, `execution: executed`, the refusal,
and the manual approval, without a score or score-cache receipt. Reports count
manual acceptance separately from automated passes and failures; “executed”
counts the actual provider request, not a completed scored evaluation. Historical
records retain that distinction. CI also checks manual claims against the
current source and committed approval before accepting them.
+138
View File
@@ -0,0 +1,138 @@
#define _DARWIN_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#ifdef __APPLE__
#include <ApplicationServices/ApplicationServices.h>
#include <Security/AuthSession.h>
#endif
struct root_fact {
const char *state;
const char *kind;
int owner_matches;
int ancestor_blocked;
};
static const char *kind_of(mode_t mode) {
if (S_ISDIR(mode)) return "directory";
if (S_ISREG(mode)) return "file";
if (S_ISLNK(mode)) return "symlink";
return "other";
}
static struct root_fact inspect_root(int home, const char *relative, uid_t owner) {
struct root_fact fact = {"unavailable", NULL, -1, 0};
char components[256];
if (strlen(relative) >= sizeof(components)) return fact;
memcpy(components, relative, strlen(relative) + 1);
int directory = dup(home);
if (directory < 0) return fact;
char *state = NULL;
char *component = strtok_r(components, "/", &state);
while (component) {
char *next = strtok_r(NULL, "/", &state);
struct stat before;
if (fstatat(directory, component, &before, AT_SYMLINK_NOFOLLOW) != 0) {
if (errno == ENOENT) fact.state = "absent";
break;
}
fact.kind = kind_of(before.st_mode);
fact.owner_matches = before.st_uid == owner;
if (!next) { fact.state = "present"; break; }
if (!S_ISDIR(before.st_mode) || before.st_uid != owner) { fact.ancestor_blocked = 1; break; }
int child = openat(directory, component, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_NONBLOCK);
struct stat after;
if (child < 0) { fact.ancestor_blocked = 1; break; }
if (fstat(child, &after) != 0 || after.st_dev != before.st_dev || after.st_ino != before.st_ino
|| !S_ISDIR(after.st_mode) || after.st_uid != owner) {
close(child);
fact.ancestor_blocked = 1;
break;
}
close(directory);
directory = child;
fact.kind = NULL;
fact.owner_matches = -1;
component = next;
}
close(directory);
return fact;
}
static const char *boolean_or_null(int value) {
return value < 0 ? "null" : value ? "true" : "false";
}
static void print_browser_roots(int home, uid_t owner) {
const char *names[] = {"chrome", "chromium", "arc", "dia", "comet", "brave", "edge", "safari", "cookies"};
const char *paths[] = {"Library/Application Support/Google/Chrome", "Library/Application Support/Chromium",
"Library/Application Support/Arc", "Library/Application Support/Dia", "Library/Application Support/Comet",
"Library/Application Support/BraveSoftware/Brave-Browser", "Library/Application Support/Microsoft Edge", "Library/Safari", "Library/Cookies"};
printf("{");
for (size_t index = 0; index < sizeof(names) / sizeof(names[0]); index++) {
struct root_fact fact = inspect_root(home, paths[index], owner);
printf("%s\"%s\":{\"state\":\"%s\",\"kind\":", index ? "," : "", names[index], fact.state);
if (fact.kind) printf("\"%s\"", fact.kind); else printf("null");
printf(",\"ownerMatches\":%s,\"ancestorBlocked\":%s}", boolean_or_null(fact.owner_matches), boolean_or_null(fact.ancestor_blocked));
}
printf("}");
}
#ifdef __APPLE__
static int dictionary_boolean(CFDictionaryRef dictionary, CFStringRef key) {
CFTypeRef value = CFDictionaryGetValue(dictionary, key);
return value && CFGetTypeID(value) == CFBooleanGetTypeID() ? CFBooleanGetValue(value) : -1;
}
int main(int argc, char **argv) {
int browser_roots = argc == 2 && strcmp(argv[1], "--browser-roots") == 0;
if (argc != 1 && !browser_roots) return 2;
uid_t uid = getuid();
struct passwd *account = getpwuid(uid);
char registered[PATH_MAX], environment[PATH_MAX];
const char *home = getenv("HOME");
int home_matches = account && home && realpath(account->pw_dir, registered) && realpath(home, environment)
&& strcmp(registered, account->pw_dir) == 0 && strcmp(registered, environment) == 0;
int home_fd = home_matches ? open(registered, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_NONBLOCK) : -1;
struct stat home_info;
home_matches = home_fd >= 0 && fstat(home_fd, &home_info) == 0 && S_ISDIR(home_info.st_mode) && home_info.st_uid == uid;
SessionAttributeBits attributes = 0;
OSStatus status = SessionGetInfo(callerSecuritySession, NULL, &attributes);
CFDictionaryRef quartz = CGSessionCopyCurrentDictionary();
int same_uid = -1, login_done = -1, on_console = -1;
if (quartz) {
CFTypeRef value = CFDictionaryGetValue(quartz, kCGSessionUserIDKey);
long long session_uid = -1;
if (value && CFGetTypeID(value) == CFNumberGetTypeID() && CFNumberGetValue(value, kCFNumberLongLongType, &session_uid)) same_uid = session_uid == uid;
if (same_uid == 1) {
login_done = dictionary_boolean(quartz, kCGSessionLoginDoneKey);
on_console = dictionary_boolean(quartz, kCGSessionOnConsoleKey);
}
}
printf("{\"protocol\":1,\"supported\":true,\"identity\":{\"effectiveUidMatches\":%s,\"homeMatchesRegistered\":%s},",
boolean_or_null(geteuid() == uid), boolean_or_null(home_matches));
printf("\"security\":{\"status\":%d,\"graphicAccess\":%s,\"rootSession\":%s,\"tty\":%s,\"remote\":%s},",
(int)status, boolean_or_null(status ? -1 : !!(attributes & sessionHasGraphicAccess)), boolean_or_null(status ? -1 : !!(attributes & sessionIsRoot)),
boolean_or_null(status ? -1 : !!(attributes & sessionHasTTY)), boolean_or_null(status ? -1 : !!(attributes & sessionIsRemote)));
printf("\"quartz\":{\"present\":%s,\"sameUid\":%s,\"loginDone\":%s,\"onConsole\":%s},\"browserRoots\":",
boolean_or_null(quartz != NULL), boolean_or_null(same_uid), boolean_or_null(login_done), boolean_or_null(on_console));
if (browser_roots && home_matches) print_browser_roots(home_fd, uid); else printf("null");
printf("}\n");
if (home_fd >= 0) close(home_fd);
if (quartz) CFRelease(quartz);
return 0;
}
#else
int main(void) {
printf("{\"protocol\":1,\"supported\":false}\n");
return 2;
}
#endif
+231
View File
@@ -0,0 +1,231 @@
import { createHash } from 'node:crypto';
import { closeSync, constants, createReadStream, fstatSync, lstatSync, openSync, readdirSync, readSync, realpathSync } from 'node:fs';
import { createRequire } from 'node:module';
import { release } from 'node:os';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { assertOwnedDiaProfile, browserOperationTimedOut, browserPreflightError, browserRootFacts, browserStartupFacts, browserStderrFacts,
nativeDiaLaunchOptions, observeBrowserLaunches, ownsFreshAccount, parseDirectoryRecord,
readFreshAccountConfiguration, stopOwnedBrowserGroup, validateQualificationHost } from './qualify-dia-macos.ts';
const require = createRequire(import.meta.url);
const sha256 = async file => {
const hash = createHash('sha256');
for await (const chunk of createReadStream(file)) hash.update(chunk);
return hash.digest('hex');
};
export function normalizedLaunchHashes(args, env, ownedPaths) {
const paths = Object.entries(ownedPaths).sort(([, left], [, right]) => right.length - left.length);
const normalize = value => {
const equals = value.startsWith('--') ? value.indexOf('=') : -1;
const prefix = equals >= 0 ? value.slice(0, equals + 1) : '';
const candidate = equals >= 0 ? value.slice(equals + 1) : value;
for (const [role, owned] of paths) {
if (candidate === owned || candidate.startsWith(owned + path.sep)) return prefix + '<' + role + '>' + candidate.slice(owned.length);
}
return value;
};
const hash = value => createHash('sha256').update(JSON.stringify(value)).digest('hex');
return { argvSha256: hash(args.map(normalize)), environmentSha256: hash(Object.entries(env).sort(([a], [b]) => a.localeCompare(b))
.map(([key, value]) => [key, normalize(value)])) };
}
export function compareDiaLaunchReceipts(left, right) {
const invalid = { comparable: false, qualificationCredit: false, reason: 'incomplete_or_incompatible_arms' };
if (!left?.launcher?.accountGuid || left.launcher.accountGuid === right?.launcher?.accountGuid) return invalid;
const fingerprints = [];
for (const [receipt, runtime] of [[left, 'bun'], [right, 'node']]) {
const qualification = receipt?.qualification;
const control = receipt?.backgroundPreflight?.comparisonControl;
const source = qualification?.launchComparison?.source;
const config = receipt?.launchComparison;
if (config?.mode !== 'launch-only' || config.runtime !== runtime || config.qualificationCredit !== false
|| receipt.backgroundPreflight?.status !== 'passed' || !control?.ready || qualification?.launchComparison?.qualificationCredit !== false
|| qualification.keychainStage !== 'completed' || qualification.isolation?.registeredIdentity !== true
|| qualification.isolation?.sharedRegisteredHome !== true || qualification.artifact?.signatureVerified !== true
|| qualification.artifact?.gatekeeperNotarized !== true || qualification.artifact?.macosCompatibility?.compatible !== true
|| qualification.platform?.os !== 'darwin' || qualification.platform?.architecture !== 'arm64'
|| qualification.platform?.bun !== '1.4.0' || qualification.platform?.playwright !== '1.62.1'
|| !/^\d+(?:\.\d+){1,2}$/.test(qualification.artifact.macosCompatibility.hostVersion)
|| qualification.artifact?.architectures?.includes('arm64') !== true
|| ['serviceStopped', 'userDomainStopped', 'userProcessesStopped', 'accountRemoved', 'groupRemoved', 'stagingRemoved'].some(key => receipt.launcherCleanup?.[key] !== true)
|| ['ownedBrowsersStopped', 'sourceProfileRemoved', 'keychainRestored', 'mountDetached', 'fixtureRemoved'].some(key => qualification.cleanup?.[key] !== true)
|| ['pass', 'fail', 'skip'].some(key => receipt.counts?.[key] !== 0 || qualification.counts?.[key] !== 0)) return invalid;
for (const [result, purpose] of [[control, 'control'], [source, 'source']]) {
const policy = result?.launchAttempts?.[0];
if (result?.protocol !== 1 || result.purpose !== purpose || result.samplingEnabled !== false || result.rootCount !== 1
|| result.supervisor?.closed !== true || result.supervisor?.exitCode !== 0 || result.cleanup?.confirmed !== true
|| result.cleanup?.childClosed !== true || result.cleanup?.groupAbsent !== true || result.cleanup?.launchSettled !== true || result.launchAttempts?.length !== 1
|| result.cleanup?.groups?.length !== 1 || result.cleanup.groups[0].absenceConfirmed !== true || result.cleanup.groups[0].childCloseObserved !== true
|| typeof result.ready !== 'boolean' || typeof result.launchReturned !== 'boolean'
|| policy?.sandboxRequired !== true || policy?.sandboxDisablingFlag !== false || policy?.pipeFlag !== true || policy?.tcpDebuggingFlag !== false
|| policy?.mockKeychainFlag !== false || policy?.passwordStoreFlag !== false || policy?.firstRunSuppressed !== false
|| policy?.headlessFlag !== true || policy?.expectedProfile !== true || policy?.detached !== true || policy?.shellDisabled !== true
|| policy?.stdioCount !== 5 || policy?.extraPipeDescriptors !== true || policy?.profileArgumentCount !== 1
|| result.driver?.runtime !== runtime || result.driver?.version !== (runtime === 'bun' ? '1.4.0' : '24.18.0')
|| result.driver?.os !== 'darwin' || result.driver?.architecture !== 'arm64' || result.driver?.playwright !== '1.62.1'
|| result.driver?.release !== qualification.platform.release
|| result.driver?.executableSha256 !== config.executableSha256 || result.driver?.driverSha256 !== config.driverSha256
|| result.driver?.helpersSha256 !== config.helpersSha256 || (result.ready && (!result.protocolResponded || !result.startupPages?.allowed || !result.postProbePages?.allowed))
|| ![result.argvSha256, result.environmentSha256, config.executableSha256, config.driverSha256, config.helpersSha256].every(value => /^[a-f0-9]{64}$/.test(value))) return invalid;
}
const hashes = [receipt.launcher?.sourceRevision, receipt.launcher?.archiveSha256, receipt.launcher?.destinationSha256,
qualification.artifact.sha256, qualification.artifact.executableSha256];
if (!/^[a-f0-9]{40}$/.test(hashes[0]) || !hashes.slice(1).every(value => /^[a-f0-9]{64}$/.test(value))) return invalid;
fingerprints.push(JSON.stringify({ hashes, platform: qualification.platform, compatibility: qualification.artifact.macosCompatibility,
version: qualification.artifact.version, bundle: qualification.artifact.bundleId, team: qualification.artifact.team,
driver: config.driverSha256, helpers: config.helpersSha256, controlArgs: control.argvSha256, controlEnv: control.environmentSha256,
sourceArgs: source.argvSha256, sourceEnv: source.environmentSha256 }));
}
if (fingerprints[0] !== fingerprints[1]) return { ...invalid, reason: 'comparison_inputs_differ' };
const bun = left.qualification.launchComparison.source.ready === true;
const node = right.qualification.launchComparison.source.ready === true;
return { comparable: true, qualificationCredit: false, outcome: bun ? node ? 'both_ready' : 'bun_only_ready' : node ? 'node_only_ready' : 'neither_ready' };
}
export async function runProtectedLaunch(executable, profile, env, purpose, ownedPaths) {
const result = { protocol: 1, purpose, stage: 'runtime_import', launchReturned: false, protocolResponded: false, ready: false,
timedOut: false, error: null, samplingEnabled: false, cleanup: { childClosed: false, groupAbsent: false, confirmed: false } };
const { chromium } = await import('playwright');
const cp = require('node:child_process');
const original = cp.spawn;
cp.spawn = function(command, args, options) {
if (command === executable) Object.assign(result, normalizedLaunchHashes(args, options.env, ownedPaths));
return original.call(this, command, args, options);
};
const observer = observeBrowserLaunches(new Map([[executable, profile]]));
let context;
let timer;
let launchSettled = false;
try {
result.stage = 'launch';
const launch = chromium.launchPersistentContext(profile, nativeDiaLaunchOptions(executable, env));
void launch.then(() => { launchSettled = true; }, () => { launchSettled = true; });
context = await Promise.race([launch,
new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('operation_timeout')), 30_000); })]);
clearTimeout(timer);
result.launchReturned = true;
result.stage = 'ownership';
if (observer.children.length !== 1) throw new Error('source_process_ownership_unconfirmed');
result.stage = 'startup_pages';
const urls = context.pages().map(page => page.url());
result.startupPages = { ...browserStartupFacts(urls, 'http://127.0.0.1:1'), allowed: urls.every(url => url === 'about:blank') };
if (!result.startupPages.allowed) throw new Error('onboarding_or_external_page');
result.stage = 'protocol_probe';
if (!context.pages()[0]) throw new Error('source_protocol_page_unavailable');
result.protocolResponded = await Promise.race([context.pages()[0].evaluate(() => 1).then(value => value === 1),
new Promise((_, reject) => { timer = setTimeout(() => reject(new Error('operation_timeout')), 5000); })]);
clearTimeout(timer);
const after = context.pages().map(page => page.url());
result.postProbePages = { ...browserStartupFacts(after, 'http://127.0.0.1:1'), allowed: after.every(url => url === 'about:blank') };
if (!result.postProbePages.allowed) throw new Error('onboarding_or_external_page');
result.ready = result.protocolResponded;
result.stage = 'ready';
} catch (error) {
const reasons = browserStderrFacts(observer.children).flatMap(facts => Object.entries(facts.reasonCounts ?? {})
.filter(([, count]) => count > 0).map(([reason]) => reason));
result.error = browserPreflightError(error, reasons);
result.timedOut = browserOperationTimedOut(error);
} finally {
clearTimeout(timer);
const deadline = performance.now() + 5_000;
observer.stop();
result.rootCount = observer.children.length;
result.launchAttempts = observer.attempts;
result.rootsBeforeCleanup = browserRootFacts(observer.children);
result.stderrBeforeCleanup = browserStderrFacts(observer.children);
if (context) void context.close().catch(() => {});
const groups = [];
for (const child of observer.children) {
const facts = { pid: child.pid, signalSent: false, absenceConfirmed: false, childCloseObserved: false };
try { await stopOwnedBrowserGroup(child, deadline, facts); }
catch { facts.failed = true; }
groups.push(facts);
}
result.cleanup.groups = groups;
result.cleanup.childClosed = observer.children.length === 1 && observer.children.every(child => child.closeObserved);
result.cleanup.groupAbsent = groups.length === 1 && groups.every(group => group.absenceConfirmed);
result.cleanup.launchSettled = launchSettled;
result.cleanup.confirmed = result.cleanup.childClosed && result.cleanup.groupAbsent && launchSettled;
result.rootsAfterCleanup = browserRootFacts(observer.children);
result.stderrAfterCleanup = browserStderrFacts(observer.children);
if (launchSettled) { observer.restore(); cp.spawn = original; }
}
return result;
}
export function readComparisonRequest() {
const buffer = Buffer.alloc(16 * 1024 + 1);
let length = 0;
while (length < buffer.length) {
const read = readSync(0, buffer, length, buffer.length - length, null);
if (!read) break;
length += read;
}
if (length > 16 * 1024) throw new Error('invalid_driver_request');
const request = JSON.parse(buffer.subarray(0, length).toString());
if (!request || !['control', 'source'].includes(request.purpose)
|| Object.keys(request).some(key => !(request.purpose === 'control' ? ['purpose'] : ['purpose', 'assetRoot', 'executableName', 'executableSha256']).includes(key))) throw new Error('invalid_driver_request');
return request;
}
async function main() {
validateQualificationHost(process.env);
const request = readComparisonRequest();
const account = readFreshAccountConfiguration(process.argv[2], 'comparison-driver');
const config = account.launchComparison;
const runtimeVersion = process.versions.bun ?? process.versions.node;
if ((config.runtime === 'bun' ? process.versions.bun !== '1.4.0' : Boolean(process.versions.bun) || runtimeVersion !== '24.18.0')
|| process.arch !== 'arm64' || require('playwright/package.json').version !== '1.62.1') throw new Error('invalid_driver_runtime');
if (await sha256(process.execPath) !== config.executableSha256 || await sha256(import.meta.filename) !== config.driverSha256
|| await sha256(path.join(import.meta.dirname, 'qualify-dia-macos.ts')) !== config.helpersSha256) throw new Error('driver_inputs_changed');
const identity = spawnSync('/usr/bin/dscl', ['.', '-read', '/Users/' + account.account, 'UniqueID', 'PrimaryGroupID', 'NFSHomeDirectory', 'GeneratedUID'],
{ env: account.environment, encoding: 'utf8', timeout: 5000, maxBuffer: 64 * 1024 });
if (identity.error || identity.status !== 0 || !ownsFreshAccount(parseDirectoryRecord(identity.stdout), account)) throw new Error('driver_identity_unconfirmed');
let executable = account.destinationExecutable;
let profile = path.join(account.temporary, 'probe/chromium');
let temporary = account.temporary;
const ownedPaths = { home: account.home, snapshot: account.snapshot, temporary: account.temporary, work: account.work };
if (request.purpose === 'source') {
const root = request.assetRoot;
if (typeof root !== 'string' || realpathSync(root) !== root || path.dirname(root) !== account.temporary || !path.basename(root).startsWith('dia-')
|| lstatSync(root).uid !== account.uid || typeof request.executableName !== 'string' || !request.executableName
|| path.basename(request.executableName) !== request.executableName || ['.', '..'].includes(request.executableName)) throw new Error('invalid_source_request');
executable = realpathSync(path.join(root, 'Dia.app/Contents/MacOS', request.executableName));
if (!executable.startsWith(path.join(root, 'Dia.app/Contents/MacOS') + path.sep)
|| !/^[a-f0-9]{64}$/.test(request.executableSha256) || await sha256(executable) !== request.executableSha256) throw new Error('source_identity_unconfirmed');
profile = path.join(account.home, 'Library/Application Support/Dia/User Data');
if (realpathSync(profile) !== profile || lstatSync(profile).uid !== account.uid) throw new Error('source_profile_unowned');
if (JSON.stringify(readdirSync(profile)) !== JSON.stringify(['.gstack-dia-owner'])) throw new Error('source_profile_not_fresh');
const descriptor = openSync(path.join(profile, '.gstack-dia-owner'), constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
const marker = Buffer.alloc(65);
let bytes;
try {
const entry = fstatSync(descriptor);
if (!entry.isFile() || entry.uid !== account.uid || entry.nlink !== 1 || entry.size !== 64 || (entry.mode & 0o077) !== 0) throw new Error('source_profile_unowned');
bytes = readSync(descriptor, marker, 0, marker.length, 0);
} finally { closeSync(descriptor); }
if (bytes !== 64) throw new Error('source_profile_unowned');
const info = lstatSync(profile, { bigint: true });
assertOwnedDiaProfile({ home: account.home, profile, uid: account.uid, dev: info.dev, ino: info.ino, nonce: marker.subarray(0, bytes).toString() });
temporary = path.join(root, 't');
ownedPaths.assets = root;
} else {
if (await sha256(executable) !== account.destinationSha256) throw new Error('control_identity_unconfirmed');
try { lstatSync(profile); throw new Error('control_profile_not_fresh'); }
catch (error) { if (error.code !== 'ENOENT') throw error; }
}
const env = { HOME: account.home, TMPDIR: temporary, PATH: '/usr/bin:/bin:/usr/sbin:/sbin', LANG: 'en_US.UTF-8' };
const result = await runProtectedLaunch(executable, profile, env, request.purpose, ownedPaths);
result.driver = { runtime: config.runtime, version: runtimeVersion, architecture: process.arch, os: process.platform, release: release(),
executableSha256: config.executableSha256, driverSha256: config.driverSha256, helpersSha256: config.helpersSha256, playwright: '1.62.1' };
return result;
}
if (import.meta.main) {
main().then(result => { process.stdout.write(JSON.stringify(result) + '\n'); }, () => {
process.stdout.write(JSON.stringify({ protocol: 1, ready: false, error: 'driver_admission_failed', cleanup: { confirmed: false } }) + '\n');
process.exitCode = 2;
});
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,125 @@
param(
[Parameter(Mandatory = $true)][string]$OutputRoot,
[switch]$Child,
[switch]$Initialized,
[string]$ExpectedSid,
[string]$BinDirectory,
[string]$GitDirectory
)
$ErrorActionPreference = 'Stop'
if (-not $IsWindows -or $env:GITHUB_ACTIONS -ne 'true' -or $env:CI -ne 'true') {
throw 'Native cookie qualification requires a disposable GitHub Actions Windows runner.'
}
$repository = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
if ($Child) {
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
if ($identity.User.Value -ne $ExpectedSid) { throw 'Unexpected qualification account identity.' }
$registered = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$ExpectedSid").ProfileImagePath
$registered = [Environment]::ExpandEnvironmentVariables($registered)
$env:USERPROFILE = $registered
$env:HOME = $registered
$folders = [Microsoft.Win32.Registry]::Users.OpenSubKey("$ExpectedSid\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders")
if (-not $folders) { throw 'The new account known-folder registry is unavailable.' }
try {
$rawLocal = $folders.GetValue('Local AppData', $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
$rawRoaming = $folders.GetValue('AppData', $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
if (-not $rawLocal -or -not $rawRoaming) { throw 'The new account app-data folders are undefined.' }
$env:LOCALAPPDATA = [Environment]::ExpandEnvironmentVariables($rawLocal)
$env:APPDATA = [Environment]::ExpandEnvironmentVariables($rawRoaming)
} finally { $folders.Dispose() }
if (-not $Initialized) {
& (Join-Path $PSHOME 'pwsh.exe') -NoLogo -NoProfile -NonInteractive -File $PSCommandPath -Child -Initialized -ExpectedSid $ExpectedSid -BinDirectory $BinDirectory -GitDirectory $GitDirectory -OutputRoot $OutputRoot
exit $LASTEXITCODE
}
$profile = [Environment]::GetFolderPath('UserProfile')
$local = [Environment]::GetFolderPath('LocalApplicationData', 'DoNotVerify')
$roaming = [Environment]::GetFolderPath('ApplicationData', 'DoNotVerify')
if ($profile -ne $registered -or -not $local.StartsWith($profile + '\', [StringComparison]::OrdinalIgnoreCase)) {
Write-Output (ConvertTo-Json -Compress @{ profileMatchesRegistered = ($profile -eq $registered); localInsideProfile = $local.StartsWith($profile + '\', [StringComparison]::OrdinalIgnoreCase); localEmpty = [string]::IsNullOrEmpty($local); localMatchesInherited = ($local -eq $env:LOCALAPPDATA) })
throw 'Qualification must use the new account real Windows profile.'
}
$keep = @('SystemRoot', 'WINDIR', 'ProgramFiles', 'ProgramFiles(x86)', 'ProgramData', 'PATHEXT')
Get-ChildItem Env: | Where-Object { $_.Name -notin $keep } | ForEach-Object { Remove-Item "Env:$($_.Name)" }
$env:USERPROFILE = $profile
$env:HOME = $profile
$env:LOCALAPPDATA = $local
$env:APPDATA = $roaming
$env:TEMP = Join-Path $local 'Temp'
$env:TMP = $env:TEMP
$env:PATH = "$BinDirectory;$GitDirectory\cmd;$GitDirectory\bin;$GitDirectory\usr\bin;$env:SystemRoot\System32;$env:SystemRoot"
$env:CI = 'true'
$env:GITHUB_ACTIONS = 'true'
New-Item -ItemType Directory -Force -Path $env:TEMP | Out-Null
Set-Location $repository
& (Join-Path $BinDirectory 'bun.exe') install --frozen-lockfile
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& (Join-Path $GitDirectory 'bin\bash.exe') browse/scripts/build-node-server.sh
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& (Join-Path $BinDirectory 'bun.exe') --no-env-file --no-install --no-macros --config=NUL browse/test/cookie-import-native-qualification.ts $OutputRoot
exit $LASTEXITCODE
}
$work = Join-Path $OutputRoot ('cookie-native-host-' + [Guid]::NewGuid().ToString('N'))
$bin = Join-Path $work 'bin'
$evidence = Join-Path $work 'evidence'
$snapshot = Join-Path $work 'repository'
New-Item -ItemType Directory -Path $work | Out-Null
$name = 'gstack' + [Guid]::NewGuid().ToString('N').Substring(0, 10)
$password = ConvertTo-SecureString ([Convert]::ToBase64String([Security.Cryptography.RandomNumberGenerator]::GetBytes(32)) + '!aA1') -AsPlainText -Force
$account = $null
$process = $null
$exitCode = 1
try {
$account = New-LocalUser -Name $name -Password $password -AccountExpires (Get-Date).AddHours(1) -Description 'Disposable gstack cookie qualification'
Add-LocalGroupMember -SID 'S-1-5-32-545' -Member $account
$principal = "$env:COMPUTERNAME\$name"
& icacls.exe $work /grant "${principal}:(OI)(CI)M" /Q | Out-Null
if ($LASTEXITCODE -ne 0) { throw 'Could not grant fixture output access.' }
New-Item -ItemType Directory -Path $bin, $evidence, $snapshot | Out-Null
$archive = Join-Path $work 'source.tar'
& git -C $repository archive --format=tar -o $archive HEAD
if ($LASTEXITCODE -ne 0) { throw 'Could not snapshot the candidate source.' }
& (Join-Path $env:SystemRoot 'System32\tar.exe') -xf $archive -C $snapshot
if ($LASTEXITCODE -ne 0) { throw 'Could not materialize the isolated source snapshot.' }
Copy-Item (Get-Command bun).Source (Join-Path $bin 'bun.exe')
Copy-Item (Get-Command node).Source (Join-Path $bin 'node.exe')
$gitDirectory = Split-Path (Split-Path (Get-Command git).Source)
if (-not (Test-Path (Join-Path $gitDirectory 'bin\bash.exe'))) { throw 'Git Bash is required for the isolated Node build.' }
$childScript = Join-Path $snapshot '.github\scripts\run-cookie-native-qualification.ps1'
$credential = [Management.Automation.PSCredential]::new($principal, $password)
$arguments = @('-NoLogo', '-NoProfile', '-NonInteractive', '-File', ('"' + $childScript + '"'), '-Child', '-ExpectedSid', $account.SID.Value,
'-BinDirectory', ('"' + $bin + '"'), '-GitDirectory', ('"' + $gitDirectory + '"'), '-OutputRoot', ('"' + $evidence + '"'))
$process = Start-Process -FilePath (Join-Path $PSHOME 'pwsh.exe') -ArgumentList $arguments -Credential $credential -LoadUserProfile -WorkingDirectory $snapshot -PassThru -WindowStyle Hidden -RedirectStandardOutput (Join-Path $work 'stdout.log') -RedirectStandardError (Join-Path $work 'stderr.log')
$null = $process.Handle
if (-not $process.WaitForExit(360000)) {
$process.Kill($true)
throw 'Native qualification exceeded its launcher deadline.'
}
if ($null -eq $process.ExitCode) { throw 'Native qualification did not return an exit status.' }
$exitCode = $process.ExitCode
foreach ($file in Get-ChildItem $evidence -Filter qualification.json -Recurse) {
$receipt = Get-Content $file.FullName -Raw | ConvertFrom-Json
if ($receipt.status -eq 'passed') {
$expected = (Get-FileHash (Join-Path $repository 'browse\dist\server-node.mjs') -Algorithm SHA256).Hash.ToLowerInvariant()
if ($receipt.qualifiedBuild.sourceHashes.'browse/dist/server-node.mjs' -ne $expected) {
throw 'The qualified Node bundle differs from the candidate workspace build.'
}
}
}
} finally {
try {
if ($process -and -not $process.HasExited) { $process.Kill($true) }
} finally {
try {
if (Test-Path (Join-Path $work 'stdout.log')) { Get-Content (Join-Path $work 'stdout.log') }
if (Test-Path (Join-Path $work 'stderr.log')) { Get-Content (Join-Path $work 'stderr.log') }
if (Test-Path $evidence) { Get-ChildItem $evidence -Directory -Filter 'cookie-native-qualification-*' | Copy-Item -Destination $OutputRoot -Recurse }
} finally {
try { if ($account) { Remove-LocalUser -SID $account.SID } }
finally { $password.Dispose() }
}
}
}
exit $exitCode
@@ -0,0 +1,863 @@
import { createHash, randomBytes, randomUUID } from 'node:crypto';
import { spawnSync } from 'node:child_process';
import { accessSync, chmodSync, constants, copyFileSync, createReadStream, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { homedir } from 'node:os';
import path from 'node:path';
import { assertDiaSocketPath, browserPreflightError, browserStartupCategory, captureUserKeychains, fixtureKeychainRestoreCommands, FRESH_WORK_PREFIX, type FreshAccount,
nativeDiaLaunchOptions, observeBrowserLaunches, observeFixtureKeychain, ownsFreshAccount, parseDirectoryRecord, stopOwnedBrowserGroup,
inspectMachOArchitectures, playwrightModuleLoadFacts, prepareKeychainHome, readFreshAccountConfiguration, runDiaLaunchComparison, runGuiReadiness,
validateQualificationHost, writePrivateReceipt } from './qualify-dia-macos';
export { FRESH_WORK_PREFIX, ownsFreshAccount, parseDirectoryRecord } from './qualify-dia-macos';
const require = createRequire(import.meta.url);
const repository = path.resolve(import.meta.dir, '../..');
interface UserDomainObservation {
uid: number;
state: 'present' | 'absent' | 'unavailable';
hasGuiDomain: boolean;
exitCode: number | null;
stdoutBytes: number;
stderrBytes: number;
structure?: {
complete: boolean;
type: 'user' | 'other' | 'unavailable';
handleMatchesUid: boolean | null;
creator: 'launchctl' | 'other' | 'unavailable';
creatorIsProbe: boolean | null;
counts: Record<string, number | null>;
sectionNonemptyLines: Record<string, number | null>;
};
}
export function classifyUserDomain(uid: number, result: { status: number | null; stdout: string; stderr: string; error?: unknown }, probePid?: number): UserDomainObservation {
if (!Number.isSafeInteger(uid) || uid < 20_000 || uid >= 60_000) throw new Error('invalid_fresh_user_domain');
const text = result.stdout.trimStart();
const diagnostic = [result.stdout.trim(), result.stderr.trim()].filter(Boolean).join('\n');
const missing = new RegExp('^(?:Bad request\\.\\s*)?Could not find domain for (?:(?:user (?:uid|user)|uid|user):\\s*' + uid + '|user/' + uid + ')\\.?$');
const present = !result.error && result.status === 0
&& (text.startsWith('user/' + uid + ' = {') || text.startsWith('com.apple.xpc.launchd.domain.user.' + uid + ' = {'));
const absent = !result.error && Number.isInteger(result.status) && result.status! > 0 && missing.test(diagnostic);
const observation: UserDomainObservation = { uid, state: present ? 'present' : absent ? 'absent' : 'unavailable',
hasGuiDomain: present && (new RegExp('\\bgui/' + uid + '(?:\\b|/)').test(text) || /\bsession\s*=\s*Aqua\b/.test(text)
|| new RegExp('com\\.apple\\.xpc\\.launchd\\.user\\.domain\\.' + uid + '\\.\\d+\\.Aqua\\b').test(text)),
exitCode: result.status, stdoutBytes: Buffer.byteLength(result.stdout), stderrBytes: Buffer.byteLength(result.stderr) };
if (!present || observation.stdoutBytes > 1024 * 1024) return observation;
const lines = text.trimEnd().split('\n');
const indent = lines.find(line => /^\s+type = \S+\s*$/.test(line))?.match(/^(\s+)/)?.[1];
const fields = new Map<string, string>();
const sections: Record<string, number | null> = Object.fromEntries(['services', 'jobs', 'subdomains', 'unmanaged processes', 'endpoints',
'externally-hosted endpoints', 'pending requests', 'pending attachments'].map(key => [key, null]));
let complete = Boolean(indent) && lines.at(-1) === '}';
for (let index = 1; indent && index < lines.length - 1; index++) {
const line = lines[index];
if (!line.trim()) continue;
if (!line.startsWith(indent) || /^\s/.test(line.slice(indent.length))) { complete = false; break; }
const entry = line.slice(indent.length).match(/^([^=]+?) = (.*)$/);
if (!entry || fields.has(entry[1])) { complete = false; break; }
fields.set(entry[1], entry[2]);
if (entry[2] === '{') {
let nonemptyLines = 0;
while (++index < lines.length - 1 && lines[index] !== indent + '}') {
if (lines[index].trim()) nonemptyLines++;
}
if (index >= lines.length - 1) { complete = false; break; }
if (Object.hasOwn(sections, entry[1])) sections[entry[1]] = nonemptyLines;
} else if (entry[2] === '{}' && Object.hasOwn(sections, entry[1])) sections[entry[1]] = 0;
}
const counts = Object.fromEntries(['active count', 'on-demand count', 'service count', 'active service count', 'external activation count',
'in-progress bootstraps', 'pended requests', 'creator euid'].map(key => {
const value = fields.get(key);
return [key, value && /^\d+$/.test(value) && Number.isSafeInteger(Number(value)) ? Number(value) : null];
}));
const creatorMatch = fields.get('creator')?.match(/^launchctl(?:\.([1-9]\d*)|\[([1-9]\d*)\])$/);
const creatorPid = creatorMatch?.[1] ?? creatorMatch?.[2];
observation.structure = { complete, type: fields.has('type') ? fields.get('type') === 'user' ? 'user' : 'other' : 'unavailable',
handleMatchesUid: fields.has('handle') ? fields.get('handle') === String(uid) : null,
creator: creatorPid ? 'launchctl' : fields.has('creator') ? 'other' : 'unavailable',
creatorIsProbe: creatorPid && Number.isSafeInteger(probePid) && probePid! > 0 ? Number(creatorPid) === probePid : null,
counts, sectionNonemptyLines: sections };
return observation;
}
export function inspectUserDomain(uid: number, deadline: number, env: Record<string, string>, spawn: typeof spawnSync = spawnSync): UserDomainObservation {
if (!Number.isSafeInteger(uid) || uid < 20_000 || uid >= 60_000) throw new Error('invalid_fresh_user_domain');
if (!Number.isFinite(deadline)) throw new Error('fresh_launcher_deadline');
const timeout = Math.floor(Math.min(3_000, deadline - performance.now()));
if (!Number.isFinite(timeout) || timeout < 1) throw new Error('fresh_launcher_deadline');
const result = spawn('/usr/bin/sudo', ['-n', '/bin/sh', '-c', 'printf "GSTACK_DIA_DOMAIN_PROBE_PID=%s\\n" "$$"; exec /bin/launchctl print "$1"',
'gstack-dia-domain-probe', 'user/' + uid], { env, encoding: 'utf8', timeout, maxBuffer: 1024 * 1024 });
const stdout = typeof result.stdout === 'string' ? result.stdout : '';
const stderr = typeof result.stderr === 'string' ? result.stderr : '';
const prefix = stdout.match(/^GSTACK_DIA_DOMAIN_PROBE_PID=([1-9]\d*)\n/);
const pid = prefix ? Number(prefix[1]) : undefined;
return classifyUserDomain(uid, { ...result, stdout: prefix ? stdout.slice(prefix[0].length) : stdout, stderr,
error: result.error || (!Number.isSafeInteger(pid) ? new Error('domain_probe_pid_unavailable') : undefined) }, pid);
}
export function classifyParentDomain(uid: number, result: { status: number | null; stdout: string; stderr: string; error?: unknown }) {
if (!Number.isSafeInteger(uid) || uid < 20_000 || uid >= 60_000) throw new Error('invalid_fresh_user_domain');
const observation = { uid, state: 'unavailable' as 'present' | 'absent' | 'unavailable', parseStage: 'command_failure',
subdomainCount: 0, matchingUserDomains: 0, matchingGuiDomains: 0, unrecognizedEntries: 0, duplicateEntries: 0,
exitCode: result.status, stdoutBytes: Buffer.byteLength(result.stdout), stderrBytes: Buffer.byteLength(result.stderr) };
if (result.error || result.status !== 0) return observation;
observation.parseStage = 'oversized';
if (observation.stdoutBytes > 1024 * 1024) return observation;
observation.parseStage = 'unexpected_parent';
const lines = result.stdout.trim().split('\n');
if (!['system = {', 'com.apple.xpc.launchd.domain.system = {'].includes(lines[0]) || lines.at(-1) !== '}') return observation;
const indent = lines.find(line => /^\s+type = system$/.test(line))?.match(/^(\s+)/)?.[1];
if (!indent) return observation;
const fields = new Set<string>();
let subdomains: string[] | undefined;
observation.parseStage = 'malformed_structure';
for (let index = 1; index < lines.length - 1; index++) {
const line = lines[index];
if (!line.trim()) continue;
if (!line.startsWith(indent) || /^\s/.test(line.slice(indent.length))) return observation;
const entry = line.slice(indent.length).match(/^([^=]+?) = (.*)$/);
if (!entry || fields.has(entry[1])) return observation;
fields.add(entry[1]);
if (entry[2] === '{') {
const start = index + 1;
while (++index < lines.length - 1 && lines[index] !== indent + '}') {}
if (index >= lines.length - 1) return observation;
if (entry[1] === 'subdomains') subdomains = lines.slice(start, index).map(line => line.trim()).filter(Boolean);
} else if (entry[1] === 'subdomains' && entry[2] === '{}') subdomains = [];
}
observation.parseStage = 'missing_subdomains';
if (!subdomains) return observation;
const seen = new Set<string>();
for (const entry of subdomains) {
observation.subdomainCount++;
const user = entry.match(/^(?:user\/|com\.apple\.xpc\.launchd\.domain\.user\.)(\d+)$/);
const gui = entry.match(/^(?:gui\/(\d+)|com\.apple\.xpc\.launchd\.user\.domain\.(\d+)\.\d+\.Aqua)$/);
const listedUid = user?.[1] ?? gui?.[1] ?? gui?.[2];
const process = entry.match(/^(?:pid\/(\d+)|com\.apple\.xpc\.launchd\.domain\.pid\.[^{}\r\n]+\.(\d+))$/);
const session = entry.match(/^(?:session\/(\d+)|com\.apple\.xpc\.launchd\.domain\.session\.(\d+))$/);
const identity = user ? 'user/' + listedUid : gui ? 'gui/' + listedUid : process ? 'pid/' + (process[1] ?? process[2])
: session ? 'session/' + (session[1] ?? session[2]) : entry;
if (seen.has(identity)) observation.duplicateEntries++;
seen.add(identity);
if (listedUid && (!Number.isSafeInteger(Number(listedUid)) || String(Number(listedUid)) !== listedUid)) observation.unrecognizedEntries++;
else if (user) observation.matchingUserDomains += Number(user[1]) === uid ? 1 : 0;
else if (gui) observation.matchingGuiDomains += Number(gui[1] ?? gui[2]) === uid ? 1 : 0;
else if (!/^(?:(?:pid|session|login)\/\d+|com\.apple\.xpc\.launchd\.domain\.(?:pid\.[^{}\r\n]+\.\d+|session\.\d+))$/.test(entry)) observation.unrecognizedEntries++;
}
observation.parseStage = observation.unrecognizedEntries ? 'unrecognized_subdomain' : observation.duplicateEntries ? 'duplicate_subdomain' : 'parsed';
if (!observation.unrecognizedEntries && !observation.duplicateEntries) observation.state = observation.matchingUserDomains || observation.matchingGuiDomains ? 'present' : 'absent';
return observation;
}
type ParentDomainObservation = ReturnType<typeof classifyParentDomain>;
export function passiveUserDomainState(observation: ParentDomainObservation | undefined, uid: number): 'absent' | 'present' | 'unavailable' {
if (!observation || observation.uid !== uid || observation.exitCode !== 0 || observation.parseStage !== 'parsed'
|| observation.duplicateEntries !== 0 || observation.unrecognizedEntries !== 0 || observation.matchingGuiDomains !== 0) return 'unavailable';
if (observation.state === 'absent' && observation.matchingUserDomains === 0) return 'absent';
if (observation.state === 'present' && observation.matchingUserDomains === 1) return 'present';
return 'unavailable';
}
export function inspectParentDomain(uid: number, deadline: number, env: Record<string, string>, spawn: typeof spawnSync = spawnSync) {
if (!Number.isSafeInteger(uid) || uid < 20_000 || uid >= 60_000) throw new Error('invalid_fresh_user_domain');
const timeout = Math.floor(Math.min(3_000, deadline - performance.now()));
if (!Number.isFinite(deadline) || !Number.isFinite(timeout) || timeout < 1) throw new Error('fresh_launcher_deadline');
const result = spawn('/usr/bin/sudo', ['-n', '/bin/launchctl', 'print', 'system'], { env, encoding: 'utf8', timeout, maxBuffer: 1024 * 1024 });
return classifyParentDomain(uid, { ...result, stdout: typeof result.stdout === 'string' ? result.stdout : '',
stderr: typeof result.stderr === 'string' ? result.stderr : '' });
}
export const ARCHIVE_CHECK = `import json, posixpath, sys, tarfile, unicodedata
try:
with tarfile.open(sys.argv[1], 'r:') as archive:
members = archive.getmembers()
if len(members) > 200000 or sum(item.size for item in members) > 2 * 1024**3:
raise ValueError()
seen, links = set(), set()
for item in members:
name = item.name.rstrip('/')
if not name or name.startswith('/') or '\\\\' in name or any(ord(char) < 32 for char in name):
raise ValueError()
canonical = unicodedata.normalize('NFC', name).casefold()
if '..' in name.split('/') or posixpath.normpath(name) != name or canonical in seen:
raise ValueError()
if not (item.isfile() or item.isdir() or item.issym()):
raise ValueError()
seen.add(canonical)
if item.issym():
target = posixpath.normpath(posixpath.join(posixpath.dirname(name), item.linkname))
if item.linkname.startswith('/') or '\\\\' in item.linkname or '..' in item.linkname.split('/') or target == '..' or target.startswith('../'):
raise ValueError()
links.add(canonical)
for item in members:
parts = unicodedata.normalize('NFC', item.name.rstrip('/')).casefold().split('/')
if any('/'.join(parts[:index]) in links for index in range(1, len(parts))):
raise ValueError()
print(json.dumps({'valid': True, 'members': len(members)}))
except Exception:
print(json.dumps({'valid': False, 'reason': 'unsafe_source_archive'}))
sys.exit(2)
`;
export const PRIVATE_RECEIPT_READ = `import json, os, stat, sys
fds = []
try:
file, uid, root = sys.argv[1], int(sys.argv[2]), os.path.realpath(sys.argv[3])
if root != sys.argv[3] or not os.path.isabs(file) or os.path.normpath(file) != file or os.path.commonpath([file, root]) != root:
raise ValueError()
parts = os.path.relpath(file, root).split(os.sep)
if any(part in ('', '.', '..') for part in parts):
raise ValueError()
fds.append(os.open(root, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW))
for part in parts[:-1]:
fds.append(os.open(part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=fds[-1]))
parent = os.fstat(fds[-1])
if parent.st_uid != uid or parent.st_mode & 0o022:
raise ValueError()
fds.append(os.open(parts[-1], os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=fds[-1]))
fd = fds[-1]
info = os.fstat(fd)
if not stat.S_ISREG(info.st_mode) or info.st_uid != uid or info.st_mode & 0o022 or info.st_size > 1024**2:
raise ValueError()
data = os.read(fd, 1024**2 + 1)
after = os.fstat(fd)
if len(data) != info.st_size or (info.st_size, info.st_mtime_ns, info.st_ctime_ns) != (after.st_size, after.st_mtime_ns, after.st_ctime_ns):
raise ValueError()
value = json.loads(data)
if not isinstance(value, dict):
raise ValueError()
print(json.dumps(value))
except Exception:
sys.exit(2)
finally:
for fd in reversed(fds):
os.close(fd)
`;
export function uidProcessFacts(output: string, uid: number) {
const known = ['bun', 'security', 'osascript', 'launchd', 'cfprefsd', 'trustd', 'distnoted', 'lsd', 'tccd', 'securityd', 'secd', 'usernoted',
'UserEventAgent', 'pkd', 'nsurlsessiond', 'containermanagerd', 'Google Chrome for Testing', 'Google Chrome', 'Chromium',
'Chromium Helper', 'Dia', 'Dia Helper', 'chrome', 'chrome_crashpad_handler'];
const aliases = new Map(known.flatMap(name => [name, name.slice(0, 15), name.slice(0, 16)].map(alias => [alias, name] as const)));
const processes: Array<{ pid: number; ppid: number; state: string; basename: string }> = [];
for (const line of output.split('\n').filter(line => line.trim())) {
const match = line.match(/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(.+?)\s*$/);
if (!match) throw new Error('invalid_uid_process_snapshot');
if (Number(match[1]) !== uid) continue;
const pid = Number(match[2]);
const ppid = Number(match[3]);
if (!Number.isSafeInteger(pid) || pid < 1 || !Number.isSafeInteger(ppid) || ppid < 0) throw new Error('invalid_uid_process_snapshot');
const state = ['I', 'R', 'S', 'T', 'U', 'Z', 'D', 'X'].includes(match[4][0]) ? match[4][0] : 'other';
processes.push({ pid, ppid, state, basename: aliases.get(match[5]) ?? 'other' });
}
return { available: true, count: processes.length, zombies: processes.filter(process => process.state === 'Z').length,
live: processes.filter(process => process.state !== 'Z').length, truncated: processes.length > 64, processes: processes.slice(0, 64) };
}
export function inspectUidProcesses(uid: number, deadline: number, env: Record<string, string>, spawn: typeof spawnSync = spawnSync) {
if (!Number.isSafeInteger(uid) || uid < 20_000 || uid >= 60_000) throw new Error('invalid_fresh_user_domain');
try {
const timeout = Math.floor(Math.min(2_000, deadline - performance.now()));
if (!Number.isFinite(deadline) || !Number.isFinite(timeout) || timeout < 1) throw new Error('fresh_launcher_deadline');
const result = spawn('/bin/ps', ['-axo', 'uid=,pid=,ppid=,state=,ucomm='], { env, encoding: 'utf8', timeout, maxBuffer: 128 * 1024 });
if (result.error || result.status !== 0 || typeof result.stdout !== 'string' || !result.stdout.trim()) throw new Error('uid_process_snapshot_failed');
return uidProcessFacts(result.stdout, uid);
} catch { return { available: false }; }
}
export function freshQualificationPassed(workerExit: number | undefined, backgroundStatus: unknown, qualificationStatus: unknown, cleanup: Record<string, unknown>): boolean {
return workerExit === 0 && backgroundStatus === 'passed' && qualificationStatus === 'passed'
&& ['serviceStopped', 'userDomainStopped', 'userProcessesStopped', 'accountRemoved', 'groupRemoved', 'stagingRemoved'].every(key => cleanup[key] === true)
&& Object.values(cleanup).every(value => value === true);
}
export function parseDirectoryIds(output: string): Set<number> {
const ids = new Set<number>();
for (const line of output.split('\n').filter(line => line.trim())) {
const value = line.match(/^\S.*?\s+(-?\d+)\s*$/)?.[1];
if (!value || !Number.isSafeInteger(Number(value))) throw new Error('invalid_directory_id_list');
ids.add(Number(value));
}
if (!ids.size) throw new Error('empty_directory_id_list');
return ids;
}
export function ownedUserDomainTarget(record: Record<string, string>, account: Pick<FreshAccount, 'guid' | 'uid' | 'gid' | 'home'>,
beforeCreation: ParentDomainObservation | undefined, current: ParentDomainObservation, currentUid = process.getuid?.()): string | null {
const currentState = passiveUserDomainState(current, account.uid);
if (!Number.isSafeInteger(account.uid) || account.uid < 20_000 || account.uid >= 60_000 || account.uid === currentUid
|| !Number.isSafeInteger(currentUid) || !ownsFreshAccount(record, account)
|| passiveUserDomainState(beforeCreation, account.uid) !== 'absent' || currentState === 'unavailable') {
throw new Error('fresh_user_domain_ownership_unconfirmed');
}
return currentState === 'present' ? 'user/' + account.uid : null;
}
export function freshLaunchDefinition(account: FreshAccount) {
return {
Label: account.label, UserName: account.account, GroupName: account.account, SessionCreate: true,
RunAtLoad: true, KeepAlive: false, ExitTimeOut: 5, Umask: 63,
WorkingDirectory: account.snapshot,
ProgramArguments: [account.bun, '--no-env-file', '--no-install', '--no-macros', '--config=/dev/null',
path.join(account.snapshot, '.github/scripts/run-dia-native-qualification.ts'), '--fresh-worker', account.configFile],
EnvironmentVariables: account.environment,
StandardOutPath: '/dev/null', StandardErrorPath: '/dev/null',
};
}
export function ownsLaunchService(state: string, account: Pick<FreshAccount, 'label' | 'bun' | 'account'>): boolean {
return state.trimStart().startsWith('system/' + account.label + ' = {')
&& state.match(/^\s*program = (.+)$/m)?.[1].trim() === account.bun
&& state.match(/^\s*username = (.+)$/m)?.[1].trim() === account.account
&& state.match(/^\s*group = (.+)$/m)?.[1].trim() === account.account;
}
async function digest(file: string) {
const hash = createHash('sha256');
for await (const chunk of createReadStream(file)) hash.update(chunk);
return hash.digest('hex');
}
function safeCommand(command: string, args: string[], timeout: number, env: NodeJS.ProcessEnv, cwd?: string) {
timeout = Math.floor(timeout);
if (!Number.isFinite(timeout) || timeout < 1) throw new Error('native_operation_timed_out');
const result = spawnSync(command, args, { env, cwd, encoding: 'utf8', timeout, maxBuffer: 1024 * 1024 });
if (result.error || result.status !== 0) {
const elevated = command === '/usr/bin/sudo';
const errorCode = (result.error as NodeJS.ErrnoException | undefined)?.code;
throw Object.assign(new Error('native_command_failed'), { diagnostic: {
command: path.basename(elevated ? args[1] : command), operation: args[elevated ? 2 : 0], exitCode: result.status,
stdoutBytes: Buffer.byteLength(result.stdout || ''), stderrBytes: Buffer.byteLength(result.stderr || ''),
spawnError: result.error ? (['ENOENT', 'EACCES', 'EPERM', 'ETIMEDOUT'].includes(errorCode || '') ? errorCode : 'spawn_failed') : undefined,
} });
}
return result.stdout.trim();
}
async function limit<T>(promise: Promise<T>, timeout: number): Promise<T> {
let timer: ReturnType<typeof setTimeout>;
try {
return await Promise.race([promise, new Promise<never>((_, reject) => {
timer = setTimeout(() => reject(new Error('native_operation_timed_out')), timeout);
})]);
} finally { clearTimeout(timer!); }
}
async function freshWorker(configFile: string) {
validateQualificationHost(process.env);
const account = readFreshAccountConfiguration(configFile);
const preflightFile = path.join(account.temporary, 'dia-background-preflight.json');
const seed = lstatSync(preflightFile);
if (!seed.isFile() || seed.uid !== process.getuid?.() || realpathSync(preflightFile) !== preflightFile) throw new Error('unsafe_preflight_receipt');
const receipt: Record<string, any> = { status: 'incomplete', reason: 'fresh_identity_preflight', nativeCasesRun: false,
preflight: { registeredIdentity: false, foundationHome: false, keychain: false, headlessChromium: false },
cleanup: { probeBrowsersStopped: false, probeKeychainRestored: false }, sessionCreate: true };
const env = account.environment;
let cleaning = false;
const run = (command: string, args: string[], timeout = 10_000) => {
try { return safeCommand(command, args, timeout, env, account.snapshot); }
catch (error) {
const diagnostic = (error as { diagnostic?: object }).diagnostic ?? { command: path.basename(command), operation: args[0] };
if (cleaning) (receipt.cleanupCommandFailures ??= []).push(diagnostic);
else receipt.initialCommandFailure ??= diagnostic;
throw new Error('native_command_failed');
}
};
let context: any;
let observer: ReturnType<typeof observeBrowserLaunches> | undefined;
let comparisonControl: Record<string, any> | undefined;
let launchAttempted = false;
let keychainCreated = false;
let keychainChanged = false;
let snapshot: ReturnType<typeof captureUserKeychains> | undefined;
const probe = path.join(account.temporary, 'probe');
const keychain = path.join(probe, 'probe.keychain-db');
try {
if (!/^[a-z][a-z0-9]{8,24}$/.test(account.account) || process.getuid?.() !== account.uid || process.geteuid?.() !== account.uid
|| process.getgid?.() !== account.gid || realpathSync(homedir()) !== account.home || realpathSync(account.work) !== account.work) throw new Error('fresh_identity_mismatch');
for (const directory of [account.home, account.temporary, account.snapshot, path.dirname(account.bun),
...(account.guiReadiness ? [] : [path.join(account.snapshot, 'node_modules')])]) {
if (!directory.startsWith(account.work + path.sep) || realpathSync(directory) !== directory || lstatSync(directory).uid !== account.uid) throw new Error('fresh_directory_ownership_mismatch');
}
const record = parseDirectoryRecord(run('/usr/bin/dscl', ['.', '-read', '/Users/' + account.account, 'UniqueID', 'PrimaryGroupID', 'NFSHomeDirectory', 'GeneratedUID']));
if (!ownsFreshAccount(record, account)) throw new Error('fresh_registered_identity_mismatch');
receipt.preflight.registeredIdentity = true;
if (account.guiReadiness) {
if (await digest(account.bun) !== account.bunSha256) throw new Error('staged_executable_changed');
receipt.reason = 'gui_readiness_only';
receipt.operations = { dependencyInstall: false, browserLaunch: false, keychainAccess: false, diaDownload: false };
receipt.guiReadiness = await runGuiReadiness(account.guiReadiness.executable, account.guiReadiness.executableSha256,
path.join(account.snapshot, '.github/scripts/dia-gui-readiness.c'), account.guiReadiness.sourceSha256, false, env);
return receipt.guiReadiness.available && receipt.guiReadiness.observation.identity.effectiveUidMatches
&& receipt.guiReadiness.observation.identity.homeMatchesRegistered ? 0 : 2;
}
if (!account.destinationExecutable || !account.destinationSha256) throw new Error('browser_qualification_authority_required');
const foundationHome = run('/usr/bin/osascript', ['-l', 'JavaScript', '-e', 'ObjC.import("Foundation"); $.NSHomeDirectory().js']);
if (realpathSync(foundationHome) !== account.home) throw new Error('foundation_home_mismatch');
receipt.preflight.foundationHome = true;
receipt.keychainHome = prepareKeychainHome(account.home, account.uid);
receipt.dependencyDirectoryPresentBeforeInstall = true;
for (const executable of [account.bun, account.destinationExecutable]) {
accessSync(executable, constants.X_OK);
if (!path.isAbsolute(executable) || realpathSync(executable) !== executable || !executable.startsWith(account.work + path.sep)) throw new Error('staged_executable_escape');
}
if (await digest(account.bun) !== account.bunSha256 || await digest(account.destinationExecutable) !== account.destinationSha256) throw new Error('staged_executable_changed');
receipt.reason = 'fresh_dependency_install';
run(account.bun, ['install', '--frozen-lockfile', '--ignore-scripts'], 180_000);
if (Bun.version !== '1.4.0' || require(path.join(account.snapshot, 'node_modules/playwright/package.json')).version !== '1.62.1') throw new Error('pinned_runtime_mismatch');
mkdirSync(probe, { mode: 0o700 });
receipt.reason = 'background_keychain_preflight';
snapshot = captureUserKeychains(env, [account.home, account.temporary]);
const password = randomBytes(24).toString('hex');
const value = randomBytes(24).toString('hex');
keychainChanged = true;
run('/usr/bin/security', ['create-keychain', '-p', password, keychain]);
keychainCreated = true;
run('/usr/bin/security', ['set-keychain-settings', '-lut', '300', keychain]);
run('/usr/bin/security', ['unlock-keychain', '-p', password, keychain]);
run('/usr/bin/security', ['list-keychains', '-d', 'user', '-s', keychain]);
run('/usr/bin/security', ['default-keychain', '-d', 'user', '-s', keychain]);
run('/usr/bin/security', ['add-generic-password', '-s', 'Gstack Native Probe', '-a', 'fixture', '-w', value,
'-T', '/usr/bin/security', keychain]);
const observed = observeFixtureKeychain(env, [account.home, account.temporary], keychain, value);
receipt.keychainObservations = { ...observed, preferencesFileExists: existsSync(path.join(account.home, 'Library/Preferences/com.apple.security.plist')) };
if (!observed.searchPathMatches || !observed.defaultPathMatches || !observed.explicitReadMatches) throw new Error('native_keychain_probe_failed');
receipt.preflight.keychain = true;
if (account.launchComparison) {
receipt.reason = 'comparison_chromium_control';
launchAttempted = true;
comparisonControl = await runDiaLaunchComparison(account, 'control');
receipt.comparisonControl = comparisonControl;
if (!comparisonControl.ready || !comparisonControl.cleanup?.confirmed) throw new Error('comparison_control_failed');
receipt.preflight.headlessChromium = true;
receipt.status = 'passed';
receipt.reason = 'background_session_ready';
} else {
receipt.browserPreflight = { stage: 'runtime_import', launchReturned: false, ownedRootCount: 0,
startupPageCount: null, startupPageCategories: [], pageSelected: false, contentSet: false, readbackMatched: false };
receipt.reason = 'background_browser_runtime_import';
const { chromium } = await import('playwright');
const profile = path.join(probe, 'chromium');
observer = observeBrowserLaunches(new Map([[account.destinationExecutable, profile]]));
launchAttempted = true;
receipt.browserPreflight.stage = 'launch';
receipt.reason = 'background_browser_launch';
context = await limit(chromium.launchPersistentContext(profile, nativeDiaLaunchOptions(account.destinationExecutable, env)), 40_000);
receipt.browserPreflight.launchReturned = true;
receipt.browserPreflight.stage = 'ownership';
receipt.reason = 'background_browser_ownership';
receipt.browserPreflight.ownedRootCount = observer.children.length;
if (observer.children.length !== 1) throw new Error('background_browser_ownership_failed');
receipt.browserPreflight.stage = 'startup_pages';
receipt.reason = 'background_browser_startup_pages';
const pages = context.pages();
const startupUrls = pages.map((page: any) => page.url());
receipt.browserPreflight.startupPageCount = pages.length;
receipt.browserPreflight.startupPageCategories = startupUrls.map(browserStartupCategory);
if (startupUrls.some((url: string) => url !== 'about:blank')) throw new Error('background_browser_startup_page_rejected');
receipt.browserPreflight.stage = 'page_selection';
receipt.reason = 'background_browser_page_selection';
const page = pages[0] ?? await limit(context.newPage(), 5_000);
receipt.browserPreflight.pageSelected = true;
receipt.browserPreflight.stage = 'content_set';
receipt.reason = 'background_browser_content_set';
await limit(page.setContent('<div id="fixture">background browser ready</div>'), 5_000);
receipt.browserPreflight.contentSet = true;
receipt.browserPreflight.stage = 'readback';
receipt.reason = 'background_browser_readback';
receipt.browserPreflight.readbackMatched = await limit(page.locator('#fixture').innerText(), 5_000) === 'background browser ready';
if (!receipt.browserPreflight.readbackMatched) throw new Error('background_browser_render_failed');
receipt.browserPreflight.stage = 'completed';
receipt.preflight.headlessChromium = true;
receipt.status = 'passed';
receipt.reason = 'background_session_ready';
}
} catch (error) {
receipt.status = 'incomplete';
if (error instanceof Error && ['fresh_identity_mismatch', 'fresh_directory_ownership_mismatch', 'fresh_registered_identity_mismatch',
'foundation_home_mismatch', 'staged_executable_escape', 'staged_executable_changed', 'pinned_runtime_mismatch',
'user_keychain_search_unavailable', 'user_default_keychain_unavailable', 'keychain_outside_owned_home_refused',
'keychain_home_unsafe', 'fixture_keychain_not_owned', 'native_keychain_probe_failed', 'comparison_control_failed',
'gui_readiness_inputs_changed', 'gui_readiness_budget_exhausted'].includes(error.message)) receipt.blocker = error.message;
if (receipt.browserPreflight) {
receipt.blocker = browserPreflightError(error);
receipt.browserPreflight.error = receipt.blocker;
if (receipt.browserPreflight.stage === 'runtime_import') receipt.browserPreflight.moduleLoad = playwrightModuleLoadFacts(account.snapshot, error);
receipt.browserPreflight.ownedRootCount = observer?.children.length ?? 0;
receipt.browserPreflight.launchAttempts = observer?.attempts ?? [];
}
receipt.initialFailure = { stage: receipt.reason, blocker: receipt.blocker ?? 'native_preflight_failed' };
} finally {
cleaning = true;
if (receipt.browserPreflight) {
receipt.browserPreflight.launchAttempts ??= observer?.attempts ?? [];
receipt.browserPreflight.rootStatesBeforeCleanup = (observer?.children ?? []).map(child => ({
pid: child.pid, exitCode: Number.isInteger(child.process.exitCode) ? child.process.exitCode : null,
signal: child.process.signalCode == null ? null
: ['SIGABRT', 'SIGTRAP', 'SIGSEGV', 'SIGBUS', 'SIGKILL', 'SIGTERM', 'SIGILL'].includes(child.process.signalCode) ? child.process.signalCode : 'other',
}));
}
observer?.stop();
if (context) await limit(context.close().catch(() => {}), 5_000).catch(() => {});
let stopped = !launchAttempted || (account.launchComparison ? comparisonControl?.cleanup?.confirmed === true : observer?.children.length === 1);
for (const child of observer?.children ?? []) {
const until = performance.now() + 5_000;
try {
await stopOwnedBrowserGroup(child, until, {});
} catch { stopped = false; }
}
receipt.cleanup.probeBrowsersStopped = stopped;
if (stopped) {
try {
if (keychainChanged && snapshot) {
let restored = true;
for (const args of fixtureKeychainRestoreCommands(snapshot, keychain, keychainCreated)) {
try { run('/usr/bin/security', args); } catch { restored = false; }
}
if (!restored || JSON.stringify(captureUserKeychains(env, [account.home, account.temporary])) !== JSON.stringify(snapshot)) throw new Error('probe_keychain_restore_failed');
}
receipt.cleanup.probeKeychainRestored = true;
if (existsSync(probe) && realpathSync(probe) === probe) rmSync(probe, { recursive: true, force: true });
} catch { receipt.cleanupFailure = 'probe_keychain_restore_failed'; }
}
if (!receipt.cleanup.probeBrowsersStopped || !receipt.cleanup.probeKeychainRestored) { receipt.status = 'incomplete'; receipt.reason = 'background_probe_cleanup_incomplete'; }
writePrivateReceipt(preflightFile, receipt, true);
}
if (receipt.status !== 'passed') return 2;
const result = spawnSync(account.bun, ['--no-env-file', '--no-install', '--no-macros', '--config=/dev/null',
path.join(account.snapshot, '.github/scripts/qualify-dia-macos.ts'), '--fresh-account', account.configFile], {
cwd: account.snapshot, env, stdio: 'ignore', timeout: 660_000, killSignal: 'SIGKILL',
});
return !result.error && result.status === 0 ? 0 : 2;
}
export async function runFreshAccountQualification(comparisonRuntime?: 'bun' | 'node', guiReadinessOnly = false) {
validateQualificationHost(process.env);
if (comparisonRuntime !== undefined && !['bun', 'node'].includes(comparisonRuntime)) throw new Error('invalid_comparison_runtime');
if (guiReadinessOnly && comparisonRuntime !== undefined) throw new Error('conflicting_diagnostic_modes');
if (process.getuid?.() === 0 || Bun.version !== '1.4.0') throw new Error('run_as_unprivileged_pinned_ci_runner');
const outputRoot = realpathSync(process.env.RUNNER_TEMP!);
const output = path.join(outputRoot, 'dia-native-qualification.json');
if (existsSync(output)) throw new Error('fresh_output_required');
const work = realpathSync(mkdtempSync(FRESH_WORK_PREFIX));
const home = path.join(work, 'home');
const temporary = path.join(work, 'tmp');
const snapshot = path.join(work, 'repo');
const bin = path.join(work, 'bin');
const browserDirectory = path.join(work, 'browser');
const archive = path.join(work, 'source.tar');
const suffix = randomBytes(6).toString('hex');
const accountName = 'gsdia' + suffix;
const label = 'ai.gstack.dia.' + suffix;
const deadline = performance.now() + 16 * 60_000;
const hostEnv = { HOME: homedir(), PATH: '/usr/bin:/bin:/usr/sbin:/sbin', LANG: 'en_US.UTF-8' };
let cleanupDeadline = 0;
const run = (command: string, args: string[], timeout = 10_000) => {
const remaining = (cleanupDeadline || deadline) - performance.now();
if (remaining <= 0) throw new Error('fresh_launcher_deadline');
return safeCommand(command, args, Math.min(timeout, remaining), hostEnv);
};
const rootCommand = (command: string, args: string[], timeout = 10_000) => run('/usr/bin/sudo', ['-n', command, ...args], timeout);
let account: FreshAccount | undefined;
let userCreated = false;
let groupCreated = false;
let serviceAttempted = false;
let domainBeforeCreation: ParentDomainObservation | undefined;
let stage = 'fresh_launcher_preflight';
const receipt: Record<string, any> = { status: 'incomplete', reason: stage, runId: process.env.GITHUB_RUN_ID, runAttempt: process.env.GITHUB_RUN_ATTEMPT,
counts: { pass: 0, fail: 0, skip: 0 }, launcher: { sessionCreate: true, aquaLogin: false },
launcherCleanup: { serviceStopped: false, userDomainStopped: false, userProcessesStopped: false, accountRemoved: false, groupRemoved: false, stagingRemoved: false } };
let workerExit: number | undefined;
let pythonExecutable: string | undefined;
const probeParentDomain = (uid: number) => inspectParentDomain(uid, cleanupDeadline || deadline, hostEnv);
const snapshotProcesses = (phase: string, uid: number) => {
const facts = inspectUidProcesses(uid, cleanupDeadline || deadline, hostEnv);
(receipt.uidProcessSnapshots ??= {})[phase] = facts;
return facts;
};
try {
rootCommand('/usr/bin/true', []);
for (const directory of [home, temporary, snapshot, bin, ...(guiReadinessOnly ? [] : [browserDirectory])]) mkdirSync(directory, { mode: 0o700 });
assertDiaSocketPath(path.join(home, 'Library/Application Support/Dia/User Data'));
writeFileSync(path.join(temporary, 'dia-background-preflight.json'), JSON.stringify({ status: 'incomplete', reason: 'fresh_worker_not_started',
nativeCasesRun: false, preflight: { registeredIdentity: false, foundationHome: false, keychain: false, headlessChromium: false } }) + '\n', { mode: 0o600, flag: 'wx' });
const sourceRevision = run('/usr/bin/git', ['-C', repository, 'rev-parse', 'HEAD']);
if (!/^[0-9a-f]{40}$/.test(sourceRevision)) throw new Error('invalid_source_revision');
const python = Bun.which('python3');
if (!python) throw new Error('archive_validator_unavailable');
pythonExecutable = realpathSync(python);
stage = 'source_archive_preflight';
run('/usr/bin/git', ['-C', repository, 'archive', '--format=tar', '--output', archive, 'HEAD'], 30_000);
const archiveResult = JSON.parse(run(pythonExecutable, ['-I', '-c', ARCHIVE_CHECK, archive], 30_000));
if (archiveResult.valid !== true) throw new Error('unsafe_source_archive');
run('/usr/bin/tar', ['--no-same-owner', '--no-same-permissions', '-xf', archive, '-C', snapshot], 30_000);
if (!guiReadinessOnly) mkdirSync(path.join(snapshot, 'node_modules'), { mode: 0o700 });
const sourceBun = realpathSync(process.execPath);
const bun = path.join(bin, 'bun');
copyFileSync(sourceBun, bun);
chmodSync(bun, 0o755);
let guiReadiness: FreshAccount['guiReadiness'];
if (guiReadinessOnly) {
stage = 'gui_readiness_build';
const source = path.join(snapshot, '.github/scripts/dia-gui-readiness.c');
const executable = path.join(bin, 'gui-readiness');
const sourceSha256 = await digest(source);
run('/usr/bin/xcrun', ['clang', '-arch', 'arm64', '-std=c11', '-O2', '-Wall', '-Wextra', source,
'-framework', 'Security', '-framework', 'ApplicationServices', '-o', executable], 30_000);
if (await digest(source) !== sourceSha256 || !inspectMachOArchitectures(executable).architectures.includes('arm64')) throw new Error('gui_readiness_build_unconfirmed');
chmodSync(executable, 0o755);
guiReadiness = { mode: 'gui-readiness-only', executable, sourceSha256, executableSha256: await digest(executable) };
stage = 'gui_readiness_original_runner';
receipt.guiReadiness = { mode: 'gui-readiness-only', qualificationCredit: false,
helper: { sourceSha256, executableSha256: guiReadiness.executableSha256 },
originalRunner: await runGuiReadiness(executable, guiReadiness.executableSha256, source, sourceSha256, true, hostEnv,
Math.min(5000, deadline - performance.now())) };
}
let launchComparison: FreshAccount['launchComparison'];
if (comparisonRuntime) {
let executable = bun;
if (comparisonRuntime === 'node') {
const sourceNode = Bun.which('node');
if (!sourceNode) throw new Error('pinned_node_unavailable');
const resolvedNode = realpathSync(sourceNode);
const node = JSON.parse(run(resolvedNode, ['-p', 'JSON.stringify({version:process.versions.node,arch:process.arch,os:process.platform,bun:Boolean(process.versions.bun)})']));
if (node.version !== '24.18.0' || node.arch !== 'arm64' || node.os !== 'darwin' || node.bun) throw new Error('pinned_node_required');
executable = path.join(bin, 'node');
copyFileSync(resolvedNode, executable);
chmodSync(executable, 0o755);
}
launchComparison = { mode: 'launch-only', runtime: comparisonRuntime, executable, executableSha256: await digest(executable),
driverSha256: await digest(path.join(snapshot, '.github/scripts/dia-launch-driver.mjs')),
helpersSha256: await digest(path.join(snapshot, '.github/scripts/qualify-dia-macos.ts')) };
}
let destination: Pick<FreshAccount, 'destinationExecutable' | 'destinationSha256'> = {};
if (!guiReadinessOnly) {
const { chromium } = await import('playwright');
if (require('playwright/package.json').version !== '1.62.1') throw new Error('pinned_playwright_required');
const originalExecutable = realpathSync(chromium.executablePath());
let bundle = path.dirname(originalExecutable);
while (!bundle.endsWith('.app')) {
const parent = path.dirname(bundle);
if (parent === bundle) throw new Error('destination_app_bundle_missing');
bundle = parent;
}
const copiedBundle = path.join(browserDirectory, path.basename(bundle));
run('/usr/bin/ditto', ['--rsrc', '--extattr', bundle, copiedBundle], 45_000);
const destinationExecutable = realpathSync(path.join(copiedBundle, path.relative(bundle, originalExecutable)));
if (!destinationExecutable.startsWith(browserDirectory + path.sep)) throw new Error('destination_bundle_escape');
destination = { destinationExecutable, destinationSha256: await digest(originalExecutable) };
}
const userIds = parseDirectoryIds(run('/usr/bin/dscl', ['.', '-list', '/Users', 'UniqueID']));
const groupIds = parseDirectoryIds(run('/usr/bin/dscl', ['.', '-list', '/Groups', 'PrimaryGroupID']));
const used = new Set([...userIds, ...groupIds]);
for (const uid of run('/bin/ps', ['-axo', 'uid=']).split(/\s+/).filter(Boolean)) used.add(Number(uid));
let uid = 20_000;
while (used.has(uid) && uid < 60_000) uid++;
if (uid >= 60_000) throw new Error('fresh_uid_unavailable');
stage = 'fresh_user_domain_preflight';
domainBeforeCreation = probeParentDomain(uid);
receipt.userDomain = { beforeCreation: domainBeforeCreation };
receipt.candidateIdentity = { uid, accountUidAbsent: !userIds.has(uid), groupUidAbsent: !groupIds.has(uid) };
const candidateProcesses = snapshotProcesses('before_account_creation', uid);
if (passiveUserDomainState(domainBeforeCreation, uid) !== 'absent' || !('count' in candidateProcesses) || candidateProcesses.count !== 0
|| !receipt.candidateIdentity.accountUidAbsent || !receipt.candidateIdentity.groupUidAbsent) {
throw new Error('candidate_domain_baseline_unconfirmed');
}
const configFile = path.join(work, 'account.json');
const metadata = Object.fromEntries(['CI', 'GITHUB_ACTIONS', 'RUNNER_ENVIRONMENT', 'RUNNER_OS', 'RUNNER_ARCH', 'GITHUB_RUN_ID',
'GITHUB_RUN_ATTEMPT', 'GSTACK_DIA_NATIVE_QUALIFY'].map(name => [name, process.env[name]!]));
account = { work, home, temporary, snapshot, bun, ...destination, uid, gid: uid, account: accountName,
...(launchComparison ? { launchComparison } : {}), ...(guiReadiness ? { guiReadiness } : {}),
guid: randomUUID().toUpperCase(), groupGuid: randomUUID().toUpperCase(), label, sourceRevision,
archiveSha256: await digest(archive), bunSha256: await digest(bun), configFile,
environment: { ...metadata, HOME: home, TMPDIR: temporary, RUNNER_TEMP: temporary, PATH: bin + ':/usr/bin:/bin:/usr/sbin:/sbin', LANG: 'en_US.UTF-8',
GSTACK_DIA_EXPECT_UID: String(uid), GSTACK_DIA_SOURCE_REVISION: sourceRevision,
...(destination.destinationExecutable ? { GSTACK_DIA_DESTINATION_EXECUTABLE: destination.destinationExecutable } : {}) } };
stage = 'fresh_account_creation';
rootCommand('/usr/bin/dscl', ['.', '-create', '/Groups/' + accountName]);
groupCreated = true;
for (const [name, value] of [['GeneratedUID', account.groupGuid], ['PrimaryGroupID', String(uid)], ['RealName', 'gstack native fixture group']]) {
rootCommand('/usr/bin/dscl', ['.', '-create', '/Groups/' + accountName, name, value]);
}
rootCommand('/usr/bin/dscl', ['.', '-create', '/Users/' + accountName]);
userCreated = true;
for (const [name, value] of [['GeneratedUID', account.guid], ['UniqueID', String(uid)], ['PrimaryGroupID', String(uid)],
['NFSHomeDirectory', home], ['UserShell', '/usr/bin/false'], ['RealName', 'gstack native fixture'], ['IsHidden', '1'], ['Password', '*']]) {
rootCommand('/usr/bin/dscl', ['.', '-create', '/Users/' + accountName, name, value]);
}
if (!ownsFreshAccount(parseDirectoryRecord(run('/usr/bin/dscl', ['.', '-read', '/Users/' + accountName, 'UniqueID', 'PrimaryGroupID', 'NFSHomeDirectory', 'GeneratedUID'])), account)) throw new Error('fresh_account_not_registered');
for (const directory of [home, temporary, snapshot, bin, ...(guiReadinessOnly ? [] : [browserDirectory])]) rootCommand('/usr/sbin/chown', ['-R', '-P', `${uid}:${uid}`, directory], 30_000);
writeFileSync(configFile, JSON.stringify(account), { mode: 0o644, flag: 'wx' });
const json = path.join(work, 'service.json');
const plist = path.join(work, label + '.plist');
writeFileSync(json, JSON.stringify(freshLaunchDefinition(account)), { mode: 0o600, flag: 'wx' });
run('/usr/bin/plutil', ['-convert', 'xml1', '-o', plist, json]);
rootCommand('/usr/sbin/chown', ['root:wheel', configFile, plist, work]);
rootCommand('/bin/chmod', ['644', configFile, plist]);
rootCommand('/bin/chmod', ['755', work]);
stage = 'background_session_bootstrap';
serviceAttempted = true;
rootCommand('/bin/launchctl', ['bootstrap', 'system', plist]);
stage = 'background_session_probe';
while (performance.now() < deadline) {
const state = rootCommand('/bin/launchctl', ['print', 'system/' + label]);
const exit = state.match(/^\s*last exit code = (\d+)\s*$/m);
const running = /^\s*pid = \d+\s*$/m.test(state);
if (!running && exit) { workerExit = Number(exit[1]); break; }
await Bun.sleep(500);
}
if (workerExit === undefined) throw new Error('background_session_timeout');
receipt.reason = workerExit === 0 ? 'fresh_account_qualification_completed' : 'fresh_account_preflight_or_qualification_failed';
} catch (error) {
receipt.reason = stage;
receipt.failureStage = stage;
if ((error as { diagnostic?: object }).diagnostic) receipt.commandFailure = (error as { diagnostic: object }).diagnostic;
} finally {
cleanupDeadline = performance.now() + 60_000;
if (serviceAttempted && account) {
try {
if (!ownsLaunchService(rootCommand('/bin/launchctl', ['print', 'system/' + label]), account)) throw new Error('service_identity_changed');
rootCommand('/bin/launchctl', ['bootout', 'system/' + label], 10_000);
receipt.launcherCleanup.serviceStopped = true;
} catch {}
} else receipt.launcherCleanup.serviceStopped = true;
let owned = false;
if (account && userCreated) {
try { owned = ownsFreshAccount(parseDirectoryRecord(run('/usr/bin/dscl', ['.', '-read', '/Users/' + accountName, 'UniqueID', 'PrimaryGroupID', 'NFSHomeDirectory', 'GeneratedUID'])), account); } catch {}
}
if (owned && account) {
const collect = (phase: string) => {
const results: Record<string, string> = {};
for (const [name, filename] of [['backgroundPreflight', 'dia-background-preflight.json'], ['qualification', 'dia-native-qualification.json']]) {
try {
if (!pythonExecutable) throw new Error('receipt_reader_unavailable');
const text = rootCommand(pythonExecutable, ['-I', '-c', PRIVATE_RECEIPT_READ, path.join(temporary, filename), String(account!.uid), work], 3_000);
if (text.length > 1024 * 1024) throw new Error('oversized_receipt');
receipt[name] = JSON.parse(text);
results[name] = 'captured';
} catch { results[name] = 'unavailable'; }
}
(receipt.diagnosticCollection ??= {})[phase] = results;
};
const active = () => {
const facts = inspectUidProcesses(account!.uid, cleanupDeadline, hostEnv);
if (!('count' in facts)) throw new Error('uid_process_snapshot_unavailable');
return facts.count !== 0;
};
collect('before_signal');
snapshotProcesses('before_signal', account.uid);
let domainOwnershipConfirmed = false;
try {
if (!receipt.launcherCleanup.serviceStopped) throw new Error('service_still_loaded');
const record = parseDirectoryRecord(run('/usr/bin/dscl', ['.', '-read', '/Users/' + accountName, 'UniqueID', 'PrimaryGroupID', 'NFSHomeDirectory', 'GeneratedUID']));
const before = probeParentDomain(account.uid);
(receipt.userDomain ??= {}).beforeTeardown = before;
const domain = ownedUserDomainTarget(record, account, domainBeforeCreation, before);
domainOwnershipConfirmed = true;
if (domain !== null) {
try { rootCommand('/bin/launchctl', ['bootout', domain], 10_000); }
catch (error) {
receipt.userDomain.teardownCommandFailure = (error as { diagnostic?: object }).diagnostic ?? { failed: true };
}
}
receipt.userDomain.afterTeardown = probeParentDomain(account.uid);
receipt.launcherCleanup.userDomainStopped = passiveUserDomainState(receipt.userDomain.afterTeardown, account.uid) === 'absent';
} catch { (receipt.userDomain ??= {}).teardownRefusedOrUnconfirmed = true; }
snapshotProcesses('after_domain_teardown', account.uid);
try {
if (!domainOwnershipConfirmed || !receipt.launcherCleanup.userDomainStopped) throw new Error('user_domain_ownership_or_absence_unconfirmed');
if (active()) {
const record = parseDirectoryRecord(run('/usr/bin/dscl', ['.', '-read', '/Users/' + accountName, 'UniqueID', 'PrimaryGroupID', 'NFSHomeDirectory', 'GeneratedUID']));
if (!ownsFreshAccount(record, account)) throw new Error('account_identity_changed');
try { rootCommand('/usr/bin/pkill', ['-KILL', '-u', String(account.uid)]); } catch {}
const until = Math.min(cleanupDeadline, performance.now() + 10_000);
snapshotProcesses('after_signal', account.uid);
while (active() && performance.now() < until) await Bun.sleep(100);
}
receipt.launcherCleanup.userProcessesStopped = !active();
} catch {}
snapshotProcesses('after_wait', account.uid);
if (domainOwnershipConfirmed) {
try {
receipt.userDomain.afterWait = probeParentDomain(account.uid);
receipt.launcherCleanup.userDomainStopped = passiveUserDomainState(receipt.userDomain.afterWait, account.uid) === 'absent';
} catch { receipt.launcherCleanup.userDomainStopped = false; }
}
collect('after_wait');
if (receipt.launcherCleanup.userProcessesStopped) {
try {
if (!receipt.launcherCleanup.serviceStopped || !receipt.launcherCleanup.userDomainStopped) throw new Error('owned_domain_or_service_still_loaded');
const record = parseDirectoryRecord(run('/usr/bin/dscl', ['.', '-read', '/Users/' + accountName, 'UniqueID', 'PrimaryGroupID', 'NFSHomeDirectory', 'GeneratedUID']));
if (!ownsFreshAccount(record, account)) throw new Error('account_identity_changed');
receipt.userDomain.beforeAccountRemoval = probeParentDomain(account.uid);
receipt.launcherCleanup.userDomainStopped = passiveUserDomainState(receipt.userDomain.beforeAccountRemoval, account.uid) === 'absent';
if (!receipt.launcherCleanup.userDomainStopped) throw new Error('fresh_uid_domain_reappeared');
if (active()) { receipt.launcherCleanup.userProcessesStopped = false; throw new Error('fresh_uid_processes_reappeared'); }
rootCommand('/usr/bin/dscl', ['.', '-delete', '/Users/' + accountName]);
receipt.launcherCleanup.accountRemoved = true;
} catch {}
}
} else if (!userCreated) {
receipt.launcherCleanup.userDomainStopped = true;
receipt.launcherCleanup.userProcessesStopped = true;
receipt.launcherCleanup.accountRemoved = true;
}
if (account && groupCreated && receipt.launcherCleanup.accountRemoved) {
try {
const group = parseDirectoryRecord(run('/usr/bin/dscl', ['.', '-read', '/Groups/' + accountName, 'GeneratedUID', 'PrimaryGroupID']));
if (group.GeneratedUID?.toUpperCase() !== account.groupGuid || group.PrimaryGroupID !== String(account.gid)) throw new Error('group_identity_changed');
rootCommand('/usr/bin/dscl', ['.', '-delete', '/Groups/' + accountName]);
receipt.launcherCleanup.groupRemoved = true;
} catch {}
} else if (!groupCreated) receipt.launcherCleanup.groupRemoved = true;
const mountSafe = !serviceAttempted || (receipt.qualification ? receipt.qualification.cleanup?.mountDetached === true
: receipt.backgroundPreflight?.status === 'incomplete');
if (receipt.launcherCleanup.serviceStopped && receipt.launcherCleanup.userDomainStopped && receipt.launcherCleanup.userProcessesStopped && receipt.launcherCleanup.accountRemoved
&& receipt.launcherCleanup.groupRemoved && mountSafe && (workerExit !== undefined || !serviceAttempted)) {
try {
const owner = lstatSync(work).uid;
if (realpathSync(work) !== work || path.dirname(work) !== '/private/tmp' || !path.basename(work).startsWith(path.basename(FRESH_WORK_PREFIX))
|| (owner !== 0 && owner !== process.getuid?.())) throw new Error('staging_identity_changed');
rootCommand('/bin/rm', ['-rf', '--', work], 20_000);
receipt.launcherCleanup.stagingRemoved = true;
} catch {}
}
if (receipt.qualification) receipt.counts = receipt.qualification.counts;
if (account) receipt.launcher = { ...receipt.launcher, uid: account.uid, gid: account.gid, accountGuid: account.guid, groupGuid: account.groupGuid, serviceLabel: account.label,
sourceRevision: account.sourceRevision, archiveSha256: account.archiveSha256, bunSha256: account.bunSha256, destinationSha256: account.destinationSha256 };
if (account?.launchComparison) receipt.launchComparison = { mode: 'launch-only', runtime: account.launchComparison.runtime,
executableSha256: account.launchComparison.executableSha256, driverSha256: account.launchComparison.driverSha256,
helpersSha256: account.launchComparison.helpersSha256, qualificationCredit: false };
const clean = Object.values(receipt.launcherCleanup).every(value => value === true);
receipt.workerExitCode = workerExit ?? null;
receipt.status = !account?.launchComparison && !account?.guiReadiness && freshQualificationPassed(workerExit, receipt.backgroundPreflight?.status, receipt.qualification?.status, receipt.launcherCleanup) ? 'passed' : 'incomplete';
if (account?.guiReadiness) {
receipt.reason = 'gui_readiness_only';
receipt.guiReadiness.freshUser = receipt.backgroundPreflight?.guiReadiness ?? { available: false, reason: 'fresh_probe_receipt_unavailable' };
}
if (account?.launchComparison && receipt.qualification?.reason === 'diagnostic_launch_comparison_only') receipt.reason = 'diagnostic_launch_comparison_only';
if (!clean) receipt.recovery = 'Discard this disposable runner. Do not reuse its account, session, profile, or Keychain.';
if (receipt.backgroundPreflight?.status !== 'passed' && receipt.backgroundPreflight) receipt.reason = receipt.backgroundPreflight.reason;
writePrivateReceipt(output, receipt);
}
return receipt;
}
if (import.meta.main) {
try {
if (process.argv[2] === '--fresh-worker') process.exitCode = await freshWorker(process.argv[3]);
else {
const args = process.argv.slice(2);
const readinessOnly = args.length === 1 && args[0] === '--gui-readiness-only';
if (args.length && !readinessOnly && (args.length !== 2 || args[0] !== '--launch-comparison' || !['bun', 'node'].includes(args[1]))) throw new Error('invalid_comparison_arguments');
const receipt = await runFreshAccountQualification(args[1] as 'bun' | 'node' | undefined, readinessOnly);
console.log(JSON.stringify({ status: receipt.status, reason: receipt.reason, counts: receipt.counts, artifact: 'dia-native-qualification.json' }));
process.exitCode = receipt.status === 'passed' ? 0 : 2;
}
} catch {
console.log(JSON.stringify({ status: 'incomplete', reason: 'fresh_account_launcher_preflight_failed', counts: { pass: 0, fail: 0, skip: 0 } }));
process.exitCode = 2;
}
}
+80 -10
View File
@@ -8,6 +8,11 @@ on:
description: 'Run ALL gate tests in the sliced lane (bypass diff selection; also arms the hollow-shard guard)'
type: boolean
default: true
validation_phase:
description: 'Validation branch phase; run quality before behavior on unchanged inputs'
type: choice
options: [all, quality, cookie-quality, behavior, cookie-behavior]
default: all
concurrency:
group: evals-${{ github.event.pull_request.number || github.run_id }}
@@ -133,10 +138,37 @@ jobs:
bun-version: 1.4.0
- name: Emit run manifest
if: github.event_name != 'workflow_dispatch' || inputs.validation_phase == 'all'
env:
EVALS_ALL: ${{ (github.event_name == 'workflow_dispatch' && inputs.evals_all) && '1' || '' }}
run: EVALS_TIER=gate bun --no-install run scripts/test-paid-shards.ts --tier gate --emit-plan /tmp/paid-plan/manifest.json --slices 6
- name: Emit validation-phase manifest
if: github.event_name == 'workflow_dispatch' && inputs.validation_phase != 'all'
env:
VALIDATION_PHASE: ${{ inputs.validation_phase }}
EVALS_ALL: ${{ inputs.evals_all && '1' || '' }}
EVALS_TIER: gate
run: |
bun --no-install -e '
import { mkdirSync, writeFileSync } from "node:fs";
import { buildRunManifest, collectPaidTestFiles } from "./scripts/test-paid-shards.ts";
const phase = process.env.VALIDATION_PHASE;
if (!["quality", "cookie-quality", "behavior", "cookie-behavior"].includes(phase)) throw new Error("Invalid validation phase");
const cookieBehavior = phase === "cookie-behavior";
const discovered = phase === "cookie-quality" ? ["test/skill-llm-eval.test.ts"]
: cookieBehavior ? ["test/skill-e2e-bws.test.ts", "test/skill-e2e-qa-workflow.test.ts", "test/skill-e2e-design.test.ts", "test/skill-e2e-diagram.test.ts", "test/skill-e2e-deploy.test.ts"]
: collectPaidTestFiles().filter(file => file.startsWith("test/skill-llm-eval") === (phase === "quality"));
const manifest = buildRunManifest({ tier: "gate", profile: "full", sliceCount: 6, evalsAll: !cookieBehavior && process.env.EVALS_ALL === "1", discovered,
...(cookieBehavior ? { changedFiles: ["browse/src/cookie-picker-routes.ts", "browse/src/cookie-import-browser.ts", "browse/src/bun-polyfill.cjs"], env: { ...process.env, EVALS_ALL: "" } } : {}) });
if (phase === "cookie-quality") manifest.selection = { e2e: [], judges: ["setup-browser-cookies/SKILL.md workflow"] };
if (cookieBehavior) manifest.selection = { e2e: ["browse-basic", "browse-snapshot", "qa-quick", "qa-only-no-fix", "design-review-detector-shim-dom", "diagram-triplet", "canary-workflow", "benchmark-workflow"], judges: [] };
manifest.selectionReason = phase + " validation subset; " + manifest.selectionReason;
mkdirSync("/tmp/paid-plan", { recursive: true });
writeFileSync("/tmp/paid-plan/manifest.json", JSON.stringify(manifest, null, 2) + "\n");
console.log(phase + ": " + manifest.entries.filter(entry => entry.status === "planned").length + " planned shards");
'
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: paid-plan
@@ -152,9 +184,9 @@ jobs:
# 40-way per row queued claude session STARTUP behind 39 siblings and ate
# per-test budgets — the documented timeout-flake family). Tune with
# parity data before raising.
# The complete gate census needs at most 197 minutes per slice; keep
# The complete gate census needs at most 201 minutes per slice; keep
# 20 minutes for setup/upload without preempting configured retries.
timeout-minutes: 220
timeout-minutes: 221
permissions:
contents: read
packages: read
@@ -334,7 +366,9 @@ jobs:
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: report-verdict
path: /tmp/report.txt
path: |
/tmp/report.txt
/tmp/paid-report/collector-outcomes.json
if-no-files-found: ignore
retention-days: 30
@@ -374,7 +408,8 @@ jobs:
path: /tmp/verdict
continue-on-error: true
# Sourced from the slice artifacts' eval-store JSONs. Keeps the
# Verified counts come from the read-only report job, not repo code in
# this write-token job. Keeps the
# "## E2E Evals" marker so the upsert keeps updating the same comment.
# Runs even when reconciliation failed — a red lane on the PR is the point.
- name: Post PR comment
@@ -384,8 +419,36 @@ jobs:
run: |
# shellcheck disable=SC2086,SC2059
RESULTS=$(find /tmp/paid-report -name '*.json' ! -name 'manifest.json' ! -name 'slice-*.json' ! -name '_partial*' 2>/dev/null | sort)
TOTAL=0; PASSED=0; FAILED=0; FLAKY=0; EXECUTED=0; REUSED=0; COST="0"
TOTAL=0; PASSED=0; FAILED=0; MANUAL=0; FLAKY=0; EXECUTED=0; REUSED=0; COST="0"
SUITE_LINES=""
VERIFIED=/tmp/verdict/paid-report/collector-outcomes.json
if ! jq -e '
. as $summary |
.version == 1 and (.files | type == "array") and (.totals | type == "object") and
([.files[] | .total == (.passed + .failed + .manual_accepted) and
(.total == (.executed + .reused)) and
([.total,.passed,.failed,.manual_accepted,.executed,.reused,.attempts,.flaky] | all(. >= 0 and (floor == .))) ] | all) and
(.totals | .total == (.passed + .failed + .manual_accepted) and .total == (.executed + .reused)) and
(["total","passed","failed","manual_accepted","executed","reused","attempts","flaky"] |
all(. as $key | ([$summary.files[] | .[$key]] | add // 0) == $summary.totals[$key]))
' "$VERIFIED" >/dev/null 2>&1; then
VERIFIED=""
echo 'Verified collector summary unavailable; manual acceptance is unavailable/unverified.'
fi
if [ -n "$VERIFIED" ]; then
while IFS=$'\t' read -r f T P F M FL EX RE _ATTEMPTS C TIER SHARD; do
[ "$T" -eq 0 ] && continue
TOTAL=$((TOTAL + T)); PASSED=$((PASSED + P)); FAILED=$((FAILED + F))
MANUAL=$((MANUAL + M)); FLAKY=$((FLAKY + FL))
EXECUTED=$((EXECUTED + EX)); REUSED=$((REUSED + RE))
COST=$(echo "$COST + $C" | bc)
STATUS_ICON="✅"
[ "$M" -gt 0 ] && STATUS_ICON="⚠ manual/unscored"
[ "$F" -gt 0 ] && STATUS_ICON="❌"
[ "$F" -eq 0 ] && [ "$M" -eq 0 ] && [ "$FL" -gt 0 ] && STATUS_ICON="✅⚠"
SUITE_LINES="${SUITE_LINES}| ${TIER}/${SHARD} | ${P}/${T} | ${M} | ${EX} | ${RE} | ${STATUS_ICON} | \$${C} |\n"
done < <(jq -r '.files[] | [.file,.total,.passed,.failed,.manual_accepted,.flaky,.executed,.reused,.attempts,.cost,.tier,.shard] | @tsv' "$VERIFIED")
else
for f in $RESULTS; do
if ! jq -e '.total_tests' "$f" >/dev/null 2>&1; then
echo "Skipping malformed JSON: $f"
@@ -418,22 +481,25 @@ jobs:
STATUS_ICON="✅"
[ "$F" -gt 0 ] && STATUS_ICON="❌"
[ "$F" -eq 0 ] && [ "$FL" -gt 0 ] && STATUS_ICON="✅⚠"
SUITE_LINES="${SUITE_LINES}| ${TIER}/${SHARD} | ${P}/${T} | ${EX} | ${RE} | ${STATUS_ICON} | \$${C} |\n"
SUITE_LINES="${SUITE_LINES}| ${TIER}/${SHARD} | ${P}/${T} | unverified | ${EX} | ${RE} | ${STATUS_ICON} | \$${C} |\n"
done
fi
COVERAGE=$(jq -r '"Profile: \(.profile // "full") / \(.prCoverage.mode // "broad"); selected behaviors: \(.selection.e2e | if . == null then "all" else length end), judges: \(.selection.judges | if . == null then "all" else length end). Deferred to scheduled/release coverage: \(.prCoverage.deferred // [] | length) behaviors and \(.prCoverage.deferredPromptFiles // [] | length) changed prompt files. Deferred checks did not run and receive no PR-pass credit."' /tmp/paid-report/manifest.json) || COVERAGE='Coverage manifest unavailable; no coverage claim.'
STATUS="✅ PASS"
if [ "${RECONCILE_EXIT:-1}" != "0" ] || [ "$FAILED" -gt 0 ]; then STATUS="❌ FAIL"; fi
if [ "$STATUS" = '✅ PASS' ] && [ "$MANUAL" -gt 0 ]; then STATUS='⚠ MANUAL ACCEPTED (unscored)'; fi
if [ -z "$VERIFIED" ]; then STATUS='❌ FAIL (manual acceptance unavailable/unverified)'; fi
BODY="## E2E Evals: ${STATUS}
**${PASSED}/${TOTAL}** recorded final results passed | **${EXECUTED} executed, ${REUSED} reused** | **\$${COST}** total cost | reconcile exit: ${RECONCILE_EXIT:-missing}$([ "$FLAKY" -gt 0 ] && printf ' | ⚠ %s cases with multiple attempts' "$FLAKY")
**${PASSED} automated passed / ${TOTAL} final results** | **${FAILED} failed, ${MANUAL} manual accepted (unscored; no score-cache credit)** | **${EXECUTED} executed, ${REUSED} reused** | **\$${COST}** total cost | reconcile exit: ${RECONCILE_EXIT:-missing}$([ "$FLAKY" -gt 0 ] && printf ' | ⚠ %s cases with multiple attempts' "$FLAKY")
${COVERAGE}
| Shard | Result | Executed | Reused | Status | Cost |
|-------|--------|----------|--------|--------|------|
| Shard | Automated result | Manual/unscored | Executed | Reused | Status | Cost |
|-------|------------------|-----------------|----------|--------|--------|------|
$(echo -e "$SUITE_LINES")
<details><summary>Fail-closed reconciliation</summary>
@@ -450,7 +516,11 @@ jobs:
FAILURES=""
for f in $RESULTS; do
if ! jq -e '.failed' "$f" >/dev/null 2>&1; then continue; fi
FAILS=$(jq -r '[.tests | group_by(.name)[] | last | select(.passed == false)][] | "- ❌ \(.name): \(.exit_reason // "unknown")"' "$f" 2>/dev/null || echo "- ⚠️ parse error")
if [ -n "$VERIFIED" ]; then
FAILS=$(jq -r '[.tests | group_by(.name)[] | last | select(.passed == false and (has("manual_review") | not))][] | "- ❌ \(.name): \(.exit_reason // "unknown")"' "$f" 2>/dev/null || echo "- ⚠️ parse error")
else
FAILS=$(jq -r '[.tests | group_by(.name)[] | last | select(.passed == false)][] | "- ❌ \(.name): \(.exit_reason // "unknown")"' "$f" 2>/dev/null || echo "- ⚠️ parse error")
fi
FAILURES="${FAILURES}${FAILS}\n"
done
BODY="${BODY}
+15
View File
@@ -182,6 +182,21 @@ jobs:
- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium
- name: Configure the bundled Chromium sandbox helper
run: |
set -euo pipefail
chrome=$(bun -e 'import { chromium } from "playwright"; import { realpathSync } from "node:fs"; console.log(realpathSync(chromium.executablePath()))')
case "$chrome" in
"$HOME"/.cache/ms-playwright/chromium-*/chrome-linux*/chrome) ;;
*) echo "Unexpected Chromium installation path" >&2; exit 1 ;;
esac
helper="${chrome%/*}/chrome_sandbox"
installed="${chrome%/*}/chrome-sandbox"
test -f "$helper" && test ! -L "$helper"
sudo install -T -o root -g root -m 4755 "$helper" "$installed"
test "$(stat -c '%u:%a' "$installed")" = '0:4755'
cmp -s "$helper" "$installed"
# Headed-browser tests (handoff, extension sidepanel DOM) need a real
# DISPLAY — first Linux run failed with Playwright's "launched a headed
# browser without an XServer" banner. xvfb-run below provides it;
+129 -2
View File
@@ -26,6 +26,23 @@ on:
pull_request:
branches: [main]
workflow_dispatch:
inputs:
dia_native_only:
description: Run disposable ARM64 macOS Dia qualification instead of Windows
type: boolean
default: false
native_diagnostics_only:
description: Run Windows launch diagnostics and credential regressions without qualification
type: boolean
default: false
dia_launch_comparison:
description: Compare protected Dia launch under Bun and Node in separate fresh Mac jobs
type: boolean
default: false
dia_gui_readiness:
description: Inspect disposable Mac GUI-session readiness without launching browsers
type: boolean
default: false
concurrency:
group: windows-free-${{ github.event.pull_request.number || github.run_id }}
@@ -37,6 +54,7 @@ permissions:
jobs:
windows-free-tests:
if: ${{ !inputs.dia_native_only && !inputs.dia_launch_comparison && !inputs.dia_gui_readiness }}
# Ubicloud Windows runner (same provider as the Linux evals workflow).
# To revert: swap to `windows-latest` (GitHub's free 4-core Windows runner).
runs-on: windows-latest
@@ -49,6 +67,10 @@ jobs:
with:
bun-version: 1.4.0
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38
with:
node-version: 24.18.0
# bun install was 35s of a 55s job, all network. Cache keyed on the
# lockfile; bun's install cache lives under ~/.bun/install/cache on
# every platform.
@@ -82,6 +104,7 @@ jobs:
shell: bash
- name: Generate host SKILL.md outputs (.agents, .factory)
if: ${{ !inputs.native_diagnostics_only }}
# The golden-file regression tests in test/gen-skill-docs.test.ts read
# .agents/skills/gstack-ship/SKILL.md and .factory/skills/gstack-ship/
# SKILL.md. Both are gitignored — generated on demand by gen:skill-docs.
@@ -91,6 +114,9 @@ jobs:
run: bun run gen:skill-docs --host all
shell: bash
- name: Install Chromium for the Node worker smoke
run: bunx playwright install chromium
# The Windows job verifies the new portability work this PR delivers,
# not the entire free suite. After v1.20.0.0 ships, full-suite Windows
# parity is a P4 follow-up TODO that depends on porting many tests off
@@ -110,6 +136,7 @@ jobs:
# (test/test-free-shards.test.ts)
- name: Run curated Windows-safe suite
if: ${{ !inputs.native_diagnostics_only }}
# Replaces the previous hand-listed 13-file subset, which drifted from
# the curation registry it was supposed to sample. The runner's
# --windows-only curation (scripts/test-free-shards.ts) is the single
@@ -125,15 +152,115 @@ jobs:
run: bun run test:windows
shell: bash
- name: Run focused native launch and credential diagnostics
if: inputs.native_diagnostics_only
shell: bash
run: |
set -o pipefail
status=0
bun test browse/test/cookie-import-native-job.test.ts --test-name-pattern 'native Windows launch diagnostics|a locked real Edge profile|real Edge synthetic profile' 2>&1 | tee "$RUNNER_TEMP/gstack-free-test-native-diagnostics.log" || status=1
bun test browse/test/cookie-credential-deadline.test.ts browse/test/cookie-import-node.test.ts browse/test/bun-polyfill.test.ts 2>&1 | tee "$RUNNER_TEMP/gstack-free-test-credential-diagnostics.log" || status=1
exit "$status"
# Same diagnosability contract as free-tests.yml: a red lane must
# carry the WHY (the runner's quiet console names files, not causes).
# (#2561 was written against the old hand-listed subset; its two new
# test files are pure-TS and flow into the --windows-only curation
# automatically, so no per-file entry is needed here.)
- name: Upload shard logs on failure
if: failure()
- name: Upload full shard logs
if: always()
uses: actions/upload-artifact@v7
with:
name: windows-free-test-shard-logs
path: ${{ runner.temp }}/gstack-free-test-*.log
if-no-files-found: ignore
cookie-native-qualification:
if: github.event_name == 'workflow_dispatch' && !inputs.dia_native_only && !inputs.native_diagnostics_only && !inputs.dia_launch_comparison && !inputs.dia_gui_readiness
runs-on: windows-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.4.0
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38
with:
node-version: 24.18.0
- name: Install pinned dependencies
run: bun install --frozen-lockfile
- name: Build the qualified Node server inputs
run: bash browse/scripts/build-node-server.sh
shell: bash
- name: Qualify owned native cookie extraction
run: ./.github/scripts/run-cookie-native-qualification.ps1 -OutputRoot "$env:RUNNER_TEMP"
- name: Preserve qualification evidence
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: cookie-native-qualification
path: ${{ runner.temp }}/cookie-native-qualification-*/
if-no-files-found: error
dia-native-qualification:
if: github.event_name == 'workflow_dispatch' && (inputs.dia_native_only || inputs.dia_launch_comparison || inputs.dia_gui_readiness)
runs-on: macos-15
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
runtime: ${{ fromJSON(inputs.dia_launch_comparison && !inputs.dia_gui_readiness && '["bun","node"]' || '["bun"]') }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6
with:
bun-version: 1.4.0
- name: Validate GUI readiness selection
if: inputs.dia_gui_readiness
env:
OTHER_DIA_MODES: ${{ inputs.dia_native_only || inputs.dia_launch_comparison || inputs.native_diagnostics_only }}
run: |
bun --no-env-file --no-install --no-macros --config=/dev/null -e '
if (process.env.OTHER_DIA_MODES !== "false") {
console.error("dia_gui_readiness must be selected alone");
process.exit(1);
}
'
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38
if: inputs.dia_launch_comparison && !inputs.dia_gui_readiness
with:
node-version: 24.18.0
architecture: arm64
- name: Install pinned dependencies
if: ${{ !inputs.dia_gui_readiness }}
run: bun install --frozen-lockfile
- name: Install the synthetic destination browser
if: ${{ !inputs.dia_gui_readiness }}
run: bunx --no-install playwright install chromium
- name: Inspect GUI readiness without browser or Keychain access
if: inputs.dia_gui_readiness
env:
GSTACK_DIA_NATIVE_QUALIFY: '1'
run: bun --no-env-file --no-install --no-macros --config=/dev/null .github/scripts/run-dia-native-qualification.ts --gui-readiness-only
- name: Qualify native Dia discovery, decryption, and import
if: ${{ !inputs.dia_launch_comparison && !inputs.dia_gui_readiness }}
env:
GSTACK_DIA_NATIVE_QUALIFY: '1'
run: bun --no-env-file --no-install --no-macros --config=/dev/null .github/scripts/run-dia-native-qualification.ts
- name: Compare protected native Dia launch without qualification credit
if: inputs.dia_launch_comparison && !inputs.dia_gui_readiness
env:
GSTACK_DIA_NATIVE_QUALIFY: '1'
COMPARISON_RUNTIME: ${{ matrix.runtime }}
run: bun --no-env-file --no-install --no-macros --config=/dev/null .github/scripts/run-dia-native-qualification.ts --launch-comparison "$COMPARISON_RUNTIME"
- name: Preserve only the sanitized qualification receipt
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: ${{ inputs.dia_gui_readiness && 'dia-gui-readiness' || inputs.dia_launch_comparison && format('dia-launch-comparison-{0}', matrix.runtime) || 'dia-native-qualification' }}
path: ${{ runner.temp }}/dia-native-qualification.json
if-no-files-found: error