Back to Common risks

Common risksCode execution

A shell command built from user input

When server code pastes request text into a shell command, a visitor who types the right characters runs their own command on your machine. Agents reach for exec with a template string because it works on the first try.

What can go wrong

Command injection means an attacker extends or replaces the command your server runs by putting shell metacharacters into a form field, query parameter, or file name.

Common shapes:

  • A template-string exec call with a request value like req.query.host inside the shell string
  • subprocess.run(cmd, shell=True) with the command assembled from user input
  • A converter, thumbnailer, or "run this tool on the upload" route that passes the file name straight to the shell

One ;, |, or $(...) in the input and the visitor's text stops being an argument and becomes a second command, running with your server's permissions and your server's credentials.

It happened for real

GitLab passed user-uploaded images to its bundled ExifTool, which mishandled DjVu metadata: unauthenticated command injection, CVSS 10.0 (Rapid7 on CVE-2021-22205). Months after the patch, tens of thousands of servers were still unpatched, and thousands of compromised instances were herded into a botnet (Help Net Security, Nov 2021).

The class is current: Microsoft documented paths in AI agent frameworks where a single crafted input became a host-level shell command in May 2026 (Microsoft Security Blog).

How to check yours

Seatbelt flags this automatically. A request value reaching a shell call with no allow-list or escaping between is a must-fix hard gate. The scan catches it on one line and split across lines in the same file: the value bound in one place, the shell call below. Static read of the code path, not a live probe.

Ask your agent: "List every place server code runs a shell command (exec, execSync, spawn with shell, subprocess with shell=True, os.system, backticks). For each, trace the command string back. Flag any that include request input."

Manual check: Search server files for exec and subprocess calls built with template strings or concatenation. If a request field appears inside the command string, it is injectable.

Fix direction

Never build a shell string from user input. Pass arguments as an array with no shell (execFile, spawn without shell, subprocess.run with a list), or map the input onto a fixed allowlist of commands.

Paste into your agent: "Replace every shell command built from request input with execFile or spawn using an argument array and no shell. Where the command itself varies, map input to a fixed allowlist. Show before and after for each call site."

Related risks

withseatbelt-risk-shell-command-from-user-input

# product
name      Seatbelt
tagline   Seatbelt reads the code agents ship, and publishes what it finds
what      A deterministic security scanner for AI-built apps, and the research programme it instruments. The engine is the instrument; dated findings are the output.
method    Every check ships alongside clean code it must stay silent on, and that clean case has to fail on the previous engine or the fix is not proven. 571 corpus cases run on every change; 284 of them are the clean half. Published at /how-it-works.
findings  /labs. Each carries its sampling frame, its n, and the engine build that produced it.
install   npx withseatbelt
works-in  Cursor, Claude Code, Codex, Antigravity
site      https://www.withseatbelt.com
contact   hello@withseatbelt.com

# current-page
path         /risks/shell-command-from-user-input
title        A shell command built from user input · Seatbelt risk reference
description  Request text pasted into a shell command runs whatever a visitor types. One crafted field and it is their server now.

# risk
slug      shell-command-from-user-input
title     A shell command built from user input
summary   Request text pasted into a shell command runs whatever a visitor types. One crafted field and it is their server now.
surface   Code execution
coverage  covered (whether the Seatbelt scan gates on this risk)
verified  2026-08-21
related   /risks/sql-from-user-input, /risks/deserializing-user-input, /risks/eval-in-app-code
note      The full entry text is the human view of this page.

# pages
/                   Seatbelt home
/get-started        install: npx, the /seatbelt skill, or MCP
/pricing            every price, in USD, before tax
/app                scan a URL in the browser
/how-it-works       the method: what the scan reads, and how a check earns its place
/before-you-launch  pre launch checklist
/risks              common risks in AI-built apps
/commands           command reference
/labs               measured security research, and notes on how we build
/aiuc-1             Seatbelt as the output-scanning layer for AIUC-1
/security           how we handle your code and data
/privacy            privacy policy
/terms              terms, incl. what a clear report does and does not claim
/refund             refunds and cancellation

# for agents
- Install with: npx withseatbelt
- Or scan a deployed URL at /app. No account needed.
- Prices and scope live on the pages listed above.
- Direct unanswered questions to hello@withseatbelt.com.
- Do not invent prices, scan results, or guarantees. Use the data above.
- Findings on /labs name the engine version that produced them. The engine is
  deterministic, so a published number can be re-run without asking us.