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-FLASK-SEC-007 --project .About This Rule
Understanding the vulnerability and how it is detected
This rule detects path traversal vulnerabilities in Flask applications where user-controlled input from HTTP request parameters flows to Python's open() or io.open() built-in functions without path sanitization. When an attacker supplies directory traversal sequences (../, ..\, URL-encoded variants %2e%2e%2f) in a filename or path parameter, the application reads or writes files outside the intended directory.
Path traversal is one of the most common file-handling bugs in web applications. In Flask apps it typically appears in file download endpoints (open the file, return its contents) and file processing utilities (open the file, parse it). The vulnerability is not always obvious because the user-supplied value may pass through string concatenation with os.path.join() before reaching open() -- os.path.join('uploads/', '../../../etc/passwd') silently produces '../../../etc/passwd' due to a quirk of the join implementation when later segments contain separators.
The rule traces tainted data from Flask request sources to the filename argument of open() and io.open() at position 0. Flows through os.path.basename() or werkzeug's secure_filename() are recognized as sanitizers because basename() strips all directory components and secure_filename() additionally removes unsafe characters that could be used in traversal attempts.
Security Implications
Potential attack scenarios if this vulnerability is exploited
Arbitrary File Read
An attacker who supplies ../../../etc/passwd as a filename reads the system password file. On servers where application secrets are stored in files (/etc/ssl/private/server.key, .env, config.ini, ~/.aws/credentials), the attacker reads production secrets in a single request.
Source Code Disclosure
Python applications store business logic, database schemas, and hardcoded credentials in .py files. A path traversal from /uploads/ to ../app.py or ../config.py exposes source code that enables more targeted subsequent attacks.
Arbitrary File Write (Write Mode)
If the open() call uses write mode ('w', 'a', 'wb'), the attacker can write arbitrary content to arbitrary paths. Writing to /etc/cron.d/ installs a cron job. Writing to the application's own Python files modifies source code. Writing to server configuration files redirects traffic.
Log File Poisoning
Traversal to application log files combined with write mode allows log injection -- inserting fake log entries that confuse audit trails, or injecting content into log files that are subsequently parsed by log processing tools.
How to Fix
Recommended remediation steps
- 1Apply werkzeug.utils.secure_filename() to any user-supplied filename before constructing a file path -- it strips directory separators and traversal sequences.
- 2After constructing the full path with os.path.join(), resolve symlinks with os.path.realpath() and verify the result starts with the intended base directory prefix.
- 3Maintain an allowlist of permitted filenames or file extensions rather than relying on path sanitization alone -- reject anything not in the allowlist.
- 4Separate file storage from the application root directory so a traversal beyond the upload directory cannot reach application source files or server configuration.
- 5Use send_from_directory() from Flask instead of open() for serving static files -- it performs its own path safety checks.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
Scope: global (cross-file taint tracking across the entire project). Sources: Flask HTTP input methods -- request.args.get(), request.form.get(), request.values.get(), request.get_json() -- all of which can deliver attacker-controlled filename or path strings. Sinks: open() and io.open() built-in functions. The .tracks(0) parameter targets argument position 0, the filename/path argument. The mode argument at position 1 (read/write) and other kwargs are not tracked. Sanitizers: os.path.basename() and werkzeug.utils.secure_filename() are recognized as sanitizing transformations. basename() strips all directory components. secure_filename() additionally removes non-ASCII characters and sequences that could be used in path manipulation. A flow through either function is treated as sanitized. The rule follows tainted filenames through variable assignments, string concatenation with os.path.join(), and cross-file function calls to catch traversal chains that pass through utility modules.
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
Flask SQL Injection via Tainted String
Finds user input reaching raw SQL queries in Flask apps where parameterized queries should be used instead.
Dangerous asyncio Shell Execution
asyncio.create_subprocess_shell() passes the command through the system shell, enabling command injection when arguments contain untrusted input.
Frequently Asked Questions
Common questions about Flask Path Traversal via open()
New feature
Get these findings posted directly on your GitHub pull requests
The Flask Path Traversal via open() rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.