Go's html/template
library does contextual autoescaping. The package tries to ensure any input data is safe to insert into the HTML. It transforms input data differently depending on whether you're inserting it as straight up text in the HTML, or JavaScript code inside a <script>
tag.
It turns out you can see what autoescaping html/template
is doing under the hood. See the code below for a few examples.
One thing I thought was interesting is that Go tries to detect whether to JavaScript escape the input based on the mime type of the <script>
tag.
package main
import (
"bytes"
"html/template"
"testing"
)
func TestRunTemplate(t *testing.T) {
type testData struct {
inputTemplate string
expectedOutputTemplate string
input any
expectedOutput any
}
runTest := func(test testData) {
t.Helper()
outputTemplate, output := runTemplate(t, test.inputTemplate, test.input)
if outputTemplate != test.expectedOutputTemplate {
t.Errorf("outputTemplate: got %s, want %s\n", outputTemplate, test.expectedOutputTemplate)
}
if output != test.expectedOutput {
t.Errorf("output: got %s, want %s\n", output, test.expectedOutput)
}
}
runTest(testData{
inputTemplate: `<script>{{.}}</script>`,
expectedOutputTemplate: `<script>{{. | _html_template_jsvalescaper}}</script>`,
input: `<body>`,
expectedOutput: `<script>"\u003cbody\u003e"</script>`,
})
runTest(testData{
inputTemplate: `<script type="foo">{{.}}</script>`,
expectedOutputTemplate: `<script type="foo">{{. | _html_template_htmlescaper}}</script>`,
input: `<body>`,
expectedOutput: `<script type="foo"><body></script>`,
})
runTest(testData{
inputTemplate: `<script type="bar">{{.}}</script>`,
expectedOutputTemplate: `<script type="bar">{{. | _html_template_htmlescaper}}</script>`,
input: template.HTML(`<body>`),
expectedOutput: `<script type="bar"><body></script>`,
})
}
func runTemplate(t *testing.T, templateText string, input any) (outputTemplate, output string) {
tmpl := template.Must(template.New("").Parse(templateText))
var out bytes.Buffer
if err := tmpl.Execute(&out, input); err != nil {
t.Fatalf("template.Execute: %v", err)
}
return tmpl.Tree.Root.String(), out.String()
}