v1.88.1.0 fix: harden credential boundaries and owned state (#2942)

* fix(settings): preserve symlinked settings targets

Resolve the selected target for locking, mutation, backup, and rollback; refuse target changes and preserve private modes. Addresses #2830.

* fix(redact): bind masking to original detected spans

Inspired by #2929's anchored-span diagnosis; independently implemented using normalization offsets. Addresses #2930 and the relocation portion of #2912 without changing detection sensitivity.

* fix(evals): exclude operator credentials from prefix admission

Adapts the credential-suffix screen proposed in #2636, with real launched-child regression coverage and deliberate provider-auth exceptions.

* fix(artifacts): retain custom allowlist rules on reinitialization

Preserve the exact user-owned suffix and publish only a successfully assembled replacement. Independently implements the repair reported in #2907.

* test(cso): verify exact masked reads and unmaskable payload refusal

* fix(cso): preserve exact filesystem identities through lease recovery

Preserve 64-bit device/inode identity and nanosecond race checks. Add native NTFS lifecycle coverage for #2927; retain ambiguous legacy-state refusal without claiming Windows PID-reuse recovery is resolved.

* fix(redact): bind pre-push scans to destination and preserve seam context

Uses #2935 (bd07318) as source evidence for push-target range and slice-overlap defects. Independently implemented; no cherry-pick or release metadata adoption.

* test(ci): gate native agent ownership and settings links on macOS

* fix(browse): bind agent lifetimes and cleanup to owned generations

Uses #2931 by Chris Hutton / Claude Fable 5.1 as attributed design input; independently implemented without broad sweeps or copied code. Keep uncertain children and locks rather than deleting foreign state.

* test(ci): include concurrent shutdown controls in the native macOS gate

* v1.88.1.0 fix: harden credential boundaries and owned state

* fix(redact): preserve target provenance and scan boundary semantics

* test(artifacts): read managed rules from atomic allowlist assembly

* fix: preserve native exit observations and fixture prerequisites

* fix: preserve UTF-16 offsets through redaction normalization
This commit is contained in:
Garry Tan
2026-09-23 08:54:53 -04:00
committed by GitHub
parent 636175d349
commit b9706f3635
42 changed files with 2719 additions and 339 deletions
+54
View File
@@ -1,6 +1,60 @@
import { describe, test, expect } from 'bun:test';
import { spawnSync } from 'node:child_process';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { pathToFileURL } from 'node:url';
import { parseNDJSON } from './session-runner';
test('runSkillTest launches a child without operator credentials', () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-hermetic-session-'));
try {
const bin = path.join(root, 'claude');
fs.writeFileSync(bin, `#!/usr/bin/env node
const names = ['GITHUB_TOKEN', 'GITHUB_PERSONAL_ACCESS_TOKEN', 'GITHUB_APP_PRIVATE_KEY', 'GH_TOKEN', 'GITHUB_ACTIONS', 'GITHUB_PATH', 'GITHUB_TOKENIZER', 'EVALS_RUN_ID'];
const present = Object.fromEntries(names.map(name => [name, Object.hasOwn(process.env, name)]));
console.log(JSON.stringify({type: 'result', subtype: 'success', result: JSON.stringify(present)}));
`, { mode: 0o700 });
const script = `import { runSkillTest } from ${JSON.stringify(pathToFileURL(path.join(import.meta.dir, 'session-runner.ts')).href)};
const result = await runSkillTest({prompt: 'synthetic fixture', workingDirectory: ${JSON.stringify(root)}, model: 'fixture', timeout: 5000, startupGraceMs: 5000, allowedTools: []});
console.log(JSON.stringify({exitReason: result.exitReason, child: JSON.parse(result.output)}));`;
const result = spawnSync(process.execPath, ['-e', script], {
cwd: path.resolve(import.meta.dir, '..', '..'),
encoding: 'utf8',
timeout: 30_000,
env: {
PATH: `${root}${path.delimiter}${process.env.PATH ?? '/usr/bin:/bin'}`,
HOME: root,
TMPDIR: os.tmpdir(),
GITHUB_TOKEN: 'synthetic-token',
GITHUB_PERSONAL_ACCESS_TOKEN: 'synthetic-pat',
GITHUB_APP_PRIVATE_KEY: 'synthetic-private-key',
GH_TOKEN: 'synthetic-gh-token',
GITHUB_ACTIONS: 'true',
GITHUB_PATH: '/tmp/actions-path',
GITHUB_TOKENIZER: 'metadata-tokenizer',
EVALS_RUN_ID: 'synthetic-run',
},
});
expect(result.status, result.stderr).toBe(0);
expect(JSON.parse(result.stdout)).toEqual({
exitReason: 'success',
child: {
GITHUB_TOKEN: false,
GITHUB_PERSONAL_ACCESS_TOKEN: false,
GITHUB_APP_PRIVATE_KEY: false,
GH_TOKEN: false,
GITHUB_ACTIONS: true,
GITHUB_PATH: true,
GITHUB_TOKENIZER: true,
EVALS_RUN_ID: true,
},
});
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
// Fixture: minimal NDJSON session (system init, assistant with tool_use, tool result, assistant text, result)
const FIXTURE_LINES = [
'{"type":"system","subtype":"init","session_id":"test-123"}',