Skip to content

Commit 3e9341f

Browse files
Model class instantiation for werkzueg headers
1 parent b9984be commit 3e9341f

File tree

3 files changed

+21
-1
lines changed

3 files changed

+21
-1
lines changed

python/ql/lib/semmle/python/frameworks/Werkzeug.qll

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ private import semmle.python.ApiGraphs
1212
private import semmle.python.frameworks.Stdlib
1313
private import semmle.python.Concepts
1414
private import semmle.python.frameworks.internal.InstanceTaintStepsHelper
15+
private import semmle.python.frameworks.data.ModelsAsData
1516

1617
/**
1718
* Provides models for the `Werkzeug` PyPI package.
@@ -144,6 +145,18 @@ module Werkzeug {
144145
* See https://werkzeug.palletsprojects.com/en/1.0.x/datastructures/#werkzeug.datastructures.Headers.
145146
*/
146147
module Headers {
148+
/** Gets a reference to the `werkzeug.datastructures.Headers` class. */
149+
API::Node classRef() {
150+
result = API::moduleImport("werkzeug").getMember("datastructures").getMember("Headers")
151+
or
152+
result = ModelOutput::getATypeNode("werkzeug.datastructures.Headers~Subclass").getASubclass*()
153+
}
154+
155+
/** A direct instantiation of `werkzeug.datastructures.Headers`. */
156+
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
157+
ClassInstantiation() { this = classRef().getACall() }
158+
}
159+
147160
/**
148161
* A source of instances of `werkzeug.datastructures.Headers`, extend this class to model new instances.
149162
*

python/ql/test/query-tests/Security/CWE-113-HeaderInjection/HeaderInjection.expected

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
edges
22
| flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:1:29:1:35 | ControlFlowNode for request | provenance | |
3+
| flask_tests.py:1:29:1:35 | ControlFlowNode for request | flask_tests.py:9:18:9:24 | ControlFlowNode for request | provenance | |
34
| flask_tests.py:1:29:1:35 | ControlFlowNode for request | flask_tests.py:20:18:20:24 | ControlFlowNode for request | provenance | |
45
| flask_tests.py:1:29:1:35 | ControlFlowNode for request | flask_tests.py:29:18:29:24 | ControlFlowNode for request | provenance | |
6+
| flask_tests.py:9:5:9:14 | ControlFlowNode for rfs_header | flask_tests.py:13:17:13:26 | ControlFlowNode for rfs_header | provenance | |
7+
| flask_tests.py:9:18:9:24 | ControlFlowNode for request | flask_tests.py:9:5:9:14 | ControlFlowNode for rfs_header | provenance | |
58
| flask_tests.py:20:5:20:14 | ControlFlowNode for rfs_header | flask_tests.py:23:22:23:31 | ControlFlowNode for rfs_header | provenance | |
69
| flask_tests.py:20:18:20:24 | ControlFlowNode for request | flask_tests.py:20:5:20:14 | ControlFlowNode for rfs_header | provenance | |
710
| flask_tests.py:29:5:29:14 | ControlFlowNode for rfs_header | flask_tests.py:32:22:32:31 | ControlFlowNode for rfs_header | provenance | |
811
| flask_tests.py:29:18:29:24 | ControlFlowNode for request | flask_tests.py:29:5:29:14 | ControlFlowNode for rfs_header | provenance | |
912
nodes
1013
| flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember |
1114
| flask_tests.py:1:29:1:35 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
15+
| flask_tests.py:9:5:9:14 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
16+
| flask_tests.py:9:18:9:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
17+
| flask_tests.py:13:17:13:26 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
1218
| flask_tests.py:20:5:20:14 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
1319
| flask_tests.py:20:18:20:24 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
1420
| flask_tests.py:23:22:23:31 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
@@ -17,5 +23,6 @@ nodes
1723
| flask_tests.py:32:22:32:31 | ControlFlowNode for rfs_header | semmle.label | ControlFlowNode for rfs_header |
1824
subpaths
1925
#select
26+
| flask_tests.py:13:17:13:26 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:13:17:13:26 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
2027
| flask_tests.py:23:22:23:31 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:23:22:23:31 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |
2128
| flask_tests.py:32:22:32:31 | ControlFlowNode for rfs_header | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | flask_tests.py:32:22:32:31 | ControlFlowNode for rfs_header | This HTTP header is constructed from a $@. | flask_tests.py:1:29:1:35 | ControlFlowNode for ImportMember | user-provided value |

python/ql/test/query-tests/Security/CWE-113-HeaderInjection/flask_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def werkzeug_headers():
1010
response = Response()
1111
headers = Headers()
1212
headers.add("HeaderName", rfs_header) # GOOD: Newlines are rejected from header value.
13-
headers.add(rfs_header, "HeaderValue") # BAD: User controls header name. Not yet found.
13+
headers.add(rfs_header, "HeaderValue") # BAD: User controls header name.
1414
response.headers = headers
1515
return response
1616

0 commit comments

Comments
 (0)