diff --git a/scripts/agent-benchmark/run-guards.mjs b/scripts/agent-benchmark/run-guards.mjs index c87b5c4a..3e206933 100644 --- a/scripts/agent-benchmark/run-guards.mjs +++ b/scripts/agent-benchmark/run-guards.mjs @@ -94,6 +94,7 @@ export function shellCommandSegments(command) { continue; } const next = source[index + 1]; + if (char === '&' && (source[index - 1] === '>' || source[index - 1] === '<' || next === '>')) continue; const separator = char === '\n' || char === ';' || char === '|' || char === '&'; if (!separator) continue; const segment = source.slice(start, index).trim(); @@ -109,10 +110,14 @@ export function shellCommandSegments(command) { export function agentDeviceIsolationInvalidReasons(commands, expectedPrefix) { const segments = commands.flatMap((command) => shellCommandSegments(command.command)); const reasons = []; - if (segments.some((command) => /(?:^|\s)agent-device\s+daemon\s+stop(?:\s|$)/.test(command))) { + const lookup = + /^(?:command\s+-[vV]|which|type|whence)\s+(?:[\w./-]+\s+)*agent-device(?:\s+[\w./-]+)*(?:\s+(?:\d*>|&>)\s*(?:&\d+|[\w./-]+))?$/; + const deviceCommands = segments.filter( + (command) => /(?:^|[\s(`])agent-device(?:\s|[)`]|$)/.test(command) && !lookup.test(command), + ); + if (deviceCommands.some((command) => /(?:^|[\s(`])agent-device\s+daemon\s+stop(?:\s|[)`]|$)/.test(command))) { reasons.push('agent-device-daemon-recovery-inside-timer'); } - const deviceCommands = segments.filter((command) => /(?:^|\s)agent-device(?:\s|$)/.test(command)); if (deviceCommands.some((command) => !command.startsWith(expectedPrefix))) { reasons.push('agent-device-run-session-not-applied'); } diff --git a/scripts/agent-benchmark/run-guards.test.mjs b/scripts/agent-benchmark/run-guards.test.mjs index ca8c192a..d6f5ec7c 100644 --- a/scripts/agent-benchmark/run-guards.test.mjs +++ b/scripts/agent-benchmark/run-guards.test.mjs @@ -56,10 +56,53 @@ describe('agent-device session isolation', () => { } }); + it('does not treat literal executable discovery as a device invocation', () => { + for (const command of [ + 'command -v agent-device', + 'command -V agent-device 2>&1', + 'which node agent-device adb', + 'type agent-device 2>/dev/null', + 'whence agent-device', + 'command -v agent-device daemon stop', + 'command -v agent-device &>/dev/null', + `command -v agent-device 2>&1; ${prefix}snapshot`, + `/bin/zsh -lc 'command -v agent-device 2>&1'`, + ]) { + expect(agentDeviceIsolationInvalidReasons([{ command }], prefix)).toEqual([]); + } + }); + + it('does not let executable discovery hide an actual unscoped invocation', () => { + for (const command of [ + 'command agent-device snapshot', + 'command -p agent-device snapshot', + 'command -v agent-device; agent-device snapshot', + 'which agent-device && agent-device snapshot', + 'command -v agent-device $(agent-device snapshot)', + 'command -v agent-device >$(agent-device snapshot)', + 'command -v agent-device 2>&$(agent-device snapshot)', + 'command -v agent-device 2>&$(agent-device daemon stop)', + 'command -v agent-device; $(agent-device snapshot)', + 'command -v agent-device && $(agent-device daemon stop)', + 'command -v agent-device & `agent-device snapshot`', + 'type agent-device `agent-device snapshot`', + ]) { + expect(agentDeviceIsolationInvalidReasons([{ command }], prefix)).toContain( + 'agent-device-run-session-not-applied', + ); + } + }); + it('rejects delayed daemon recovery even with the correct session', () => { expect(agentDeviceIsolationInvalidReasons([{ command: `sleep 5; ${prefix}daemon stop --clean` }], prefix)).toEqual([ 'agent-device-daemon-recovery-inside-timer', ]); + expect( + agentDeviceIsolationInvalidReasons( + [{ command: 'command -v agent-device && $(agent-device daemon stop)' }], + prefix, + ), + ).toEqual(['agent-device-daemon-recovery-inside-timer', 'agent-device-run-session-not-applied']); }); });