From 8335f0c73cb2ade7f2bdd5d92ab6b0df1bc14e7a Mon Sep 17 00:00:00 2001 From: "Kai (Tam Nhu) Tran" <61256810+kaitranntt@users.noreply.github.com> Date: Sat, 23 May 2026 17:33:38 -0400 Subject: [PATCH] fix(dispatcher): also treat -p as non-subcommand short for --print (#1352) Red-team follow-up to #1341: the original fix only guarded --print but Claude accepts -p as the short form, and CCS itself passes -p in headless-executor.ts. Without this check the security bypass survived via the short flag. Added regression tests: ['-p', 'agents'], ['-p', 'doctor'], ['-p'] alone, and injector short-circuit verification for -p form. --- src/utils/claude-subcommand-detector.ts | 2 +- .../unit/utils/claude-subcommand-detector.test.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/utils/claude-subcommand-detector.ts b/src/utils/claude-subcommand-detector.ts index 4ecdaf8d..adcf6080 100644 --- a/src/utils/claude-subcommand-detector.ts +++ b/src/utils/claude-subcommand-detector.ts @@ -144,7 +144,7 @@ export function getClaudeSubcommandName(args: readonly string[]): string | null const arg = args[i]; if (arg === '--') return null; - if (arg === '--print') return null; + if (arg === '--print' || arg === '-p') return null; if (arg.startsWith('-')) { if (VALUE_TAKING_FLAGS.has(arg)) { diff --git a/tests/unit/utils/claude-subcommand-detector.test.ts b/tests/unit/utils/claude-subcommand-detector.test.ts index 27492e0c..120a1184 100644 --- a/tests/unit/utils/claude-subcommand-detector.test.ts +++ b/tests/unit/utils/claude-subcommand-detector.test.ts @@ -62,6 +62,14 @@ describe('isClaudeSubcommandInvocation', () => { expect(isClaudeSubcommandInvocation(['--print', 'doctor'])).toBe(false); }); + it('treats -p (short form of --print) as non-subcommand — regression for #1341', () => { + // CCS uses -p in headless-executor; without this check the security bypass + // survived via the short flag form. + expect(isClaudeSubcommandInvocation(['-p', 'agents'])).toBe(false); + expect(isClaudeSubcommandInvocation(['-p', 'doctor'])).toBe(false); + expect(isClaudeSubcommandInvocation(['-p'])).toBe(false); + }); + it('handles --flag=value forms', () => { expect(isClaudeSubcommandInvocation(['--model=sonnet', 'agents'])).toBe(true); }); @@ -214,6 +222,12 @@ describe('subcommand passthrough — injectors short-circuit', () => { expect(out).toContain('--disallowedTools'); }); + it('injectors still inject in -p prompt mode — regression for #1341', () => { + const out = appendThirdPartyWebSearchToolArgs(['-p', 'agents']); + expect(out).toContain('--append-system-prompt'); + expect(out).toContain('--disallowedTools'); + }); + it('injectors still inject for non-subcommand interactive launches', () => { const out = appendThirdPartyWebSearchToolArgs(['fix the bug']); expect(out).toContain('--append-system-prompt');