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 — 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 — 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);
});
});
pool.query(`SELECT * FROM users WHERE id = ${req.params.id}`)