Command Injection Detection — ShipSafe

ShipSafe ships 89 command injection rules that cover Node.js child_process (exec, execSync, spawn with shell:true), Python subprocess and os.system, and indirect injection via filenames, environment variables, and argument flags. It distinguishes between shell-mode execution (dangerous) and argument-array execution (safe).

89 detection rulesLocal-only scanning

What is Command Injection?

Command injection occurs when an application passes untrusted user input to a system shell command. Attackers can execute arbitrary commands on the server, potentially taking full control of the system, reading sensitive files, or pivoting to other systems on the network.

Why It Matters

Command injection gives attackers direct operating system access with the privileges of your application process. They can read /etc/passwd, dump environment variables (which often contain database passwords and API keys), install backdoors, pivot to internal network services, or wipe the filesystem entirely. In containerized deployments, a command injection can be the first step toward container escape. This is a "game over" vulnerability — one successful exploit typically compromises the entire server.

What ShipSafe Detects

Example: Vulnerable Code

Vulnerable file conversion endpoint with command injection

// Vulnerable: user input in shell command
app.post("/convert", async (req, res) => {
  const { filename } = req.body;
  const { exec } = require("child_process");
  exec(`ffmpeg -i uploads/${filename} output.mp4`, (err, stdout) => {
    res.json({ status: "converted" });
  });
});

// An attacker sends filename: "video.mp4; rm -rf /"
// The shell interprets the semicolon as a command separator
// and executes: ffmpeg -i uploads/video.mp4; rm -rf / output.mp4

ShipSafe Catches It

$ shipsafe scan

  CRITICAL  command-injection/exec-with-user-input
  src/routes/convert.ts:4
  User input from req.body is interpolated into shell command via exec().
  Fix: Use execFile() with an argument array instead of exec() with string interpolation.
  execFile("ffmpeg", ["-i", `uploads/${filename}`, "output.mp4"])

What to Do Instead

Safe alternative: execFile with argument array and input validation

// SAFE: execFile with argument array (no shell involved)
const { execFile } = require("child_process");

app.post("/convert", async (req, res) => {
  const { filename } = req.body;

  // Validate the filename — allow only alphanumeric, dash, underscore, dot
  if (!/^[a-zA-Z0-9._-]+$/.test(filename)) {
    return res.status(400).json({ error: "Invalid filename" });
  }

  // execFile does NOT use a shell — arguments are passed directly
  // Shell metacharacters like ; | & ` $() have no effect
  execFile(
    "ffmpeg",
    ["-i", `uploads/${filename}`, "output.mp4"],
    (err, stdout) => {
      if (err) return res.status(500).json({ error: "Conversion failed" });
      res.json({ status: "converted" });
    }
  );
});

Frequently Asked Questions

What is the difference between exec and execFile?

exec() passes the command string to a shell (/bin/sh), which interprets metacharacters like ;, |, &, and $(). execFile() invokes the program directly with an argument array — no shell is involved, so metacharacters are treated as literal text. Always prefer execFile() when you need to run an external program with user-supplied arguments.

Is spawn safe from command injection?

By default, yes. spawn() passes arguments as an array without a shell. However, if you pass the option { shell: true }, spawn behaves like exec and becomes vulnerable. ShipSafe detects spawn calls with shell: true and user input.

Does ShipSafe detect command injection in Python?

Yes. ShipSafe detects os.system(), subprocess.call() with shell=True, subprocess.run() with shell=True, and subprocess.Popen() with shell=True when they contain user-controlled input.

What about argument injection?

ShipSafe detects argument injection where user input is used as command flags. For example, if a user can control a --output argument and passes --output=/etc/crontab, they could write to arbitrary files. ShipSafe flags unsanitized user input in argument positions.

Detect Command Injection in Your Code

Install ShipSafe and scan your project in under 60 seconds.

npm install -g @shipsafe/cli

Related Security Categories