Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions change-notes/1.19/analysis-python.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ A new predicate `Stmt.getAnEntryNode()` has been added to make it easier to writ

| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
| Flask app is run in debug mode (`py/flask-debug`) | security, external/cwe/cwe-215, external/cwe/cwe-489 | Finds instances where a Flask application is run in debug mode. Enabled on LGTM by default. |
| Information exposure through an exception (`py/stack-trace-exposure`) | security, external/cwe/cwe-209, external/cwe/cwe-497 | Finds instances where information about an exception may be leaked to an external user. Enabled on LGTM by default. |

## Changes to existing queries
Expand Down
9 changes: 9 additions & 0 deletions python/ql/src/Security/CWE-215/FlaskDebug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from flask import Flask

app = Flask(__name__)

@app.route('/crash')
def main():
raise Exception()

app.run(debug=True)
39 changes: 39 additions & 0 deletions python/ql/src/Security/CWE-215/FlaskDebug.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
Running a Flask application with debug mode enabled may allow an
attacker to gain access through the Werkzeug debugger.
</p>

</overview>
<recommendation>

<p>
Ensure that Flask applications that are run in a production
environment have debugging disabled.
</p>

</recommendation>
<example>

<p>
Running the following code starts a Flask webserver that has
debugging enabled. By visiting <code>/crash</code>, it is possible
to gain access to the debugger, and run arbitrary code through the
interactive debugger.
</p>

<sample src="FlaskDebug.py" />

</example>

<references>
<li>Flask Quickstart Documentation: <a href="http://flask.pocoo.org/docs/1.0/quickstart/#debug-mode">Debug Mode</a>.</li>
<li>Werkzeug Documentation: <a href="http://werkzeug.pocoo.org/docs/0.14/debug/">Debugging Applications</a>.</li>
</references>

</qhelp>

23 changes: 23 additions & 0 deletions python/ql/src/Security/CWE-215/FlaskDebug.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @name Flask app is run in debug mode
* @description Running a Flask app in debug mode may allow an attacker to run arbitrary code through the Werkzeug debugger.
* @kind problem
* @problem.severity error
* @precision high
* @id py/flask-debug
* @tags security
* external/cwe/cwe-215
* external/cwe/cwe-489
*/

import python

import semmle.python.web.flask.General


from CallNode call, Object isTrue
where
call = theFlaskClass().declaredAttribute("run").(FunctionObject).getACall() and
call.getArgByName("debug").refersTo(isTrue) and
isTrue.booleanValue() = true
select call, "A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger."
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
| test.py:10:1:10:19 | ControlFlowNode for Attribute() | A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger. |
| test.py:25:1:25:20 | ControlFlowNode for Attribute() | A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger. |
| test.py:29:1:29:20 | ControlFlowNode for Attribute() | A Flask app appears to be run in debug mode. This may allow an attacker to run arbitrary code through the debugger. |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Security/CWE-215/FlaskDebug.ql
1 change: 1 addition & 0 deletions python/ql/test/query-tests/Security/CWE-215/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
semmle-extractor-options: --max-import-depth=2 -p ../lib
37 changes: 37 additions & 0 deletions python/ql/test/query-tests/Security/CWE-215/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from flask import Flask

app = Flask(__name__)

@app.route('/crash')
def main():
raise Exception()

# bad
app.run(debug=True)

# okay
app.run()
app.run(debug=False)

# also okay
run(debug=True)

app.notrun(debug=True)

# a slightly more involved example using flow and truthy values

DEBUG = True

app.run(debug=DEBUG)

DEBUG = 1

app.run(debug=DEBUG)

if False:
app.run(debug=True)

# false negative

runapp = app.run
runapp(debug=True)
2 changes: 1 addition & 1 deletion python/ql/test/query-tests/Security/lib/flask/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@


class Flask(object):
pass
def run(self, *args, **kwargs): pass

from .globals import request

Expand Down