fix(vscode): use shell mode for spawn on Windows to detect npm correctly - #584
fix(vscode): use shell mode for spawn on Windows to detect npm correctly#584aalhadxx wants to merge 3 commits into
Conversation
On Windows, npm is shipped as npm.cmd. Without shell: true, Node's spawn fails to execute batch files, causing the VS Code extension to falsely report 'npm not detected' even when it is installed. The install() method already used this flag; apply the same fix to probeCommand (environment detection) and runRaw (CLI execution). Fixes alibaba#453
|
🔍 OpenCodeReview found 1 issue(s) in this PR.
|
| const proc = spawn(resolveBin(this.cliPath), args, { | ||
| cwd, | ||
| env: envExtra ? { ...getShellEnv(), ...envExtra } : getShellEnv(), | ||
| shell: process.platform === 'win32', | ||
| }); |
There was a problem hiding this comment.
[security · medium]
Security consideration: Using shell: true on Windows causes Node.js to concatenate the command and arguments into a single string passed to cmd.exe. In runRaw, arguments may include user-controlled values (e.g., opts.customPrompt from buildReviewArgs). While Node.js applies basic quoting for array arguments, cmd.exe metacharacters like %, ^, or ! can still potentially cause unexpected behavior or injection.
Consider sanitizing user-supplied argument values before passing them to spawn with shell: true, or alternatively use cross-spawn / execa which handle Windows .cmd resolution without requiring shell: true.
Reverts shell: true in runRaw to address security feedback: runRaw args may include user-controlled values (opts.from, opts.to, opts.customPrompt), and shell mode could interpret cmd.exe metacharacters. Keep shell: true only in probeCommand where args are known-safe (--version). The original bug (alibaba#453) was specifically about environment detection failing on Windows, so this targeted fix is sufficient.
lizhengfeng101
left a comment
There was a problem hiding this comment.
Code Review
Overview
This PR fixes a real Windows issue where npm.cmd can't be spawned without shell: true, causing the VS Code extension to falsely report "npm not detected." The fix is straightforward and the test intent is correct, but there are two blocking issues.
Blocking Issues
1. jest.mock('child_process') breaks existing tests in the same file
The added jest.mock('child_process') is a module-level mock that affects the entire test file. The existing CliService.isAvailable, CliService.runRaw, and CliService.testConnection tests all rely on real spawn to execute node commands. With this mock in place, those tests will all fail.
Fix options:
- Move the new tests to a separate file (e.g.
CliService.shell.test.ts) wherechild_processcan be mocked in isolation - Or use
jest.spyOnwith per-test setup/teardown instead of a module-level mock
2. PR description claims runRaw is fixed, but the diff only changes probeCommand
The PR body states:
Apply
shell: process.platform === 'win32'to the spawn calls in:
probeCommandrunRaw
But the diff only modifies probeCommand (line 35). runRaw (line 111) is untouched. Either add the missing fix to runRaw or correct the PR description.
Minor Issues
3. headRefName is main
The PR is submitted from the fork's main branch rather than a feature branch. Not blocking, but it prevents parallel development on the same fork.
4. Non-Windows assertion could be more precise
expect(spawn).toHaveBeenCalledWith(
'npm', ['--version'],
expect.not.objectContaining({ shell: true }),
);Since shell: process.platform === 'win32' evaluates to { shell: false } on Linux, asserting expect.objectContaining({ shell: false }) would be more explicit about the expected behavior.
5. Security note on shell: true
shell: true can introduce command injection if args contain unescaped shell metacharacters. In this case the risk is low (args is hardcoded ['--version']), but a brief inline comment explaining why it's safe would help future readers.
Summary
| Aspect | Verdict |
|---|---|
| Correctness | jest.mock breaks existing tests; runRaw fix is missing |
| Scope | Focused, solves a real Windows problem |
| Tests | Logic is correct but mock placement is wrong |
| Security | Low risk — args are hardcoded |
Recommendation: Fix the two blocking issues (isolate the mock, address runRaw discrepancy), then this is good to merge.
- Replace module-level jest.mock('child_process') with per-test jest.spyOn
so existing isAvailable / runRaw / testConnection tests continue to
use the real spawn.
- Add inline comment explaining why shell: true is safe in probeCommand
(hardcoded --version args, no user input).
- Use explicit expect.objectContaining({ shell: false }) for non-Windows
assertion instead of expect.not.objectContaining({ shell: true }).
|
Hi @lizhengfeng101, thanks for the detailed review. All blocking issues have been addressed in the latest push:
Minor fixes also applied:
Please take another look when you have a moment. |
|
Hi @lizhengfeng101, thanks for the detailed review. All blocking issues have been addressed in the latest push:
Minor fixes also applied:
Please take another look when you have a moment. |
Problem
On Windows, npm is shipped as
npm.cmd. Node'sspawnwithoutshell: truefails to execute batch files, causing the VS Code extension to falsely report 'npm not detected' even when npm is installed and works in the terminal.Reproduction
On Windows 11 with Node.js and npm installed, open the Open Code Review configuration page in VS Code. Node.js is detected but npm is marked as missing.
Fix
Apply
shell: process.platform === 'win32'toprobeCommand(used for environment detection: node, npm, ocr version checks).The
install()method already used this flag.runRawwas intentionally excluded because its args may include user-controlled values (opts.from,opts.to,opts.customPrompt), andshell: truewith user input carries command-injection risk.Test
Added
CliService probe shell optiontests that verify:win32),spawnreceivesshell: truelinux),spawnreceivesshell: falseFixes #453