fix(#829): harden runtime launch guard against marker evasions
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful

This commit is contained in:
ms-lead-reviewer
2026-07-18 00:17:50 -05:00
parent 64efdb4460
commit 1eb77c17f3
11 changed files with 316 additions and 62 deletions

View File

@@ -54,7 +54,10 @@ function okAdapter(overrides: TestAdapterOverrides = {}): ClaudexHarnessAdapter
return {
harnessPreflight: () => {},
composePrompt: () => '# Composed Claude contract',
execLeaseGated: adapterOverrides.execLeaseGated ?? ((args, env) => exec?.('claude', args, env)),
execLeaseGated:
adapterOverrides.execLeaseGated ??
((args, env, dangerous) =>
exec?.('claude', dangerous ? ['--dangerously-skip-permissions', ...args] : args, env)),
...adapterOverrides,
};
}

View File

@@ -475,7 +475,7 @@ export interface ClaudexHarnessAdapter {
/** Compose the full Claude runtime contract (== `composeContract('claude')`). */
composePrompt: () => string;
/** Register the Claude parent with the broker, then exec with the same PID. */
execLeaseGated: (args: string[], env: NodeJS.ProcessEnv) => void;
execLeaseGated: (args: string[], env: NodeJS.ProcessEnv, dangerous: boolean) => void;
}
export interface LaunchClaudexDeps {
@@ -533,9 +533,8 @@ export async function launchClaudex(
log(buildClaudexBanner(resolvedModels));
const cliArgs = yolo ? ['--dangerously-skip-permissions'] : [];
cliArgs.push('--append-system-prompt', prompt, ...args);
adapter.execLeaseGated(cliArgs, env);
const cliArgs = ['--append-system-prompt', prompt, ...args];
adapter.execLeaseGated(cliArgs, env, yolo);
} catch (err) {
errorLog(
`[mosaic] claudex launch aborted: ${err instanceof Error ? err.message : String(err)}`,

View File

@@ -755,7 +755,7 @@ function launchRuntime(runtime: RuntimeName, args: string[], yolo: boolean): nev
printSettingsWarnings(settingsAudit);
const prompt = buildRuntimePrompt('claude');
const cliArgs = yolo ? ['--dangerously-skip-permissions'] : [];
const cliArgs: string[] = [];
cliArgs.push('--append-system-prompt', prompt);
if (hasMissionNoArgs) {
cliArgs.push(missionPrompt);
@@ -763,7 +763,7 @@ function launchRuntime(runtime: RuntimeName, args: string[], yolo: boolean): nev
cliArgs.push(...args);
}
console.log(`[mosaic] Launching ${label}${modeStr}${missionStr}...`);
execLeaseGatedRuntime('claude', cliArgs);
execLeaseGatedRuntime('claude', cliArgs, process.env, yolo);
break;
}
@@ -818,13 +818,19 @@ function execLeaseGatedRuntime(
runtime: 'claude' | 'pi',
args: string[],
baseEnv: NodeJS.ProcessEnv = process.env,
dangerous = false,
): void {
const launcher = resolveTool('lease-broker', 'launch-runtime.py');
execRuntime('python3', [launcher, '--runtime', runtime, '--', runtime, ...args], {
...baseEnv,
MOSAIC_LEASE_BROKER_SOCKET: defaultLeaseBrokerSocket(baseEnv),
MOSAIC_RUNTIME_GENERATION: baseEnv['MOSAIC_RUNTIME_GENERATION'] ?? '1',
});
const dangerousArgs = dangerous ? ['--dangerous'] : [];
execRuntime(
'python3',
[launcher, ...dangerousArgs, '--runtime', runtime, '--', runtime, ...args],
{
...baseEnv,
MOSAIC_LEASE_BROKER_SOCKET: defaultLeaseBrokerSocket(baseEnv),
MOSAIC_RUNTIME_GENERATION: baseEnv['MOSAIC_RUNTIME_GENERATION'] ?? '1',
},
);
}
/** exec into the runtime, replacing the current process. */
@@ -860,7 +866,8 @@ function launchClaudexProduction(args: string[], yolo: boolean): void {
checkSequentialThinking('claude');
},
composePrompt: () => buildRuntimePrompt('claude'),
execLeaseGated: (cmdArgs, env) => execLeaseGatedRuntime('claude', cmdArgs, env),
execLeaseGated: (cmdArgs, env, dangerous) =>
execLeaseGatedRuntime('claude', cmdArgs, env, dangerous),
};
void launchClaudex(args, yolo, adapter);
}