skills/igbuend/grimbard/command-injection-anti-pattern

command-injection-anti-pattern

SKILL.md

Command Injection Anti-Pattern

Severity: Critical

Summary

Command injection allows attackers to execute arbitrary OS commands by manipulating user input. This anti-pattern occurs when applications concatenate user input into shell command strings. Common in AI-generated code. Enables complete system compromise, data exfiltration, malware installation, and lateral movement.

The Anti-Pattern

User input embedded in shell command strings enables command injection. The shell cannot distinguish between intended commands and attacker-injected commands.

BAD Code Example

# VULNERABLE: Shell command with user input
import os

def ping_host(hostname):
    # User input is directly concatenated into the command string.
    # An attacker can inject malicious commands separated by a semicolon or other shell metacharacters.
    command = "ping -c 4 " + hostname
    os.system(command)

# Example of a successful attack:
# hostname = "google.com; rm -rf /"
# Resulting command: "ping -c 4 google.com; rm -rf /"
# This executes the ping and then attempts to delete the entire filesystem.

GOOD Code Example

# SECURE: Use argument arrays, avoid shell
import subprocess

def ping_host(hostname):
    # Validate input against allowlist
    import re
    if not re.match(r'^[a-zA-Z0-9.-]+$', hostname):
        raise ValueError("Invalid hostname format")

    # The command and its arguments are passed as a list.
    # The underlying OS API executes the command directly without invoking a shell,
    # so shell metacharacters in `hostname` are treated as a literal string.
    try:
        subprocess.run(["ping", "-c", "4", hostname], check=True, shell=False)
    except subprocess.CalledProcessError as e:
        print(f"Error executing ping: {e}")

JavaScript/Node.js Examples

BAD:

// VULNERABLE: Shell command with user input
const { exec } = require('child_process');

function pingHost(hostname) {
    // User input concatenated into command string
    exec(`ping -c 4 ${hostname}`, (error, stdout) => {
        console.log(stdout);
    });
}

// Attack: hostname = "google.com; cat /etc/passwd"
// Executes: ping -c 4 google.com; cat /etc/passwd

GOOD:

// SECURE: Use execFile with argument array
const { execFile } = require('child_process');

function pingHost(hostname) {
    // Validate hostname format
    if (!/^[a-zA-Z0-9.-]+$/.test(hostname)) {
        throw new Error('Invalid hostname format');
    }

    // Arguments passed as array, no shell invocation
    execFile('ping', ['-c', '4', hostname], (error, stdout) => {
        if (error) {
            console.error(`Error: ${error.message}`);
            return;
        }
        console.log(stdout);
    });
}

Java Examples

BAD:

// VULNERABLE: Runtime.exec() with string concatenation
public void pingHost(String hostname) {
    try {
        // String concatenation creates command injection risk
        String command = "ping -c 4 " + hostname;
        Runtime.getRuntime().exec(command);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

GOOD:

// SECURE: ProcessBuilder with argument array
import java.io.IOException;
import java.util.regex.Pattern;

public void pingHost(String hostname) {
    // Validate hostname format
    if (!Pattern.matches("^[a-zA-Z0-9.-]+$", hostname)) {
        throw new IllegalArgumentException("Invalid hostname format");
    }

    try {
        // Arguments in array, no shell interpretation
        ProcessBuilder pb = new ProcessBuilder("ping", "-c", "4", hostname);
        Process process = pb.start();
        process.waitFor();
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    }
}

Detection

Python:

  • os.system() with any user input
  • subprocess.run() or subprocess.Popen() with shell=True
  • String concatenation: "command " + user_input
  • f-strings: f"command {user_input}"

JavaScript/Node.js:

  • child_process.exec() with user input
  • Template literals: `command ${userInput}`
  • String concatenation: "command " + userInput

Java:

  • Runtime.getRuntime().exec() with string concatenation
  • Single string argument to exec() instead of string array

PHP:

  • exec(), system(), shell_exec(), passthru() with user input
  • String concatenation: "command " . $userInput

Search Patterns:

  • Grep: shell=True|exec\(|system\(|child_process\.exec
  • Look for user input variables in command construction
  • Check for string concatenation or interpolation with command functions

Prevention

  • Use argument arrays instead of shell strings (e.g., subprocess.run(["command", "arg1", "arg2"], shell=False)).
  • Never pass shell=True with user-controlled input to execution functions.
  • Validate all input against a strict allowlist of known-good values or formats.
  • Use language-specific libraries or APIs instead of external shell commands whenever possible.
  • Apply the Principle of Least Privilege to the process executing the command, restricting its permissions to the absolute minimum required.

Testing for Command Injection

Manual Testing:

  1. Test shell metacharacters: ;, |, &, $(), `, &&, ||
  2. Input payloads: ; ls, | whoami, & cat /etc/passwd, `id`
  3. Verify commands execute safely without shell interpretation
  4. Confirm metacharacters treated as literal strings

Automated Testing:

  • Static Analysis: Semgrep, Bandit (Python), ESLint security plugins, SpotBugs (Java)
  • DAST: Burp Suite, OWASP ZAP with command injection payloads
  • Code Review: Search for detection patterns above

Example Test:

# Test that shell metacharacters are treated literally
def test_command_injection_prevention():
    malicious_input = "google.com; rm -rf /"
    try:
        ping_host(malicious_input)  # Should fail validation
        assert False, "Should reject malicious input"
    except ValueError:
        pass  # Expected

Remediation Steps

  1. Identify vulnerable code - Use detection patterns above
  2. Validate necessity - Can you avoid shell commands entirely?
  3. Replace with safe API - Use language-specific libraries when possible
  4. Convert to argument arrays - Replace string concatenation
  5. Remove shell=True - Never use with user input
  6. Add input validation - Allowlist known-good patterns
  7. Test the fix - Verify shell metacharacters are literal
  8. Review similar code - Check for pattern across codebase

Related Security Patterns & Anti-Patterns

References

Weekly Installs
4
GitHub Stars
4
First Seen
Jan 20, 2026
Installed on
claude-code4
codex4
cursor4
opencode4
trae-cn3
gemini-cli3