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-020 --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 eval() function.
Python's eval() interprets its string argument as a Python expression and evaluates it in the current scope. When user-controlled data reaches eval(), an attacker can inject arbitrary Python code that executes with the full privileges and scope of the application process. Unlike SQL injection or command injection, eval() injection gives attackers direct access to the Python runtime, all imported modules, the filesystem, and network resources without any shell intermediary.
Even supposedly "safe" uses of eval() with restricted builtins or custom namespaces have repeatedly been bypassed through creative use of Python's object model and dunder attributes. There is no safe way to call eval() on untrusted input; the function must be replaced with purpose-specific parsers (ast.literal_eval() for data structures, or custom validators for specific expression types).
Security Implications
Potential attack scenarios if this vulnerability is exploited
Direct Remote Code Execution
eval() with user input is direct Remote Code Execution. An attacker can import os, read filesystem contents, spawn shells, exfiltrate secrets, and install persistence backdoors through a single request. No privilege escalation step is needed -- the code runs immediately in the application process context.
Secret and Credential Theft
Django applications store database passwords, API keys, and secret keys in settings or environment variables. An injected expression like __import__('os').environ can exfiltrate all of these in a single request.
Complete Application Compromise
Beyond reading secrets, an attacker can modify application state, alter database records, delete files, corrupt the application's module cache, or replace functions with malicious versions that persist for the lifetime of the process.
Sandbox Escape Patterns
Restricted namespaces and custom builtins passed to eval() do not provide meaningful protection. Attackers can access the full Python object hierarchy through patterns like ().__class__.__base__.__subclasses__() to obtain references to arbitrary classes and modules without needing direct imports.
How to Fix
Recommended remediation steps
- 1Remove all uses of eval() with user-controlled input; there is no safe sanitizer for this.
- 2Use ast.literal_eval() for safely parsing Python literals (dicts, lists, strings, numbers) without executing arbitrary code.
- 3For mathematical expressions, implement a custom recursive descent parser over the AST using ast.parse() with strict node type validation.
- 4For function dispatch, use an explicit allowlist dictionary mapping string names to callable objects rather than using eval() or globals().
- 5For JSON-like data structures, use json.loads() which is safe and standardized.
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("eval") with tainted input tracked via .tracks(0). There are no recognized sanitizers for eval() -- any user-controlled input reaching eval() is a confirmed vulnerability. The rule follows taint across file and module boundaries.
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 exec()
User input flows to exec(), enabling arbitrary Python statement 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 eval()
New feature
Get these findings posted directly on your GitHub pull requests
The Django Code Injection via eval() rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.