Rationale
PR #1886 introduced runArgv() / runArgvCapture() to eliminate shell injection in nim.ts, local-inference.ts, and policies.ts. However, three files still use run()/runCapture() with shell strings:
src/lib/onboard.ts — 18 run() + 10 runCapture() shell-string calls (~5,400 lines)
src/lib/agent-onboard.ts — 3 run() shell-string calls
src/lib/preflight.ts — 14 runCapture() shell-string calls
These callsites use shellQuote() for defense, so they are not directly vulnerable — but they remain in the weaker security posture (quoting-dependent rather than structurally safe).
Scope
Convert each shell-string call to pass an argv array to the now-polymorphic run()/runCapture() (see #1886 for the polymorphic refactor). Each conversion is mechanical:
// Before
run(`docker rm -f ${shellQuote(name)} 2>/dev/null || true`, { ignoreError: true });
// After
run(["docker", "rm", "-f", name], { ignoreError: true });
File-by-file breakdown
| File |
run() calls |
runCapture() calls |
Notes |
src/lib/onboard.ts |
18 |
10 |
Largest file; migrate incrementally |
src/lib/preflight.ts |
0 |
14 |
Includes lsof, sysctl, df, sudo calls |
src/lib/agent-onboard.ts |
3 |
0 |
docker image inspect, docker build |
Additional cleanup
- Add an ESLint
no-restricted-syntax rule to flag shell: true in child_process calls, preventing future reintroduction of the vulnerability class
- Remove
shellQuote imports from files once all their callsites are converted
Acceptance criteria
References
Rationale
PR #1886 introduced
runArgv()/runArgvCapture()to eliminate shell injection innim.ts,local-inference.ts, andpolicies.ts. However, three files still userun()/runCapture()with shell strings:src/lib/onboard.ts— 18run()+ 10runCapture()shell-string calls (~5,400 lines)src/lib/agent-onboard.ts— 3run()shell-string callssrc/lib/preflight.ts— 14runCapture()shell-string callsThese callsites use
shellQuote()for defense, so they are not directly vulnerable — but they remain in the weaker security posture (quoting-dependent rather than structurally safe).Scope
Convert each shell-string call to pass an argv array to the now-polymorphic
run()/runCapture()(see #1886 for the polymorphic refactor). Each conversion is mechanical:File-by-file breakdown
run()callsrunCapture()callssrc/lib/onboard.tssrc/lib/preflight.tslsof,sysctl,df,sudocallssrc/lib/agent-onboard.tsdocker image inspect,docker buildAdditional cleanup
no-restricted-syntaxrule to flagshell: trueinchild_processcalls, preventing future reintroduction of the vulnerability classshellQuoteimports from files once all their callsites are convertedAcceptance criteria
run()/runCapture()calls in the three files pass argv arrays instead of shell stringsshellQuoteis no longer imported in any of the three filesbash -cpatterns introduced (except documented exceptions like backgrounding)shell: truein child_process optionsReferences
run()/runCapture()+ initial argv conversion