fix(#829): gate claudex and close branch coverage gaps
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful

This commit is contained in:
ms-lead-reviewer
2026-07-17 23:10:58 -05:00
parent 046896c612
commit 6a3505dfbc
10 changed files with 253 additions and 40 deletions

View File

@@ -244,6 +244,52 @@ describe('whole mutator-class lease gate', () => {
}
});
test('lease and tool validation failures remain fail-closed at the broker boundary', async () => {
const { socket } = await startBroker();
const sessionId = await register(socket);
const baseRequest = {
action: 'begin_verification',
session_id: sessionId,
runtime_generation: 1,
runtime: 'claude',
ttl_seconds: 300,
binding: binding(),
};
expect(await request(socket, { ...baseRequest, runtime: 'codex' })).toMatchObject({
ok: false,
code: 'INVALID_RUNTIME',
});
expect(
await request(socket, { ...baseRequest, binding: { ...binding(), h_source: 'bad' } }),
).toMatchObject({
ok: false,
code: 'INVALID_BINDING',
});
expect(await request(socket, { ...baseRequest, ttl_seconds: 0 })).toMatchObject({
ok: false,
code: 'INVALID_LEASE_TTL',
});
expect(
await request(socket, {
action: 'authorize_tool',
session_id: sessionId,
runtime_generation: 1,
runtime: 'codex',
tool_name: 'Read',
}),
).toMatchObject({ ok: false, code: 'INVALID_RUNTIME' });
expect(
await request(socket, {
action: 'authorize_tool',
session_id: sessionId,
runtime_generation: 1,
runtime: 'claude',
tool_name: 'x'.repeat(257),
}),
).toMatchObject({ ok: false, code: 'INVALID_TOOL' });
});
test('observer revocation and monotonic TTL expiry deny the next mutator', async () => {
const { socket } = await startBroker();
const sessionId = await register(socket);
@@ -400,9 +446,7 @@ raise SystemExit(0 if len(session_id) == 64 and hook_present and denied else 1)
const adapter = {
harnessPreflight: () => {},
composePrompt: () => '# composed Claude contract',
// Pre-remediation Claudex uses this direct boundary and therefore bypasses registration.
exec: run,
// The remediated orchestration must select this shared register-before-exec boundary.
// Claudex exposes only the shared register-before-exec boundary.
execLeaseGated: (args: string[], env: NodeJS.ProcessEnv) =>
run('python3', [launcherPath, '--runtime', 'claude', '--', 'claude', ...args], env),
} as unknown as ClaudexHarnessAdapter;
@@ -438,8 +482,8 @@ raise SystemExit(0 if len(session_id) == 64 and hook_present and denied else 1)
});
expect(execution).toBeDefined();
expect(execution!.status, execution!.stderr).toBe(0);
expect(JSON.parse(execution!.stdout)).toEqual({
expect(execution!.status, String(execution!.stderr)).toBe(0);
expect(JSON.parse(String(execution!.stdout))).toEqual({
session_id: expect.stringMatching(/^[a-f0-9]{64}$/),
hook_present: true,
denied: true,