13
13
14
14
import go
15
15
16
- /**
17
- * Holds if the provided `untrusted` node flows into a conversion to a PassthroughType.
18
- * The `targetType` parameter gets populated with the name of the PassthroughType,
19
- * and `conversionSink` gets populated with the node where the conversion happens.
20
- */
21
- predicate flowsFromUntrustedToConversion (
22
- DataFlow:: Node untrusted , PassthroughTypeName targetType , DataFlow:: Node conversionSink
23
- ) {
24
- exists ( DataFlow:: Node source |
25
- UntrustedToPassthroughTypeConversionFlow:: flow ( source , conversionSink ) and
26
- source = untrusted and
27
- UntrustedToPassthroughTypeConversionConfig:: isSinkToPassthroughType ( conversionSink , targetType )
28
- )
29
- }
30
-
31
16
/**
32
17
* A name of a type that will not be escaped when passed to
33
18
* a `html/template` template.
@@ -36,65 +21,6 @@ class PassthroughTypeName extends string {
36
21
PassthroughTypeName ( ) { this = [ "HTML" , "HTMLAttr" , "JS" , "JSStr" , "CSS" , "Srcset" , "URL" ] }
37
22
}
38
23
39
- module UntrustedToPassthroughTypeConversionConfig implements DataFlow:: ConfigSig {
40
- predicate isSource ( DataFlow:: Node source ) { source instanceof ActiveThreatModelSource }
41
-
42
- additional predicate isSinkToPassthroughType ( DataFlow:: TypeCastNode sink , PassthroughTypeName name ) {
43
- exists ( Type typ |
44
- typ = sink .getResultType ( ) and
45
- typ .getUnderlyingType * ( ) .hasQualifiedName ( "html/template" , name )
46
- )
47
- }
48
-
49
- predicate isSink ( DataFlow:: Node sink ) { isSinkToPassthroughType ( sink , _) }
50
-
51
- predicate isBarrier ( DataFlow:: Node node ) {
52
- node instanceof SharedXss:: Sanitizer or node .getType ( ) instanceof NumericType
53
- }
54
- }
55
-
56
- /**
57
- * Tracks taint flow for reasoning about when a `ActiveThreatModelSource` is
58
- * converted into a special "passthrough" type which will not be escaped by the
59
- * template generator; this allows the injection of arbitrary content (html,
60
- * css, js) into the generated output of the templates.
61
- */
62
- module UntrustedToPassthroughTypeConversionFlow =
63
- TaintTracking:: Global< UntrustedToPassthroughTypeConversionConfig > ;
64
-
65
- /**
66
- * Holds if the provided `conversion` node flows into the provided `execSink`.
67
- */
68
- predicate flowsFromConversionToExec (
69
- DataFlow:: Node conversion , PassthroughTypeName targetType , DataFlow:: Node execSink
70
- ) {
71
- PassthroughTypeConversionToTemplateExecutionCallFlow:: flow ( conversion , execSink ) and
72
- PassthroughTypeConversionToTemplateExecutionCallConfig:: isSourceConversionToPassthroughType ( conversion ,
73
- targetType )
74
- }
75
-
76
- module PassthroughTypeConversionToTemplateExecutionCallConfig implements DataFlow:: ConfigSig {
77
- predicate isSource ( DataFlow:: Node source ) { isSourceConversionToPassthroughType ( source , _) }
78
-
79
- additional predicate isSourceConversionToPassthroughType (
80
- DataFlow:: TypeCastNode source , PassthroughTypeName name
81
- ) {
82
- exists ( Type typ |
83
- typ = source .getResultType ( ) and
84
- typ .getUnderlyingType * ( ) .hasQualifiedName ( "html/template" , name )
85
- )
86
- }
87
-
88
- predicate isSink ( DataFlow:: Node sink ) { isSinkToTemplateExec ( sink , _) }
89
- }
90
-
91
- /**
92
- * Tracks taint flow for reasoning about when the result of a conversion to a
93
- * PassthroughType flows to a template execution call.
94
- */
95
- module PassthroughTypeConversionToTemplateExecutionCallFlow =
96
- TaintTracking:: Global< PassthroughTypeConversionToTemplateExecutionCallConfig > ;
97
-
98
24
/**
99
25
* Holds if the sink is a data value argument of a template execution call.
100
26
*/
@@ -109,46 +35,70 @@ predicate isSinkToTemplateExec(DataFlow::Node sink, DataFlow::CallNode call) {
109
35
)
110
36
}
111
37
112
- module FromUntrustedToTemplateExecutionCallConfig implements DataFlow:: ConfigSig {
113
- predicate isSource ( DataFlow:: Node source ) { source instanceof ActiveThreatModelSource }
38
+ /**
39
+ * Flow state for tracking whether a conversion to a passthrough type has occurred.
40
+ */
41
+ class FlowState extends int {
42
+ FlowState ( ) { this = 0 or this = 1 }
43
+
44
+ predicate isBeforeConversion ( ) { this = 0 }
114
45
115
- predicate isSink ( DataFlow :: Node sink ) { isSinkToTemplateExec ( sink , _ ) }
46
+ predicate isAfterConversion ( ) { this = 1 }
116
47
}
117
48
118
49
/**
119
- * Tracks taint flow from a `ActiveThreatModelSource` into a template executor
120
- * call .
50
+ * Data flow configuration that tracks flows from untrusted sources (A) to template execution calls (C),
51
+ * and tracks whether a conversion to a passthrough type (B) has occurred .
121
52
*/
122
- module FromUntrustedToTemplateExecutionCallFlow =
123
- TaintTracking:: Global< FromUntrustedToTemplateExecutionCallConfig > ;
53
+ module UntrustedToTemplateExecWithConversionConfig implements DataFlow:: ConfigSig {
54
+ predicate isSource ( DataFlow:: Node source , FlowState state ) {
55
+ state .isBeforeConversion ( ) and source instanceof ActiveThreatModelSource
56
+ }
57
+
58
+ predicate isSink ( DataFlow:: Node sink , FlowState state ) {
59
+ state .isAfterConversion ( ) and isSinkToTemplateExec ( sink , _)
60
+ }
124
61
125
- import FromUntrustedToTemplateExecutionCallFlow:: PathGraph
62
+ predicate isBarrier ( DataFlow:: Node node , FlowState state ) {
63
+ node instanceof SharedXss:: Sanitizer or node .getType ( ) instanceof NumericType
64
+ }
126
65
127
- /**
128
- * Holds if the provided `untrusted` node flows into the provided `execSink`.
129
- */
130
- predicate flowsFromUntrustedToExec (
131
- FromUntrustedToTemplateExecutionCallFlow:: PathNode untrusted ,
132
- FromUntrustedToTemplateExecutionCallFlow:: PathNode execSink
133
- ) {
134
- FromUntrustedToTemplateExecutionCallFlow:: flowPath ( untrusted , execSink )
66
+ /**
67
+ * When a conversion to a passthrough type is encountered, transition the flow state.
68
+ */
69
+ predicate step ( DataFlow:: Node pred , FlowState predState , DataFlow:: Node succ , FlowState succState ) {
70
+ // If not yet converted, look for a conversion to a passthrough type
71
+ predState .isBeforeConversion ( ) and
72
+ succState .isAfterConversion ( ) and
73
+ pred = succ and
74
+ exists ( Type typ , PassthroughTypeName name |
75
+ typ = pred .getType ( ) and
76
+ typ .getUnderlyingType * ( ) .hasQualifiedName ( "html/template" , name )
77
+ )
78
+ or
79
+ // Otherwise, normal flow with unchanged state
80
+ predState = succState
81
+ }
135
82
}
136
83
84
+ module UntrustedToTemplateExecWithConversionFlow =
85
+ DataFlow:: PathGraph< UntrustedToTemplateExecWithConversionConfig > ;
86
+
137
87
from
138
- FromUntrustedToTemplateExecutionCallFlow :: PathNode untrustedSource ,
139
- FromUntrustedToTemplateExecutionCallFlow :: PathNode templateExecCall ,
140
- PassthroughTypeName targetTypeName , DataFlow :: Node conversion
88
+ UntrustedToTemplateExecWithConversionFlow :: PathNode untrustedSource ,
89
+ UntrustedToTemplateExecWithConversionFlow :: PathNode templateExecCall , DataFlow :: Node conversion ,
90
+ PassthroughTypeName targetTypeName
141
91
where
142
- // A = untrusted remote flow source
143
- // B = conversion to PassthroughType
144
- // C = template execution call
145
- // Flows:
146
- // A -> B
147
- flowsFromUntrustedToConversion ( untrustedSource . getNode ( ) , targetTypeName , conversion ) and
148
- // B -> C
149
- flowsFromConversionToExec ( conversion , targetTypeName , templateExecCall . getNode ( ) ) and
150
- // A -> C
151
- flowsFromUntrustedToExec ( untrustedSource , templateExecCall )
92
+ UntrustedToTemplateExecWithConversionFlow :: flowPath ( untrustedSource , templateExecCall ) and
93
+ // Find the conversion node in the path
94
+ exists ( int i |
95
+ i = templateExecCall . getPathIndex ( ) - 1 and
96
+ conversion = templateExecCall . getPathNode ( i ) . getNode ( ) and
97
+ exists ( Type typ |
98
+ typ = conversion . getType ( ) and
99
+ typ . getUnderlyingType * ( ) . hasQualifiedName ( "html/template" , targetTypeName )
100
+ )
101
+ )
152
102
select templateExecCall .getNode ( ) , untrustedSource , templateExecCall ,
153
103
"Data from an $@ will not be auto-escaped because it was $@ to template." + targetTypeName ,
154
104
untrustedSource .getNode ( ) , "untrusted source" , conversion , "converted"
0 commit comments