1313
1414import go
1515
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-
3116/**
3217 * A name of a type that will not be escaped when passed to
3318 * a `html/template` template.
@@ -36,65 +21,6 @@ class PassthroughTypeName extends string {
3621 PassthroughTypeName ( ) { this = [ "HTML" , "HTMLAttr" , "JS" , "JSStr" , "CSS" , "Srcset" , "URL" ] }
3722}
3823
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-
9824/**
9925 * Holds if the sink is a data value argument of a template execution call.
10026 */
@@ -109,46 +35,70 @@ predicate isSinkToTemplateExec(DataFlow::Node sink, DataFlow::CallNode call) {
10935 )
11036}
11137
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 }
11445
115- predicate isSink ( DataFlow :: Node sink ) { isSinkToTemplateExec ( sink , _ ) }
46+ predicate isAfterConversion ( ) { this = 1 }
11647}
11748
11849/**
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 .
12152 */
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+ }
12461
125- import FromUntrustedToTemplateExecutionCallFlow:: PathGraph
62+ predicate isBarrier ( DataFlow:: Node node , FlowState state ) {
63+ node instanceof SharedXss:: Sanitizer or node .getType ( ) instanceof NumericType
64+ }
12665
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+ }
13582}
13683
84+ module UntrustedToTemplateExecWithConversionFlow =
85+ DataFlow:: PathGraph< UntrustedToTemplateExecWithConversionConfig > ;
86+
13787from
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
14191where
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+ )
152102select templateExecCall .getNode ( ) , untrustedSource , templateExecCall ,
153103 "Data from an $@ will not be auto-escaped because it was $@ to template." + targetTypeName ,
154104 untrustedSource .getNode ( ) , "untrusted source" , conversion , "converted"
0 commit comments