test(cli): invoke the CLI without a shell - #1426
Conversation
Twenty-five test call sites built a command string and handed it to execSync, which runs it through a shell. The interpolated value is a constant path in every case, so nothing was exploitable, but it is the pattern CodeQL reports as shell-command-injection and it accounts for every remaining alert on the repository. Each call now passes an argument array to execFileSync, which never involves a shell. Error handling is unaffected: both APIs reject with the same spawnSync error carrying status and stderr, which these tests assert on. A path containing spaces would now be passed as one argument rather than word-split, which is the more correct behavior. Test-only. Nothing in test/ ships in the npm package. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughThe CLI test suite replaces shell-based ChangesCLI test invocation
Estimated code review effort: 2 (Simple) | ~10 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
alfred-openspec
left a comment
There was a problem hiding this comment.
Approved at exact head 2f4c3b1. All 25 conversions preserve argument order and subprocess error behavior without relying on shell features; the seven affected files pass 25 real subprocess tests locally, the full exact-head cross-platform/security/CodeQL matrix is green, and the test-only no-changeset scope is correct.
Status: Test-only. Clears the last 25 code scanning alerts.
What was wrong
Twenty-five test call sites built a command string and handed it to
execSync, which runs it through a shell:The interpolated value is a constant path in every case, so nothing was exploitable. But it is the pattern CodeQL reports as
js/shell-command-injection-from-environment, and it accounts for every remaining alert on the repository.How it was fixed
Each call passes an argument array to
execFileSync, which never involves a shell:test/commands/spec.test.tstest/commands/show.test.tstest/commands/validate.enriched-output.test.tstest/commands/spec.interactive-show.test.tstest/commands/spec.interactive-validate.test.tstest/commands/change.interactive-show.test.tstest/commands/change.interactive-validate.test.tsWhy this is safe
test/is not inpackage.jsonfiles. Packing the tarball gives 362 files, zero fromtest/.node ${bin}followed by plain literal arguments or one simple variable — no pipes, redirects, chaining, or globs.spawnSyncerror carryingstatusandstderr, which these tests assert on. The error-path tests pass, which is direct evidence the subprocess still runs and still fails as expected.Proof
execSyncremaining in these filesNo changeset: test-only, nothing to release.
Note
Unlike the guard change in #1425, this one is a categorical fix rather than a bet on the analyzer's dataflow.
js/shell-command-injection-from-environmenttargets shell-invoking APIs;execFileSyncwithoutshell: trueis not one, so the rule should no longer apply at all. Still only confirmable once CodeQL re-analyzesmain.🤖 Generated with Claude Code
Summary by CodeRabbit