Command injection: when a parameter runs a shell command
Passing request input to a shell lets the caller end your command and start their own. One semicolon is enough.
Example
const { exec } = require("child_process");
app.get("/ping", (req, res) => {
exec("ping -c 1 " + req.query.host, (err, out) => res.send(out));
});const { execFile } = require("child_process");
const HOST = /^[a-z0-9.-]{1,253}$/i;
app.get("/ping", (req, res) => {
const host = String(req.query.host);
if (!HOST.test(host)) return res.sendStatus(400);
execFile("ping", ["-c", "1", host], (err, out) => res.send(out));
});How it happens
Functions such as exec, execSync, os.system or subprocess with shell=True hand the whole string to /bin/sh, which interprets ;, &&, |, backticks and $( ). A host parameter of example.com; cat /etc/passwd runs both commands with the server's permissions — reading secrets, installing malware, moving to other machines.
How to fix it
Do not involve a shell at all.
- Use execFile or spawn with an argument array (subprocess.run([...]) in Python) and no shell option
- Validate the value against an allowlist pattern before using it
- Prefer a library call over shelling out to a tool when one exists
How RepoVerse finds it
- CriticalRequest input reaches a shell command
A `;` or `$(…)` in the input runs a second command on the host.
data flow · CWE-78
- HighShell command built from a variable
A command string is interpolated and then executed.
pattern
- HighSubprocess run through a shell
`shell=True` hands the command line to `/bin/sh`, so a semicolon or backtick in any interpolated value runs a second command.
pattern
Questions
- Is spawn safe from command injection?
- spawn and execFile with an argument array do not start a shell, so metacharacters are not interpreted. Passing shell: true brings the risk back.
- How does RepoVerse find command injection?
- It traces request input to child_process calls and shell helpers and reports the path; line rules also flag shell=True in Python and string-built commands in other languages.
Related
- Code injection: request input passed to evalWhy eval, new Function and string timers on request data mean remote code execution (CWE-95), a real example from OWASP NodeGoat, and the fix.
- SQL injection: what it is and how to fix itWhat SQL injection (CWE-89) is, a vulnerable query next to the fixed one, and how to find string-built SQL in your code with data-flow analysis.
- GitHub security scanner for code, secrets and dependenciesScan a GitHub repository for injection flaws, leaked secrets and vulnerable dependencies. Data-flow traces from request to sink, lockfile-aware, with fixes.
Updated 2026-10-11