fix(#2667): run-with-timeout mediates .cmd/.bat spawns on Windows (CVE-2024-27980); fallow pre-pass names failure kind (#2897)
* fix(#2667): mediate .cmd/.bat/.exe spawns on Windows; split fallow pre-pass failure diagnostic
run-with-timeout spawned .cmd/.bat/.exe commands without shell:true on Windows,
tripping Node's CVE-2024-27980 EINVAL (April 2024 security hardening). The fallow
structural pre-pass then no-op'd silently — a hard execution failure read the same
as 'optional dependency absent'.
(A) gsd-core/bin/gsd-tools.cjs runWithTimeout: gate shell:true on
(win32 && command ends in .cmd/.bat/.exe). Narrow by design — never fires for
the 7 `bash -c` callers (command is `bash`, no such suffix), so the recorded
no-shell-for-argv-array security contract (DEFECT.UNBOUNDED-SUBPROCESS) is
preserved; cmdArgs stays an array. POSIX untouched.
(B) code-review.md fallow pre-pass: name the failure KIND (timeout / spawn failure
/ crash / not-found) so a Windows .cmd spawn failure is not mistaken for an
absent binary.
Regression test in tests/run-with-timeout.test.cjs gated to win32 (.cmd/.bat/.exe
shims run with exit 0 + non-empty stdout; pre-fix EINVAL → exit 125/empty). POSIX
negative-space test guards the unchanged bash -c callers.
* chore(#2667): changeset fragment
* chore(#2667): backfill changeset PR 2897 + correct body (cmd.exe array, not shell:true)
* fix(#2667): exclude .exe from the win32 spawn-mediation gate; ack code-review.md growth
CI caught two failures on the first push:
1. windows-24: 'exits 124 when the wall-clock budget is exceeded' regressed. The
gate matched .exe, so the HANG command (node.exe -e 'setTimeout(...)') was
wrapped in 'cmd.exe /c node.exe ...' — the wrapped child escaped the timeout
cap's process-group reap (exit 124 never fired; hit the 30s harness backstop)
AND cmd.exe risked mis-parsing the -e script arg. .exe is INTENTIONALLY
excluded now: real PE executables spawn fine directly; only .cmd/.bat are the
CVE-2024-27980 EINVAL cases. The .exe test becomes a negative-space test
(node.exe spawned directly, exit 0).
2. ubuntu-22: emitted-attribution — code-review.md grew 1177 bytes from the
#2667 fallow pre-pass failure-KIND case statement; acknowledge it.
---------
Co-authored-by: Test <test@example.com>
(cherry picked from commit 79ed181ec0)
This commit is contained in:
@@ -464,7 +464,22 @@ FALLOW_OK=$(FALLOW_TMP=\"${FALLOW_JSON_PATH}.tmp\" node -e \"
|
||||
if [ \"$FALLOW_OK\" != \"1\" ]; then
|
||||
FALLOW_STDERR_SUMMARY=$(head -5 \"$FALLOW_STDERR_TMP\")
|
||||
rm -f \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_STDERR_TMP\"
|
||||
echo \"WARNING: fallow structural pre-pass failed (exit ${FALLOW_EXIT}): ${FALLOW_STDERR_SUMMARY}\"
|
||||
# #2667: distinguish a hard EXECUTION failure (the binary was found at step 1
|
||||
# but would not run) from the binary-missing path (step 2). Exit 124 = timeout,
|
||||
# 2 = usage error, 125 = spawn failure (e.g. Windows EINVAL on a .cmd shim —
|
||||
# CVE-2024-27980, now mediated by run-with-timeout), 126/127 = not executable /
|
||||
# not found. A non-zero exit here with a resolved binary means fallow is
|
||||
# installed but did not produce a report — surface that loudly so a Windows
|
||||
# user does not mistake it for "fallow absent".
|
||||
case \"$FALLOW_EXIT\" in
|
||||
124) FALLOW_FAIL_KIND=\"timed out\" ;;
|
||||
2) FALLOW_FAIL_KIND=\"usage error\" ;;
|
||||
125) FALLOW_FAIL_KIND=\"spawn failure (the binary was found but did not start — e.g. a Windows .cmd shim; run-with-timeout mediates this)\" ;;
|
||||
126) FALLOW_FAIL_KIND=\"not executable\" ;;
|
||||
127) FALLOW_FAIL_KIND=\"not found\" ;;
|
||||
*) FALLOW_FAIL_KIND=\"crashed\" ;;
|
||||
esac
|
||||
echo \"WARNING: fallow structural pre-pass failed (${FALLOW_FAIL_KIND}, exit ${FALLOW_EXIT}): ${FALLOW_STDERR_SUMMARY}\"
|
||||
FALLOW_JSON_PATH=\"\"
|
||||
else
|
||||
mv \"${FALLOW_JSON_PATH}.tmp\" \"$FALLOW_JSON_PATH\"
|
||||
@@ -472,7 +487,7 @@ else
|
||||
fi
|
||||
```
|
||||
|
||||
On any failure of the structural pre-pass (binary missing, timeout, empty output, or unparseable JSON), the workflow continues with no `<structural_findings>` injection; the reviewer agent receives a normal review request.
|
||||
On any failure of the structural pre-pass (binary missing at step 2, or an execution failure here — timeout, spawn failure, crash, empty output, or unparseable JSON), the workflow continues with no `<structural_findings>` injection; the reviewer agent receives a normal review request. The WARNING above names the failure KIND so a hard execution failure (e.g. a Windows `.cmd` spawn failure) is not mistaken for an absent optional dependency.
|
||||
|
||||
4) Optional MCP bridge path (runtime-dependent):
|
||||
- If `FALLOW_MCP=true`, set reviewer input mode to MCP-backed structural findings.
|
||||
|
||||
Reference in New Issue
Block a user