From 4797cf81a43c6be57abe485e84fc2926dff231f5 Mon Sep 17 00:00:00 2001 From: Borja <3930245+BorjaLL@users.noreply.github.com> Date: Fri, 3 Jul 2026 16:26:53 -0700 Subject: [PATCH] fix(agent-hooks): stream hook payload to a temp file instead of inlining it on the curl command line (#4475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The POSIX agent-hook script for every curl-based agent inlined the full event payload via `curl --data-urlencode "payload=${payload}"`. Tool output can be tens of KB, so the resulting process command line could be multi-KB — which endpoint security tools (e.g. Microsoft Defender for Endpoint) flag as an oversized/suspicious command line. That produced a false-positive detection on Orca's own loopback (127.0.0.1) telemetry POST. Stream the payload to an mktemp file and post it with `--data-urlencode "payload@$payload_file"` instead. The urlencoded body on the wire is byte-identical, so the agent-hook receiver is unchanged; the payload simply never appears on a process command line. `trap ... EXIT` removes the temp file on every exit path. Small bounded metadata fields (paneKey/tabId/worktreeId/env/version) stay inline. Applied to all curl-based agents: claude, codex, command-code, copilot, cursor, droid, gemini, grok, antigravity. (amp/hermes/opencode post via the HTTP request body and were never affected.) The Windows post-command shares the same latent pattern and is flagged as follow-up. Co-authored-by: Claude Opus 4.8 (1M context) Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com> Co-authored-by: Orca --- src/main/antigravity/hook-service.test.ts | 5 +++++ src/main/antigravity/hook-service.ts | 7 +++++-- src/main/claude/hook-service.test.ts | 6 ++++++ src/main/claude/hook-service.ts | 7 +++++-- src/main/codex/hook-service.ts | 7 +++++-- src/main/command-code/command-code-managed-script.ts | 7 +++++-- src/main/copilot/hook-service.ts | 7 +++++-- src/main/cursor/hook-service.test.ts | 5 +++++ src/main/cursor/hook-service.ts | 7 +++++-- src/main/devin/hook-service.test.ts | 5 +++++ src/main/devin/hook-service.ts | 7 +++++-- src/main/droid/hook-service.ts | 7 +++++-- src/main/gemini/hook-service.ts | 7 +++++-- src/main/grok/hook-service.test.ts | 5 +++++ src/main/grok/hook-service.ts | 7 +++++-- src/main/kimi/hook-service.test.ts | 5 +++++ src/main/kimi/hook-service.ts | 7 +++++-- 17 files changed, 86 insertions(+), 22 deletions(-) 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')