Security ⚑ Critical severity

Command Injection in Child Process

User input passed to exec() becomes shell syntax — attackers run arbitrary commands.

`child_process.exec()` spawns a shell (`/bin/sh` or `cmd.exe`) and passes your entire command string to it. Any user input in that string — even a semicolon, pipe, or `$()` — becomes shell syntax. The fix is never pass user input to exec(), use `execFile()` or `spawn()` with an argument array, and validate against an allowlist.

❌ Vulnerable
// VULNERABLE — shell injection via exec
app.get('/ping', (req, res) => {
  const host = req.query.host;
  exec(`ping -c 3 ` + host + ``, (err, stdout) => { // Attacker: ?host=8.8.8.8; cat /etc/passwd
    res.send(stdout);
  });
});
✓ Fixed
// FIXED — argument array, no shell
const { execFile } = require('child_process');
app.get('/ping', (req, res) => {
  const host = req.query.host;
  // Allowlist validation
  if (!/^[a-zA-Z0-9.-]+$/.test(host)) return res.status(400).send('Invalid host');
  execFile('ping', ['-c', '3', host], (err, stdout) => {
    res.send(stdout);
  });
});
CVE-2024-27980 (Node.js command injection via child_process.spawn on Windows even without shell option — BatBadBut incomplete fix). CVE-2024-21488 (network npm package command injection via child_process.exec). CVE-2019-25158 (tts-api package RCE via onSpeechDone unsafe shell commands). Also: CVE-2024-27982 (Node.js HTTP request smuggling, related to parser handling).
PullLight flags `child_process.exec()`, `child_process.execSync()`, `child_process.spawn()` calls where arguments contain `req.query`, `req.body`, `req.params`, or string interpolation. It detects unescaped shell metacharacters (`,;`, `$()`, backticks, `|`, `>`) and flags when user input flows into command strings.
See it in action — paste a diff into /analyze
Try a vulnerable example: pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`)
Analyze a diff →