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-001 --project .About This Rule
Understanding the vulnerability and how it is detected
This rule detects Flask applications that call app.run(debug=True), enabling the interactive Werkzeug debugger. When debug mode is active, any unhandled exception opens an interactive Python console directly in the browser. An attacker who can trigger a 500 error -- by sending malformed input, hitting an edge case, or guessing a route -- gains the ability to execute arbitrary Python code in the server process. This is unauthenticated remote code execution, not a theoretical risk.
Beyond the debugger itself, debug mode also exposes full stack traces with local variable values in HTTP responses, enables the auto-reloader, and disables several Flask security hardening behaviors. Stack traces leak framework versions, file system paths, configuration values, and application logic that attackers use to plan further exploitation.
The detection uses FlaskApp.method("run").where("debug", True) -- a QueryType-based precise match. FlaskApp declares fqns=["flask"], so the engine only matches objects actually imported from the flask package. The .where("debug", True) filter means only calls where the debug keyword argument is explicitly the boolean True are flagged. Calls with debug=False, debug=os.getenv(...), or no debug argument at all are not flagged. This precision delivers zero false positives on properly configured applications.
Security Implications
Potential attack scenarios if this vulnerability is exploited
Unauthenticated Remote Code Execution via Werkzeug Debugger
The Werkzeug interactive debugger opens a Python console on every unhandled exception. Any visitor who can trigger a 500 error can execute arbitrary Python in the server process. There is a PIN mechanism in Werkzeug 0.11+, but it is derived from machine details (hostname, MAC address, process ID, app path) that are frequently leaked through stack traces or accessible via /proc on Linux. Do not treat the PIN as a security control.
Full Stack Traces Leaked in HTTP Responses
Debug mode returns detailed exception tracebacks to the browser, including local variable values, function call chains, and source file paths. Attackers use this to map the application's internal structure, identify library versions with known CVEs, and locate exact lines of code to target with follow-up attacks.
Secret and Configuration Disclosure
Stack traces frequently capture the values of configuration variables, database connection strings, API keys, and Flask's SECRET_KEY at the moment of the exception. A single triggered error can expose credentials that give an attacker direct database access or the ability to forge session cookies.
Persistent Exposure Through Reloader
The debug reloader watches the file system for source changes and restarts the app automatically. In containerized environments with mounted volumes, this can expose file system paths, trigger import errors that produce verbose stack traces, and create timing side-channels that reveal the application's directory structure.
How to Fix
Recommended remediation steps
- 1Remove debug=True from all app.run() calls. The default is False -- omitting the argument is safe and explicit.
- 2Drive debug mode through an environment variable (FLASK_DEBUG) and verify it is set to 0 or false in every production, staging, and CI environment.
- 3Replace app.run() with a production WSGI server such as Gunicorn, uWSGI, or Waitress. These servers never expose the Werkzeug debugger regardless of configuration.
- 4Implement a global exception handler (app.errorhandler(500)) that returns a generic error page to users and logs the full traceback server-side only.
- 5Add a pre-deployment check in CI -- run this rule with pathfinder ci -- to catch accidental re-introduction of debug=True before it reaches production.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
This rule uses a QueryType-based precise match: FlaskApp.method("run").where("debug", True). FlaskApp declares fqns=["flask"], restricting matches to objects imported from the flask package. The .method("run") step targets the run() method call on that object. The .where("debug", True) step filters to calls where the keyword argument debug is explicitly the boolean True. Calls with debug=False, debug=0, debug=variable_name, or no debug argument are not flagged. This precision eliminates false positives from correctly configured applications. The rule operates at the call-site level without cross-file dataflow analysis.
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 Bound to All Interfaces
Detects Flask applications binding the development server to 0.0.0.0, exposing it to every network interface instead of localhost only.
Flask Code Injection via eval()
User input from Flask request parameters flows to eval(). Replace with ast.literal_eval() for data parsing or json.loads() for structured input.
Frequently Asked Questions
Common questions about Flask Debug Mode Enabled
New feature
Get these findings posted directly on your GitHub pull requests
The Flask Debug Mode Enabled rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.