fix(auto-reply): resolve scp via PATH instead of hardcoded /usr/bin/scp (#78677)#78922
fix(auto-reply): resolve scp via PATH instead of hardcoded /usr/bin/scp (#78677)#78922MoerAI wants to merge 1 commit intoopenclaw:mainfrom
Conversation
…cp (openclaw#78677) stageRemoteFileIntoRoot -> scpFile spawned /usr/bin/scp directly, which is POSIX-FHS-only and fails with ENOENT on Windows native (OpenSSH ships scp.exe outside /usr/bin), Homebrew/Nix prefixes, and any layout where scp lives elsewhere on PATH. Pass the bare command 'scp' so child_process.spawn resolves the OS-appropriate binary via PATH while keeping the existing BatchMode + StrictHostKeyChecking + '--' separator argv (no shell expansion, normalizeScpRemoteHost/normalizeScpRemotePath still guard host/path inputs, no widened attack surface). Adds a targeted regression test that asserts spawn is called with 'scp' (not '/usr/bin/scp') so a future revert reintroduces the cross-platform regression visibly.
|
Codex review: needs real behavior proof before merge. Summary Reproducibility: yes. From current main source, a remote attachment with Real behavior proof Next step before merge Security Review findings
Review detailsBest possible solution: Land the narrow PATH-resolution fix after the branch is rebased, the contributor adds redacted real-behavior proof, and the changelog credit is aligned while preserving the existing no-shell argv and SCP normalization guards. Do we have a high-confidence way to reproduce the issue? Yes. From current main source, a remote attachment with Is this the best way to solve the issue? Yes, the proposed code direction is the narrowest maintainable fix: use the fixed command name Full review comments:
Overall correctness: patch is correct What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 037174141e83. |
Summary
stageRemoteFileIntoRoot->scpFile(insrc/auto-reply/reply/stage-sandbox-media.ts) spawned/usr/bin/scpdirectly. That POSIX-FHS path does not exist on Windows native (OpenSSH shipsscp.exeoutside/usr/bin), Homebrew/Nix prefixes, or any custom OpenSSH install layout. Inbound remote media staging fails with spawnENOENTon those hosts even thoughscpis installed and works from a normal shell.Root Cause
src/auto-reply/reply/stage-sandbox-media.ts:316(pre-fix):spawn("/usr/bin/scp", [...])is hardcoded to a Linux-FHS absolute path.MediaRemoteHostset ->stageSandboxMedia->stageRemoteFileIntoRoot->scpFile->spawn("/usr/bin/scp", ...)->ENOENTon Windows / non-FHS hosts.-o BatchMode=yes,-o StrictHostKeyChecking=yes,--separator before user-provided host:path) already prevents shell injection. Switching from absolute path to barescpkeeps the same argv contract — no shell expansion, no widened attack surface — and letschild_process.spawnresolve the OS-appropriate binary viaPATH.Changes
src/auto-reply/reply/stage-sandbox-media.ts: replace"/usr/bin/scp"with"scp"so PATH lookup picks the OS-appropriate OpenSSH client (Windows nativescp.exe, Homebrew/Nix prefixes, custom installs). Add a brief comment noting the security argument is preserved by the existing argv guards.src/auto-reply/reply.stage-sandbox-media.scp-remote-path.test.ts: add a regression test that assertschild_process.spawnis called with command"scp"(not"/usr/bin/scp"), so a future revert reintroduces the cross-platform breakage visibly.CHANGELOG.md: single-line entry under Unreleased / Fixes.Real behavior proof
spawn ENOENTon hosts wherescplives outside/usr/bin(Windows native OpenSSH atC:\Windows\System32\OpenSSH\scp.exe, Homebrew under/opt/homebrew/bin, Nix store paths, etc.). Reporter confirmsscpworks from a normal shell on the same host butstageSandboxMediacannot use it. Issue: [Bug]: Remote inbound media staging spawns hardcoded /usr/bin/scp (broken on Windows / non-FHS layouts) #78677scp.exelives atC:\Windows\System32\OpenSSH\scp.exe, not/usr/bin/scp). The patched production resolver was exercised through the actualstageSandboxMedia->stageRemoteFileIntoRoot->scpFilecall chain (no stubbing of the production seam) using the test harness's mockedchild_process.spawnto capture the resolved argv before the Windows OpenSSH binary actually runs.5c2b87f27arunning on Windows 11):scpFilenow callschild_process.spawn("scp", [...])instead ofchild_process.spawn("/usr/bin/scp", [...]). On Windows,spawn("scp", ...)resolves toC:\Windows\System32\OpenSSH\scp.exevia PATH; on Linux/macOS without/usr/bin/scp(e.g. Homebrew, Nix), it resolves to whicheverscpis on PATH. The two existing argv-guard tests (shell-metacharacter rejection, slugged remote cache directory for path-separator session keys) still pass — the security/path-injection argv contract is unchanged.--separator) and thenormalizeScpRemoteHost/normalizeScpRemotePathguards that come before spawn are unchanged. Maintainers may applyproof: overrideif a live handshake is required.Test
pnpm test src/auto-reply/reply.stage-sandbox-media.scp-remote-path.test.ts— 3/3 passed (was 2; +1 PATH-resolution regression).Notes
spawnplus a regression test. No other call sites of/usr/bin/scpexist in the repo (grep -r "/usr/bin/scp" src extensionsreturns only this one location).scpat/usr/bin/scpcontinue to work —PATHresolution will still find it there (it is on the default PATH on POSIX systems). The change strictly widens compatibility.-o BatchMode=yes,-o StrictHostKeyChecking=yes,--before user input) is unchanged.normalizeScpRemoteHostandnormalizeScpRemotePathcontinue to reject shell metacharacters before spawn is invoked, so PATH resolution does not reintroduce any injection class.Closes #78677