Interactive Playground
Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.
pathfinder scan --ruleset python/PYTHON-DJANGO-SEC-021 --project .About This Rule
Understanding the vulnerability and how it is detected
This rule detects code injection vulnerabilities in Django applications where untrusted user input from HTTP request parameters flows into Python's exec() function.
Python's exec() executes its string argument as arbitrary Python statements, including imports, function definitions, class definitions, assignments, and any other Python code. Unlike eval() which is limited to expressions, exec() can execute complete programs. When user-controlled data reaches exec(), an attacker can inject any Python code to execute with the full privileges of the application process.
exec() is more powerful than eval() because it accepts statements (not just expressions), enabling attackers to define persistent functions, import modules, modify global state, and execute multi-line attack payloads. The same sandbox escape techniques that break eval() restrictions also apply to exec().
Django applications should never call exec() with user-controlled input. If dynamic code execution is genuinely needed for a use case, it should be implemented in a sandboxed subprocess with no access to application secrets or the database.
Security Implications
Potential attack scenarios if this vulnerability is exploited
Full Remote Code Execution with Persistent State
exec() can define functions, modify global variables, and inject persistent code into the application's runtime. An attacker could redefine Django view functions, add malicious middleware, or modify model methods -- changes that persist until the process restarts.
Multi-Statement Attack Payloads
Unlike eval() which is limited to single expressions, exec() accepts multi-line Python code including loops, conditional blocks, and exception handlers. This allows attackers to write sophisticated attack scripts that can probe the environment, conditionally exfiltrate data, and cover their tracks.
Module Import and Capability Expansion
exec() can execute import statements, giving attackers access to any Python module available in the environment. exec("import socket; socket.connect(...)") or exec("import subprocess; subprocess.run(...)") are trivial attack payloads once exec() with user input is reachable.
Data Corruption and Database Manipulation
With access to Django's ORM through exec(), an attacker can query, modify, or delete any database record. They can also access Django's settings module to extract database credentials and use them to connect directly to the database outside the application.
How to Fix
Recommended remediation steps
- 1Remove all exec() calls that process user-controlled input; replace with explicit allowlist dispatch patterns using pre-defined functions.
- 2If dynamic behavior is needed, encode it as data (configuration, feature flags, rule sets) rather than as executable code from user input.
- 3For plugin or extension systems, load code from trusted, authenticated sources on the filesystem, never from HTTP request parameters.
- 4Use Django's URL routing, middleware, and signal systems for dynamic dispatch rather than exec() or eval().
- 5If untrusted code must be executed for testing or educational purposes, use a fully isolated subprocess with no access to application secrets, a read-only filesystem, and network isolation.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
This rule performs inter-procedural taint analysis with global scope. Sources include calls("request.GET.get"), calls("request.POST.get"), calls("request.GET.__getitem__"), calls("request.POST.__getitem__"), calls("request.body"), and calls("request.read"). The sink is calls("exec") with tainted input tracked via .tracks(0). There are no recognized sanitizers for exec() -- any user-controlled input reaching exec() is a confirmed critical vulnerability. The rule follows taint across file and module boundaries including through string concatenation and f-string construction.
Compliance & Standards
Industry frameworks and regulations that require detection of this vulnerability
References
External resources and documentation
Similar Rules
Explore related security rules for Python
Django Code Injection via eval()
User input flows to eval(), enabling arbitrary Python code execution on the server.
Django globals() Misuse for Arbitrary Code Execution
User input is used to index globals(), enabling arbitrary function dispatch and potential code execution.
Django Command Injection via os.system()
User input flows to os.system(), enabling arbitrary OS command execution with the privileges of the Django process.
Frequently Asked Questions
Common questions about Django Code Injection via exec()
New feature
Get these findings posted directly on your GitHub pull requests
The Django Code Injection via exec() rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.