-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathHeaderInjection.ql
More file actions
94 lines (82 loc) · 3.31 KB
/
HeaderInjection.ql
File metadata and controls
94 lines (82 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/**
* @name HTTP Header Injection
* @description User input should not be used in HTTP headers without first being escaped,
* otherwise a malicious user may be able to inject a value that could manipulate the response.
* @kind path-problem
* @problem.severity error
* @id python/header-injection
* @tags security
* external/cwe/cwe-113
* external/cwe/cwe-079
*/
// determine precision above
import python
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking
import semmle.python.ApiGraphs
import DataFlow::PathGraph
class WerkzeugHeaderCall extends DataFlow::CallCfgNode {
WerkzeugHeaderCall() {
exists(DataFlow::AttrRead addMethod |
this.getFunction() = addMethod and
addMethod.getObject().getALocalSource() =
API::moduleImport("werkzeug").getMember("datastructures").getMember("Headers").getACall() and
addMethod.getAttributeName() = "add"
)
}
DataFlow::Node getHeaderInputNode() { result = this.getArg(1) }
}
class FlaskHeaderCall extends DataFlow::CallCfgNode {
DataFlow::Node headerInputNode;
FlaskHeaderCall() {
exists(
DataFlow::CallCfgNode headerInstance, DataFlow::AttrRead responseMethod,
AssignStmt sinkDeclaration
|
headerInstance = API::moduleImport("flask").getMember("Response").getACall() and
responseMethod.getAttributeName() = "headers" and
responseMethod.getObject().getALocalSource() = headerInstance and
sinkDeclaration.getATarget() = responseMethod.asExpr().getParentNode() and
headerInputNode.asExpr() = sinkDeclaration.getValue() and
this.getFunction() = responseMethod
)
}
DataFlow::Node getHeaderInputNode() { result = headerInputNode }
}
class FlaskMakeResponse extends DataFlow::Node {
FlaskMakeResponse() {
exists(
DataFlow::CallCfgNode headerInstance, DataFlow::AttrRead responseMethod,
AssignStmt sinkDeclaration
|
headerInstance = API::moduleImport("flask").getMember("make_response").getACall() and
responseMethod.getAttributeName() = "headers" and
responseMethod.getObject().getALocalSource() = headerInstance and
(
sinkDeclaration.getATarget() = responseMethod.asExpr().getParentNode() and
this.asExpr() = sinkDeclaration.getValue()
)
//or
//extendMethod.getAttributeName() = "extend" and
//extendMethod.getObject().getALocalSource() = responseMethod and
//this = extendMethod.(DataFlow::CallCfgNode).getArg(0)
)
}
}
class HeaderInjectionSink extends DataFlow::Node {
HeaderInjectionSink() {
this instanceof WerkzeugHeaderCall or
this instanceof FlaskHeaderCall or
this instanceof FlaskMakeResponse
}
}
class HeaderInjectionFlowConfig extends TaintTracking::Configuration {
HeaderInjectionFlowConfig() { this = "HeaderInjectionFlowConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) { sink instanceof HeaderInjectionSink }
}
from HeaderInjectionFlowConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "$@ header is constructed from a $@.", sink.getNode(), "This",
source.getNode(), "user-provided value"