Skip to content

Commit 66fdd53

Browse files
committed
Merge branch 'jorgectf/python/headerInjection' into jorgectf/python/insecure-cookie
2 parents 4f68a17 + 8d0386b commit 66fdd53

5 files changed

Lines changed: 45 additions & 16 deletions

File tree

python/ql/src/experimental/semmle/python/Concepts.qll

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,21 +213,26 @@ class SQLEscape extends DataFlow::Node {
213213
/** Provides classes for modeling HTTP Header APIs. */
214214
module HeaderDeclaration {
215215
/**
216-
* A data-flow node that collects functions setting HTTP Headers' content.
216+
* A data-flow node that collects functions setting HTTP Headers.
217217
*
218218
* Extend this class to model new APIs. If you want to refine existing API models,
219219
* extend `HeaderDeclaration` instead.
220220
*/
221221
abstract class Range extends DataFlow::Node {
222+
/**
223+
* Gets the argument containing the header name.
224+
*/
225+
abstract DataFlow::Node getNameArg();
226+
222227
/**
223228
* Gets the argument containing the header value.
224229
*/
225-
abstract DataFlow::Node getAnInput();
230+
abstract DataFlow::Node getValueArg();
226231
}
227232
}
228233

229234
/**
230-
* A data-flow node that collects functions setting HTTP Headers' content.
235+
* A data-flow node that collects functions setting HTTP Headers.
231236
*
232237
* Extend this class to model new APIs. If you want to refine existing API models,
233238
* extend `HeaderDeclaration` instead.
@@ -238,7 +243,12 @@ class HeaderDeclaration extends DataFlow::Node {
238243
HeaderDeclaration() { this = range }
239244

240245
/**
241-
* Gets the argument containing the header value.
246+
* Gets the argument containing the header name.
242247
*/
243-
DataFlow::Node getAnInput() { result = range.getAnInput() }
248+
DataFlow::Node getNameArg() { result = range.getNameArg() }
249+
250+
/**
251+
* Gets the argument containing the header name.
252+
*/
253+
DataFlow::Node getValueArg() { result = range.getValueArg() }
244254
}

python/ql/src/experimental/semmle/python/frameworks/Django.qll

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ private module PrivateDjango {
5656
class DjangoResponseSetItemCall extends DataFlow::CallCfgNode, HeaderDeclaration::Range {
5757
DjangoResponseSetItemCall() { this.getFunction() = headerSetItemCall() }
5858

59-
override DataFlow::Node getAnInput() { result = this.getArg([0, 1]) }
59+
override DataFlow::Node getNameArg() { result = this.getArg(0) }
60+
61+
override DataFlow::Node getValueArg() { result = this.getArg(1) }
6062
}
6163

6264
class DjangoResponseDefinition extends DataFlow::Node, HeaderDeclaration::Range {
@@ -67,9 +69,11 @@ private module PrivateDjango {
6769
headerInput.asCfgNode() = this.asCfgNode().(DefinitionNode).getValue()
6870
}
6971

70-
override DataFlow::Node getAnInput() {
71-
result.asExpr() in [headerInput.asExpr(), this.asExpr().(Subscript).getIndex()]
72+
override DataFlow::Node getNameArg() {
73+
result.asExpr() = this.asExpr().(Subscript).getIndex()
7274
}
75+
76+
override DataFlow::Node getValueArg() { result = headerInput }
7377
}
7478
}
7579
}

python/ql/src/experimental/semmle/python/frameworks/Flask.qll

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,31 @@ module ExperimentalFlask {
5454
headerInput.asCfgNode() = this.asCfgNode().(DefinitionNode).getValue()
5555
}
5656

57-
override DataFlow::Node getAnInput() {
58-
result.asExpr() in [headerInput.asExpr(), this.asExpr().(Subscript).getIndex()]
59-
}
57+
override DataFlow::Node getNameArg() { result.asExpr() = this.asExpr().(Subscript).getIndex() }
58+
59+
override DataFlow::Node getValueArg() { result = headerInput }
6060
}
6161

6262
private class FlaskMakeResponseExtend extends DataFlow::CallCfgNode, HeaderDeclaration::Range {
63-
FlaskMakeResponseExtend() { this.getFunction() = headerInstanceCall() }
63+
KeyValuePair item;
64+
65+
FlaskMakeResponseExtend() {
66+
this.getFunction() = headerInstanceCall() and
67+
item = this.getArg(_).asExpr().(Dict).getAnItem()
68+
}
6469

65-
override DataFlow::Node getAnInput() { result = this.getArg(_) }
70+
override DataFlow::Node getNameArg() { result.asExpr() = item.getKey() }
71+
72+
override DataFlow::Node getValueArg() { result.asExpr() = item.getValue() }
6673
}
6774

6875
private class FlaskResponse extends DataFlow::CallCfgNode, HeaderDeclaration::Range {
76+
KeyValuePair item;
77+
6978
FlaskResponse() { this = Flask::Response::classRef().getACall() }
7079

71-
override DataFlow::Node getAnInput() { result = this.getArgByName("headers") }
80+
override DataFlow::Node getNameArg() { result.asExpr() = item.getKey() }
81+
82+
override DataFlow::Node getValueArg() { result.asExpr() = item.getValue() }
7283
}
7384
}

python/ql/src/experimental/semmle/python/frameworks/Werkzeug.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ private module Werkzeug {
2424
this.getFunction().(DataFlow::AttrRead).getAttributeName() = "add"
2525
}
2626

27-
override DataFlow::Node getAnInput() { result = this.getArg(_) }
27+
override DataFlow::Node getNameArg() { result = this.getArg(0) }
28+
29+
override DataFlow::Node getValueArg() { result = this.getArg(1) }
2830
}
2931
}
3032
}

python/ql/src/experimental/semmle/python/security/injection/HTTPHeaders.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class HeaderInjectionFlowConfig extends TaintTracking::Configuration {
1313
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
1414

1515
override predicate isSink(DataFlow::Node sink) {
16-
sink = any(HeaderDeclaration headerDeclaration).getAnInput()
16+
exists(HeaderDeclaration headerDeclaration |
17+
sink in [headerDeclaration.getNameArg(), headerDeclaration.getValueArg()]
18+
)
1719
}
1820
}

0 commit comments

Comments
 (0)