forked from github/codeql
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHtmlTemplateEscapingBypassXss.ql
More file actions
119 lines (105 loc) · 4.19 KB
/
HtmlTemplateEscapingBypassXss.ql
File metadata and controls
119 lines (105 loc) · 4.19 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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
* @name HTML template escaping bypass cross-site scripting
* @description Converting user input to a special type that avoids escaping
* when fed into an HTML template allows for a cross-site
* scripting vulnerability.
* @kind path-problem
* @problem.severity error
* @security-severity 6.1
* @precision high
* @id go/html-template-escaping-bypass-xss
* @tags security
* external/cwe/cwe-079
* external/cwe/cwe-116
*/
import go
/**
* A type that will not be escaped when passed to a `html/template` template.
*/
class UnescapedType extends Type {
UnescapedType() {
this.hasQualifiedName("html/template",
["CSS", "HTML", "HTMLAttr", "JS", "JSStr", "Srcset", "URL"])
}
}
/**
* Holds if the sink is a data value argument of a template execution call.
*
* Note that this is slightly more general than
* `SharedXss::HtmlTemplateSanitizer` because it uses `Function.getACall()`,
* which finds calls through interfaces which the receiver implements. This
* finds more results in practice.
*/
predicate isSinkToTemplateExec(DataFlow::Node sink) {
exists(Method fn, string methodName, DataFlow::CallNode call |
fn.hasQualifiedName("html/template", "Template", methodName) and
call = fn.getACall()
|
methodName = "Execute" and sink = call.getArgument(1)
or
methodName = "ExecuteTemplate" and sink = call.getArgument(2)
)
}
/**
* Data flow configuration that tracks flows from untrusted sources to template execution calls
* which go through a conversion to an unescaped type.
*/
module UntrustedToTemplateExecWithConversionConfig implements DataFlow::StateConfigSig {
private newtype TConversionState =
TUnconverted() or
TConverted(UnescapedType unescapedType)
/**
* The flow state for tracking whether a conversion to an unescaped type has
* occurred.
*/
class FlowState extends TConversionState {
predicate isBeforeConversion() { this instanceof TUnconverted }
predicate isAfterConversion(UnescapedType unescapedType) { this = TConverted(unescapedType) }
/** Gets a textual representation of this element. */
string toString() {
this.isBeforeConversion() and result = "Unconverted"
or
exists(UnescapedType unescapedType | this.isAfterConversion(unescapedType) |
result = "Converted to " + unescapedType.getQualifiedName()
)
}
}
predicate isSource(DataFlow::Node source, FlowState state) {
state.isBeforeConversion() and source instanceof ActiveThreatModelSource
}
predicate isSink(DataFlow::Node sink, FlowState state) {
state.isAfterConversion(_) and isSinkToTemplateExec(sink)
}
predicate isBarrier(DataFlow::Node node) {
node instanceof SharedXss::Sanitizer and not node instanceof SharedXss::HtmlTemplateSanitizer
or
node.getType() instanceof NumericType
}
/**
* When a conversion to a passthrough type is encountered, transition the flow state.
*/
predicate isAdditionalFlowStep(
DataFlow::Node pred, FlowState predState, DataFlow::Node succ, FlowState succState
) {
exists(ConversionExpr conversion, UnescapedType unescapedType |
// If not yet converted, look for a conversion to a passthrough type
predState.isBeforeConversion() and
succState.isAfterConversion(unescapedType) and
succ.(DataFlow::TypeCastNode).getExpr() = conversion and
pred.asExpr() = conversion.getOperand() and
conversion.getType().getUnderlyingType*() = unescapedType
)
}
}
module UntrustedToTemplateExecWithConversionFlow =
TaintTracking::GlobalWithState<UntrustedToTemplateExecWithConversionConfig>;
import UntrustedToTemplateExecWithConversionFlow::PathGraph
from
UntrustedToTemplateExecWithConversionFlow::PathNode untrustedSource,
UntrustedToTemplateExecWithConversionFlow::PathNode templateExecCall, UnescapedType unescapedType
where
UntrustedToTemplateExecWithConversionFlow::flowPath(untrustedSource, templateExecCall) and
templateExecCall.getState().isAfterConversion(unescapedType)
select templateExecCall.getNode(), untrustedSource, templateExecCall,
"Data from an $@ will not be auto-escaped because it was converted to template." +
unescapedType.getName(), untrustedSource.getNode(), "untrusted source"