You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
JS: js/command-line-injection misses child_process argument arrays when the shell option is set #22825
js/command-line-injection does not report remote input that reaches the argument array of spawn/execFile when the shell option is set, although Node.js then runs the joined command line in a shell. Checked with the latest release, CodeQL 2.27.2 (codeql/javascript-queries 2.4.7).
constcp=require("child_process");constapp=require("express")();app.get("/a",(req,res)=>{cp.exec("echo hello "+req.query.name);// reportedres.send("ok");});app.get("/b",(req,res)=>{cp.spawn("echo",["hello",req.query.name],{shell: true});// not reportedres.send("ok");});app.get("/c",(req,res)=>{cp.spawn("echo",["hello",req.query.name],{shell: "/bin/sh"});// not reportedres.send("ok");});
Only /a is reported, by js/command-line-injection. For /b and /c, no query in javascript-security-extended.qls reports a command injection. At runtime both run /bin/sh -c "echo hello <name>" (Node.js joins [file, ...args] with spaces when shell is truthy; see the spawn documentation), so name = "x; touch /tmp/marker" creates the file (Node.js v24.18.0).
As far as I can tell, CommandInjection::SystemCommandExecutionSink only uses getACommandArgument(), which for spawn/execFile is the program name. The argument array is treated as shell-interpreted only in UnsafeShellCommandConstruction (executesArrayAsShell), i.e. for library inputs, and even there only for shell: true: with a string-valued shell such as "/bin/sh", js/shell-command-constructed-from-input misses the call as well.
Activity