flawopen.com/Command Injection/exec vs execFile
exec() vs. execFile() in Node.js: what's the difference?
exec() takes one string and runs it through a shell, so pipes and wildcards work—but shell metacharacters in untrusted input can change the command. execFile() starts the named program directly with an argument array and no shell by default. That blocks shell parsing, not every input flaw: the target program can still treat an argument as an option or otherwise act on an unintended value.
Side by side
exec(`ls ${dir} | grep .txt`,
(err, out) => {...})
// pipe works, but so does injection
execFile('ls', [dir],
(err, out) => {...})
// shell metacharacters stay data; validate ls argument semantics
// break out of its own argument
When you genuinely need shell features
If a command genuinely requires piping, globbing, or shell redirection, execFile() cannot express those features directly. Prefer composing the pipeline in Node itself by spawning each stage separately and connecting streams. If exec() is unavoidable, keep untrusted input out of the command string. Also remember that shell-free argument arrays do not prevent argument injection: validate each value for the target program, use -- where that program supports it, and allow-list commands and options. The shell: true option enables a shell in APIs that otherwise omit one; never pass unsanitized input in that mode.
FAQ
Windows batch files change the boundary
On Windows, .bat and .cmd files cannot be launched directly with execFile(). Node documents invoking them through exec(), cmd.exe, or a shell-enabled spawn(); these routes reintroduce command-interpreter parsing. Prefer a native executable or direct API. If batch invocation is unavoidable, keep the script path and arguments fixed or validate them against that script’s exact contract. Node also deprecates passing an argument array with shell: true (since v22.15.0 and v23.11.0).
spawn() is also shell-free by default and accepts an argument array. The practical difference is mainly output handling: spawn() streams output, while execFile() buffers it. Neither prevents the target program from interpreting an argument as an option, so validate arguments for that program. Setting shell: true reintroduces shell parsing.