---
title: Avoid command injection
description: Datadog, the leading service for cloud-scale monitoring.
breadcrumbs: >-
  Docs > Datadog Security > Code Security > Static Code Analysis (SAST) > SAST
  Rules > Avoid command injection
---

# Avoid command injection

{% callout %}
# Important note for users on the following Datadog sites: app.ddog-gov.com

{% alert level="danger" %}
This product is not supported for your selected [Datadog site](https://docs.datadoghq.com/getting_started/site). ().
{% /alert %}

{% /callout %}

## Metadata{% #metadata %}

**ID:** `python-flask/command-injection`

**Language:** Python

**Severity:** Error

**Category:** Security

**CWE**: [78](https://cwe.mitre.org/data/definitions/78.html)

## Description{% #description %}

This rule identifies potential OS Command Injection vulnerabilities in Python code. These vulnerabilities occur when an application constructs shell commands by incorporating user-supplied or otherwise untrusted data directly into command strings without proper sanitization. An attacker can exploit this by crafting input that includes malicious shell commands, which are then executed on the server with the privileges of the running application. This can lead to unauthorized server access, data breaches, arbitrary code execution, or denial of service.

The rule specifically looks for:

1. Calls to `subprocess` module functions (like `run`, `call`, `Popen`, etc.) where the command argument is built using string formatting (f-strings, `.format()`, `%` operator) or concatenation with potentially unsanitized variables, especially when `shell=True` might be implied or used.
1. Assignments to variables (often named `cmd`, `command`, `args`, etc.) where the string value is constructed using these unsafe methods, and these variables are likely intended for command execution.

## How to Remediate{% #how-to-remediate %}

To prevent OS Command Injection vulnerabilities when constructing and executing shell commands in Python:

1. **Prefer List Arguments with `shell=False`:** When using `subprocess` functions (e.g., `subprocess.run()`, `subprocess.call()`), pass the command and its arguments as a list of strings. Ensure `shell=False` is used (this is the default for `subprocess.run()`). This method treats each item in the list as a literal argument and does not invoke a shell to interpret the command string, significantly reducing the risk of injection.

   ```python
   import subprocess
   
   user_filename = "some_file.txt" # Example of an argument, potentially from user input
   
   # Compliant: command and arguments as a list, shell=False (default)
   try:
       result = subprocess.run(["cat", user_filename], capture_output=True, text=True, check=True)
       print(result.stdout)
   except subprocess.CalledProcessError as e:
       print(f"Error executing command: {e}")
   except FileNotFoundError:
       print("Error: Command not found.")
   ```

1. **Use `shlex.quote()` for Shell Execution:** If you absolutely must use `shell=True` or need to construct a single command string that will be interpreted by the shell, sanitize any dynamic parts of the command string (especially those derived from external input) using `shlex.quote()`. This function escapes characters that have special meaning to the shell, making the input safe.

   ```python
   import subprocess
   import shlex
   
   user_input = "some value; malicious_command" # Example of user input
   safe_argument = shlex.quote(user_input)
   
   # Compliant: user input is quoted before being included in a command string for shell=True
   try:
       result = subprocess.run(f"echo {safe_argument}", shell=True, capture_output=True, text=True, check=True)
       print(result.stdout)
   except subprocess.CalledProcessError as e:
       print(f"Error executing command: {e}")
   ```

1. **Avoid Direct String Concatenation/Formatting for Commands:** Do not build command strings by directly concatenating or formatting them with untrusted input if the command will be processed by a shell.

   ```python
   # Non-compliant example:
   # user_directory = request.args.get("dir")
   # command = "ls -l " + user_directory 
   # subprocess.run(command, shell=True) # Vulnerable
   
   # Non-compliant example:
   # search_term = request.args.get("term")
   # command = f"grep '{search_term}' /var/log/app.log"
   # subprocess.run(command, shell=True) # Vulnerable
   ```

Instead, use the list-based approach (Remediation #1) or `shlex.quote()` (Remediation #2).

1. **Validate and Sanitize All External Input:** Beyond specific command construction, rigorously validate and sanitize any external input that influences program behavior, including what commands are run or what arguments are passed. Use allowlists for permissible inputs where possible.

1. **Principle of Least Privilege:** Run your application with the minimum privileges necessary. This will not prevent the injection itself but can limit the potential damage if such a vulnerability is exploited.

## Non-Compliant Code Examples{% #non-compliant-code-examples %}

```python
import subprocess
import urllib.parse
from flask import request

def vulnerable_example1():
    param = request.headers.get('user_input', '')
    param = urllib.parse.unquote(param)
    command = f"echo {param}"
    result = subprocess.run(command, shell=True, capture_output=True, text=True)
    return result.stdout

def vulnerable_example2():
    user_input = get_user_input()
    cmd = "ls " + user_input
    subprocess.call(cmd, shell=True)

def vulnerable_example3():
    filename = request.args.get('file')
    args = ["sh", "-c", "cat " + filename]
    subprocess.run(args)

def vulnerable_example4():
    search = input("Enter search term: ")
    subprocess.check_output("grep '{}'".format(search), shell=True)
```

## Compliant Code Examples{% #compliant-code-examples %}

```python
def delete_attributes(conn, table_list, attribute_list):
    cursor = conn.cursor()
    for table in table_list:
        for attribute in attribute_list:
            # SQL injection, not command injection
            query = f"MATCH (n:{table}) REMOVE n.{attribute}"
            cursor.execute(query)
            print(f"Deleted attribute {attribute} from table {table}")
    conn.commit()
```

```python
import subprocess
import shlex
import urllib.parse
from flask import request

def safe_example1():
    # Using list arguments without concatenation
    result = subprocess.run(["echo", "hello"], capture_output=True, text=True)
    return result.stdout

def safe_example2():
    # Using shlex.quote for proper escaping
    user_input = get_user_input()
    safe_input = shlex.quote(user_input)
    subprocess.run(f"echo {safe_input}", shell=True)

def safe_example3():
    # Parameterized approach
    filename = "hardcoded.txt"
    subprocess.run(["cat", filename])

def safe_example4():
    # Static command without user input
    command = "ls -la /tmp"
    subprocess.run(command, shell=True)

# annotation_count: 0
```
  Seamless integrations. Try Datadog Code SecurityDatadog Code Security 
{% icon name="icon-external-link" /%}
 