diff --git a/src/main/antigravity/hook-service.test.ts b/src/main/antigravity/hook-service.test.ts index 978c96319..67750cedf 100644 --- a/src/main/antigravity/hook-service.test.ts +++ b/src/main/antigravity/hook-service.test.ts @@ -96,6 +96,11 @@ describe('AntigravityHookService', () => { expect(script).toContain('payload=$(cat)') expect(script).toContain("payload='{}'") expect(script).not.toContain('if [ -z "$payload" ]; then\n exit 0\nfi') + // Why: payload is piped to curl via stdin (`payload@-`) so it never lands + // on the curl command line (EDR oversized-command-line false positive). + expect(script).toContain('printf \'%s\' "$payload" | curl') + expect(script).toContain('--data-urlencode "payload@-"') + expect(script).not.toContain('--data-urlencode "payload=${payload}"') } expect(script).toContain('{"decision":""}') }) diff --git a/src/main/antigravity/hook-service.ts b/src/main/antigravity/hook-service.ts index 546546e01..c8e30632a 100644 --- a/src/main/antigravity/hook-service.ts +++ b/src/main/antigravity/hook-service.ts @@ -127,7 +127,10 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { " payload='{}'", 'fi', // Timeout caps best-effort hook posts if the local listener stalls. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/antigravity" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/antigravity" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -138,7 +141,7 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', ' --data-urlencode "hook_event_name=${ORCA_ANTIGRAVITY_EVENT}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/claude/hook-service.test.ts b/src/main/claude/hook-service.test.ts index 22c20b63e..ed77f48e6 100644 --- a/src/main/claude/hook-service.test.ts +++ b/src/main/claude/hook-service.test.ts @@ -269,6 +269,12 @@ describe('ClaudeHookService.installRemote', () => { const script = fs.files.get('/home/dev/.orca/agent-hooks/claude-hook.sh') expect(script).toContain('#!/bin/sh') expect(script).toContain('DEVIN_PROJECT_DIR') + // Why: payload is piped to curl via stdin (`payload@-`) so it never lands + // on the curl command line (EDR oversized-command-line false positive), + // matching the Windows curl.exe hook post. + expect(script).toContain('printf \'%s\' "$payload" | curl') + expect(script).toContain('--data-urlencode "payload@-"') + expect(script).not.toContain('--data-urlencode "payload=${payload}"') expect(fs.modes.get('/home/dev/.orca/agent-hooks/claude-hook.sh')).toBe(0o755) }) diff --git a/src/main/claude/hook-service.ts b/src/main/claude/hook-service.ts index 1ef948e9c..758daec38 100644 --- a/src/main/claude/hook-service.ts +++ b/src/main/claude/hook-service.ts @@ -113,7 +113,10 @@ function getManagedScript( // shell is not safe once a path contains quotes or newlines. Post the raw // hook payload plus metadata as form fields and let the receiver parse it. // Timeout caps best-effort hook posts if the local listener stalls. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/claude" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/claude" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -123,7 +126,7 @@ function getManagedScript( ' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/codex/hook-service.ts b/src/main/codex/hook-service.ts index 687ac1c11..0b01d6555 100644 --- a/src/main/codex/hook-service.ts +++ b/src/main/codex/hook-service.ts @@ -712,7 +712,10 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { // shell is not safe once a path contains quotes or newlines. Post the raw // hook payload plus metadata as form fields and let the receiver parse it. // Timeout caps best-effort hook posts if the local listener stalls. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/codex" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/codex" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -722,7 +725,7 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { ' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/command-code/command-code-managed-script.ts b/src/main/command-code/command-code-managed-script.ts index 002247076..cc625c605 100644 --- a/src/main/command-code/command-code-managed-script.ts +++ b/src/main/command-code/command-code-managed-script.ts @@ -121,7 +121,10 @@ export function buildCommandCodeManagedScript( ' exit 0', 'fi', // Timeout caps best-effort hook posts if the local listener stalls. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/command-code" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/command-code" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -131,7 +134,7 @@ export function buildCommandCodeManagedScript( ' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/copilot/hook-service.ts b/src/main/copilot/hook-service.ts index d6039796b..a61588ebf 100644 --- a/src/main/copilot/hook-service.ts +++ b/src/main/copilot/hook-service.ts @@ -166,7 +166,10 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { 'if [ -z "$payload" ]; then', ' exit 0', 'fi', - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/copilot" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/copilot" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -177,7 +180,7 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { ' --data-urlencode "hookEventName=${ORCA_COPILOT_HOOK_EVENT}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/cursor/hook-service.test.ts b/src/main/cursor/hook-service.test.ts index dbbf29e6e..d04609044 100644 --- a/src/main/cursor/hook-service.test.ts +++ b/src/main/cursor/hook-service.test.ts @@ -77,7 +77,12 @@ describe('CursorHookService', () => { if (process.platform === 'win32') { expect(script).toContain('%SystemRoot%\\System32\\curl.exe') } else { + // Why: payload is piped to curl via stdin (`payload@-`) so it never lands + // on the curl command line (EDR oversized-command-line false positive). expect(script).toContain('payload=$(cat)') + expect(script).toContain('printf \'%s\' "$payload" | curl') + expect(script).toContain('--data-urlencode "payload@-"') + expect(script).not.toContain('--data-urlencode "payload=${payload}"') } }) diff --git a/src/main/cursor/hook-service.ts b/src/main/cursor/hook-service.ts index f86edae23..9c5d19e8f 100644 --- a/src/main/cursor/hook-service.ts +++ b/src/main/cursor/hook-service.ts @@ -104,7 +104,10 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { // shell is not safe once a path contains quotes or newlines. Post the raw // hook payload plus metadata as form fields and let the receiver parse it. // Timeout caps best-effort hook posts if the local listener stalls. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/cursor" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/cursor" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -114,7 +117,7 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { ' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/devin/hook-service.test.ts b/src/main/devin/hook-service.test.ts index 565cce3bf..6bc105ffd 100644 --- a/src/main/devin/hook-service.test.ts +++ b/src/main/devin/hook-service.test.ts @@ -64,6 +64,11 @@ describe('DevinHookService', () => { } const script = readFileSync(getDevinManagedScriptPath(), 'utf8') expect(script).toContain('/hook/devin') + // Why: payload is piped to curl via stdin (`payload@-`) so it never lands + // on the curl command line (EDR oversized-command-line false positive). + expect(script).toContain('printf \'%s\' "$payload" | curl') + expect(script).toContain('--data-urlencode "payload@-"') + expect(script).not.toContain('--data-urlencode "payload=${payload}"') }) it('preserves unrelated keys in Devin config when installing hooks', () => { diff --git a/src/main/devin/hook-service.ts b/src/main/devin/hook-service.ts index ba9e1428b..b0d63b96e 100644 --- a/src/main/devin/hook-service.ts +++ b/src/main/devin/hook-service.ts @@ -80,7 +80,10 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { // shell is not safe once a path contains quotes or newlines. Post the raw // hook payload plus metadata as form fields and let the receiver parse it. // Timeout caps best-effort hook posts if the local listener stalls. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/devin" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/devin" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -90,7 +93,7 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { ' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/droid/hook-service.ts b/src/main/droid/hook-service.ts index 7bc35be35..920e07998 100644 --- a/src/main/droid/hook-service.ts +++ b/src/main/droid/hook-service.ts @@ -92,7 +92,10 @@ function getManagedScript(): string { ' exit 0', 'fi', // Timeout caps best-effort hook posts if the local listener stalls. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/droid" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/droid" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -102,7 +105,7 @@ function getManagedScript(): string { ' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/gemini/hook-service.ts b/src/main/gemini/hook-service.ts index aaf86961b..423df12f8 100644 --- a/src/main/gemini/hook-service.ts +++ b/src/main/gemini/hook-service.ts @@ -97,7 +97,10 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { // shell is not safe once a path contains quotes or newlines. Post the raw // hook payload plus metadata as form fields and let the receiver parse it. // Timeout caps best-effort hook posts if the local listener stalls. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/gemini" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/gemini" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -107,7 +110,7 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { ' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/grok/hook-service.test.ts b/src/main/grok/hook-service.test.ts index a7b67b7e9..23cc2eeaf 100644 --- a/src/main/grok/hook-service.test.ts +++ b/src/main/grok/hook-service.test.ts @@ -76,7 +76,12 @@ describe('GrokHookService', () => { if (process.platform === 'win32') { expect(script).toContain('%SystemRoot%\\System32\\curl.exe') } else { + // Why: payload is piped to curl via stdin (`payload@-`) so it never lands + // on the curl command line (EDR oversized-command-line false positive). expect(script).toContain('payload=$(cat)') + expect(script).toContain('printf \'%s\' "$payload" | curl') + expect(script).toContain('--data-urlencode "payload@-"') + expect(script).not.toContain('--data-urlencode "payload=${payload}"') } }) diff --git a/src/main/grok/hook-service.ts b/src/main/grok/hook-service.ts index ae7cdc55c..6fbbacadb 100644 --- a/src/main/grok/hook-service.ts +++ b/src/main/grok/hook-service.ts @@ -90,7 +90,10 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { ' exit 0', 'fi', // Timeout caps best-effort hook posts if the local listener stalls. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/grok" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/grok" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -100,7 +103,7 @@ function getManagedScript(target: 'local' | 'posix' = 'local'): string { ' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n') diff --git a/src/main/kimi/hook-service.test.ts b/src/main/kimi/hook-service.test.ts index 56b56ca07..24a77444e 100644 --- a/src/main/kimi/hook-service.test.ts +++ b/src/main/kimi/hook-service.test.ts @@ -55,6 +55,11 @@ describe('KimiHookService', () => { // The managed script must exist and POST to the Kimi hook endpoint. const script = readFileSync(scriptPath(), 'utf-8') expect(script).toContain('/hook/kimi') + // Why: payload is piped to curl via stdin (`payload@-`) so it never lands + // on the curl command line (EDR oversized-command-line false positive). + expect(script).toContain('printf \'%s\' "$payload" | curl') + expect(script).toContain('--data-urlencode "payload@-"') + expect(script).not.toContain('--data-urlencode "payload=${payload}"') // The command Kimi runs points at the managed script via sh. expect(config).toContain('agent-hooks/kimi-hook.sh') }) diff --git a/src/main/kimi/hook-service.ts b/src/main/kimi/hook-service.ts index 52eeb7960..459c0994e 100644 --- a/src/main/kimi/hook-service.ts +++ b/src/main/kimi/hook-service.ts @@ -75,7 +75,10 @@ function getManagedScript(): string { // Why: worktreeId embeds a filesystem path, so hand-building JSON in POSIX // shell is not safe once a path contains quotes or newlines. Post the raw // hook payload plus metadata as form fields and let the receiver parse it. - 'curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/kimi" \\', + // Why: pipe payload to curl's stdin (`payload@-`) instead of an inline + // `payload=$VALUE` arg, so tens-of-KB tool output stays off the curl + // command line (EDR command-line false positives). Wire body is identical. + 'printf \'%s\' "$payload" | curl -sS -X POST "http://127.0.0.1:${ORCA_AGENT_HOOK_PORT}/hook/kimi" \\', ' --connect-timeout 0.5 --max-time 1.5 \\', ' -H "Content-Type: application/x-www-form-urlencoded" \\', ' -H "X-Orca-Agent-Hook-Token: ${ORCA_AGENT_HOOK_TOKEN}" \\', @@ -85,7 +88,7 @@ function getManagedScript(): string { ' --data-urlencode "worktreeId=${ORCA_WORKTREE_ID}" \\', ' --data-urlencode "env=${ORCA_AGENT_HOOK_ENV}" \\', ' --data-urlencode "version=${ORCA_AGENT_HOOK_VERSION}" \\', - ' --data-urlencode "payload=${payload}" >/dev/null 2>&1 || true', + ' --data-urlencode "payload@-" >/dev/null 2>&1 || true', 'exit 0', '' ].join('\n')