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:
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.
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
To prevent OS Command Injection vulnerabilities when constructing and executing shell commands in Python:
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.
importsubprocessuser_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)exceptsubprocess.CalledProcessErrorase:print(f"Error executing command: {e}")exceptFileNotFoundError:print("Error: Command not found.")
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.
importsubprocessimportshlexuser_input="some value; malicious_command"# Example of user inputsafe_argument=shlex.quote(user_input)# Compliant: user input is quoted before being included in a command string for shell=Truetry:result=subprocess.run(f"echo {safe_argument}",shell=True,capture_output=True,text=True,check=True)print(result.stdout)exceptsubprocess.CalledProcessErrorase:print(f"Error executing command: {e}")
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.
Instead, use the list-based approach (Remediation #1) or shlex.quote() (Remediation #2).
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.
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.
importsubprocessimportshlexdefsafe_example1():# Using list arguments without concatenationresult=subprocess.run(["echo","hello"],capture_output=True,text=True)returnresult.stdoutdefsafe_example2():# Using shlex.quote for proper escapinguser_input=get_user_input()safe_input=shlex.quote(user_input)subprocess.run(f"echo {safe_input}",shell=True)defsafe_example3():# Parameterized approachfilename="hardcoded.txt"subprocess.run(["cat",filename])defsafe_example4():# Static command without user inputcommand="ls -la /tmp"subprocess.run(command,shell=True)# annotation_count: 0
Seamless integrations. Try Datadog Code Security
Datadog Code Security
Try this rule and analyze your code with Datadog Code Security
How to use this rule
1
2
rulesets:- python-flask # Rules to enforce Python flask.
Create a static-analysis.datadog.yml with the content above at the root of your repository
Use our free IDE Plugins or add Code Security scans to your CI pipelines