mirror of
https://github.com/gohugoio/hugo.git
synced 2025-08-30 22:39:58 +02:00
@@ -8,7 +8,6 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -26,7 +25,7 @@ func TestAddParseTreeHTML(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
added := Must(root.AddParseTree("b", tree["b"]))
|
||||
b := new(bytes.Buffer)
|
||||
b := new(strings.Builder)
|
||||
err = added.ExecuteTemplate(b, "a", "1>0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -43,7 +42,7 @@ func TestClone(t *testing.T) {
|
||||
// In the t2 template, it will be in a JavaScript context.
|
||||
// In the t3 template, it will be in a CSS context.
|
||||
const tmpl = `{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}`
|
||||
b := new(bytes.Buffer)
|
||||
b := new(strings.Builder)
|
||||
|
||||
// Create an incomplete template t0.
|
||||
t0 := Must(New("t0").Parse(tmpl))
|
||||
|
@@ -284,7 +284,7 @@ func TestTypedContent(t *testing.T) {
|
||||
[]string{
|
||||
`#ZgotmplZ`,
|
||||
`#ZgotmplZ`,
|
||||
// Commas are not esacped
|
||||
// Commas are not escaped.
|
||||
`Hello,#ZgotmplZ`,
|
||||
// Leading spaces are not percent escapes.
|
||||
` dir=%22ltr%22`,
|
||||
@@ -389,7 +389,7 @@ func TestTypedContent(t *testing.T) {
|
||||
tmpl := Must(New("x").Parse(test.input))
|
||||
pre := strings.Index(test.input, "{{.}}")
|
||||
post := len(test.input) - (pre + 5)
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
for i, x := range data {
|
||||
b.Reset()
|
||||
if err := tmpl.Execute(&b, x); err != nil {
|
||||
@@ -423,7 +423,7 @@ func (s *errorer) Error() string {
|
||||
|
||||
func TestStringer(t *testing.T) {
|
||||
s := &myStringer{3}
|
||||
b := new(bytes.Buffer)
|
||||
b := new(strings.Builder)
|
||||
tmpl := Must(New("x").Parse("{{.}}"))
|
||||
if err := tmpl.Execute(b, s); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@@ -80,7 +80,9 @@ func (c context) mangle(templateName string) string {
|
||||
// HTML5 parsing algorithm because a single token production in the HTML
|
||||
// grammar may contain embedded actions in a template. For instance, the quoted
|
||||
// HTML attribute produced by
|
||||
// <div title="Hello {{.World}}">
|
||||
//
|
||||
// <div title="Hello {{.World}}">
|
||||
//
|
||||
// is a single token in HTML's grammar but in a template spans several nodes.
|
||||
type state uint8
|
||||
|
||||
|
@@ -12,14 +12,14 @@ The documentation here focuses on the security features of the package.
|
||||
For information about how to program the templates themselves, see the
|
||||
documentation for text/template.
|
||||
|
||||
Introduction
|
||||
# Introduction
|
||||
|
||||
This package wraps package text/template so you can share its template API
|
||||
to parse and execute HTML templates safely.
|
||||
|
||||
tmpl, err := template.New("name").Parse(...)
|
||||
// Error checking elided
|
||||
err = tmpl.Execute(out, data)
|
||||
tmpl, err := template.New("name").Parse(...)
|
||||
// Error checking elided
|
||||
err = tmpl.Execute(out, data)
|
||||
|
||||
If successful, tmpl will now be injection-safe. Otherwise, err is an error
|
||||
defined in the docs for ErrorCode.
|
||||
@@ -34,38 +34,37 @@ provided below.
|
||||
|
||||
Example
|
||||
|
||||
import template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
|
||||
...
|
||||
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
|
||||
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
|
||||
import template "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
|
||||
...
|
||||
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
|
||||
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
|
||||
|
||||
produces
|
||||
|
||||
Hello, <script>alert('you have been pwned')</script>!
|
||||
Hello, <script>alert('you have been pwned')</script>!
|
||||
|
||||
but the contextual autoescaping in html/template
|
||||
|
||||
import template "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
|
||||
...
|
||||
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
|
||||
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
|
||||
import template "github.com/gohugoio/hugo/tpl/internal/go_templates/htmltemplate"
|
||||
...
|
||||
t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
|
||||
err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
|
||||
|
||||
produces safe, escaped HTML output
|
||||
|
||||
Hello, <script>alert('you have been pwned')</script>!
|
||||
Hello, <script>alert('you have been pwned')</script>!
|
||||
|
||||
|
||||
Contexts
|
||||
# Contexts
|
||||
|
||||
This package understands HTML, CSS, JavaScript, and URIs. It adds sanitizing
|
||||
functions to each simple action pipeline, so given the excerpt
|
||||
|
||||
<a href="/search?q={{.}}">{{.}}</a>
|
||||
<a href="/search?q={{.}}">{{.}}</a>
|
||||
|
||||
At parse time each {{.}} is overwritten to add escaping functions as necessary.
|
||||
In this case it becomes
|
||||
|
||||
<a href="/search?q={{. | urlescaper | attrescaper}}">{{. | htmlescaper}}</a>
|
||||
<a href="/search?q={{. | urlescaper | attrescaper}}">{{. | htmlescaper}}</a>
|
||||
|
||||
where urlescaper, attrescaper, and htmlescaper are aliases for internal escaping
|
||||
functions.
|
||||
@@ -73,117 +72,113 @@ functions.
|
||||
For these internal escaping functions, if an action pipeline evaluates to
|
||||
a nil interface value, it is treated as though it were an empty string.
|
||||
|
||||
Namespaced and data- attributes
|
||||
# Namespaced and data- attributes
|
||||
|
||||
Attributes with a namespace are treated as if they had no namespace.
|
||||
Given the excerpt
|
||||
|
||||
<a my:href="{{.}}"></a>
|
||||
<a my:href="{{.}}"></a>
|
||||
|
||||
At parse time the attribute will be treated as if it were just "href".
|
||||
So at parse time the template becomes:
|
||||
|
||||
<a my:href="{{. | urlescaper | attrescaper}}"></a>
|
||||
<a my:href="{{. | urlescaper | attrescaper}}"></a>
|
||||
|
||||
Similarly to attributes with namespaces, attributes with a "data-" prefix are
|
||||
treated as if they had no "data-" prefix. So given
|
||||
|
||||
<a data-href="{{.}}"></a>
|
||||
<a data-href="{{.}}"></a>
|
||||
|
||||
At parse time this becomes
|
||||
|
||||
<a data-href="{{. | urlescaper | attrescaper}}"></a>
|
||||
<a data-href="{{. | urlescaper | attrescaper}}"></a>
|
||||
|
||||
If an attribute has both a namespace and a "data-" prefix, only the namespace
|
||||
will be removed when determining the context. For example
|
||||
|
||||
<a my:data-href="{{.}}"></a>
|
||||
<a my:data-href="{{.}}"></a>
|
||||
|
||||
This is handled as if "my:data-href" was just "data-href" and not "href" as
|
||||
it would be if the "data-" prefix were to be ignored too. Thus at parse
|
||||
time this becomes just
|
||||
|
||||
<a my:data-href="{{. | attrescaper}}"></a>
|
||||
<a my:data-href="{{. | attrescaper}}"></a>
|
||||
|
||||
As a special case, attributes with the namespace "xmlns" are always treated
|
||||
as containing URLs. Given the excerpts
|
||||
|
||||
<a xmlns:title="{{.}}"></a>
|
||||
<a xmlns:href="{{.}}"></a>
|
||||
<a xmlns:onclick="{{.}}"></a>
|
||||
<a xmlns:title="{{.}}"></a>
|
||||
<a xmlns:href="{{.}}"></a>
|
||||
<a xmlns:onclick="{{.}}"></a>
|
||||
|
||||
At parse time they become:
|
||||
|
||||
<a xmlns:title="{{. | urlescaper | attrescaper}}"></a>
|
||||
<a xmlns:href="{{. | urlescaper | attrescaper}}"></a>
|
||||
<a xmlns:onclick="{{. | urlescaper | attrescaper}}"></a>
|
||||
<a xmlns:title="{{. | urlescaper | attrescaper}}"></a>
|
||||
<a xmlns:href="{{. | urlescaper | attrescaper}}"></a>
|
||||
<a xmlns:onclick="{{. | urlescaper | attrescaper}}"></a>
|
||||
|
||||
Errors
|
||||
# Errors
|
||||
|
||||
See the documentation of ErrorCode for details.
|
||||
|
||||
|
||||
A fuller picture
|
||||
# A fuller picture
|
||||
|
||||
The rest of this package comment may be skipped on first reading; it includes
|
||||
details necessary to understand escaping contexts and error messages. Most users
|
||||
will not need to understand these details.
|
||||
|
||||
|
||||
Contexts
|
||||
# Contexts
|
||||
|
||||
Assuming {{.}} is `O'Reilly: How are <i>you</i>?`, the table below shows
|
||||
how {{.}} appears when used in the context to the left.
|
||||
|
||||
Context {{.}} After
|
||||
{{.}} O'Reilly: How are <i>you</i>?
|
||||
<a title='{{.}}'> O'Reilly: How are you?
|
||||
<a href="/{{.}}"> O'Reilly: How are %3ci%3eyou%3c/i%3e?
|
||||
<a href="?q={{.}}"> O'Reilly%3a%20How%20are%3ci%3e...%3f
|
||||
<a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...?
|
||||
<a onx='f({{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?"
|
||||
<a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f
|
||||
Context {{.}} After
|
||||
{{.}} O'Reilly: How are <i>you</i>?
|
||||
<a title='{{.}}'> O'Reilly: How are you?
|
||||
<a href="/{{.}}"> O'Reilly: How are %3ci%3eyou%3c/i%3e?
|
||||
<a href="?q={{.}}"> O'Reilly%3a%20How%20are%3ci%3e...%3f
|
||||
<a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...?
|
||||
<a onx='f({{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?"
|
||||
<a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f
|
||||
|
||||
If used in an unsafe context, then the value might be filtered out:
|
||||
|
||||
Context {{.}} After
|
||||
<a href="{{.}}"> #ZgotmplZ
|
||||
Context {{.}} After
|
||||
<a href="{{.}}"> #ZgotmplZ
|
||||
|
||||
since "O'Reilly:" is not an allowed protocol like "http:".
|
||||
|
||||
|
||||
If {{.}} is the innocuous word, `left`, then it can appear more widely,
|
||||
|
||||
Context {{.}} After
|
||||
{{.}} left
|
||||
<a title='{{.}}'> left
|
||||
<a href='{{.}}'> left
|
||||
<a href='/{{.}}'> left
|
||||
<a href='?dir={{.}}'> left
|
||||
<a style="border-{{.}}: 4px"> left
|
||||
<a style="align: {{.}}"> left
|
||||
<a style="background: '{{.}}'> left
|
||||
<a style="background: url('{{.}}')> left
|
||||
<style>p.{{.}} {color:red}</style> left
|
||||
Context {{.}} After
|
||||
{{.}} left
|
||||
<a title='{{.}}'> left
|
||||
<a href='{{.}}'> left
|
||||
<a href='/{{.}}'> left
|
||||
<a href='?dir={{.}}'> left
|
||||
<a style="border-{{.}}: 4px"> left
|
||||
<a style="align: {{.}}"> left
|
||||
<a style="background: '{{.}}'> left
|
||||
<a style="background: url('{{.}}')> left
|
||||
<style>p.{{.}} {color:red}</style> left
|
||||
|
||||
Non-string values can be used in JavaScript contexts.
|
||||
If {{.}} is
|
||||
|
||||
struct{A,B string}{ "foo", "bar" }
|
||||
struct{A,B string}{ "foo", "bar" }
|
||||
|
||||
in the escaped template
|
||||
|
||||
<script>var pair = {{.}};</script>
|
||||
<script>var pair = {{.}};</script>
|
||||
|
||||
then the template output is
|
||||
|
||||
<script>var pair = {"A": "foo", "B": "bar"};</script>
|
||||
<script>var pair = {"A": "foo", "B": "bar"};</script>
|
||||
|
||||
See package json to understand how non-string content is marshaled for
|
||||
embedding in JavaScript contexts.
|
||||
|
||||
|
||||
Typed Strings
|
||||
# Typed Strings
|
||||
|
||||
By default, this package assumes that all pipelines produce a plain text string.
|
||||
It adds escaping pipeline stages necessary to correctly and safely embed that
|
||||
@@ -197,24 +192,23 @@ exempted from escaping.
|
||||
|
||||
The template
|
||||
|
||||
Hello, {{.}}!
|
||||
Hello, {{.}}!
|
||||
|
||||
can be invoked with
|
||||
|
||||
tmpl.Execute(out, template.HTML(`<b>World</b>`))
|
||||
tmpl.Execute(out, template.HTML(`<b>World</b>`))
|
||||
|
||||
to produce
|
||||
|
||||
Hello, <b>World</b>!
|
||||
Hello, <b>World</b>!
|
||||
|
||||
instead of the
|
||||
|
||||
Hello, <b>World<b>!
|
||||
Hello, <b>World<b>!
|
||||
|
||||
that would have been produced if {{.}} was a regular string.
|
||||
|
||||
|
||||
Security Model
|
||||
# Security Model
|
||||
|
||||
https://rawgit.com/mikesamuel/sanitized-jquery-templates/trunk/safetemplate.html#problem_definition defines "safe" as used by this package.
|
||||
|
||||
|
@@ -33,14 +33,17 @@ type ErrorCode int
|
||||
//
|
||||
// Output: "ZgotmplZ"
|
||||
// Example:
|
||||
// <img src="{{.X}}">
|
||||
// where {{.X}} evaluates to `javascript:...`
|
||||
//
|
||||
// <img src="{{.X}}">
|
||||
// where {{.X}} evaluates to `javascript:...`
|
||||
//
|
||||
// Discussion:
|
||||
// "ZgotmplZ" is a special value that indicates that unsafe content reached a
|
||||
// CSS or URL context at runtime. The output of the example will be
|
||||
// <img src="#ZgotmplZ">
|
||||
// If the data comes from a trusted source, use content types to exempt it
|
||||
// from filtering: URL(`javascript:...`).
|
||||
//
|
||||
// "ZgotmplZ" is a special value that indicates that unsafe content reached a
|
||||
// CSS or URL context at runtime. The output of the example will be
|
||||
// <img src="#ZgotmplZ">
|
||||
// If the data comes from a trusted source, use content types to exempt it
|
||||
// from filtering: URL(`javascript:...`).
|
||||
const (
|
||||
// OK indicates the lack of an error.
|
||||
OK ErrorCode = iota
|
||||
|
@@ -412,13 +412,19 @@ func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode {
|
||||
// nudge returns the context that would result from following empty string
|
||||
// transitions from the input context.
|
||||
// For example, parsing:
|
||||
// `<a href=`
|
||||
//
|
||||
// `<a href=`
|
||||
//
|
||||
// will end in context{stateBeforeValue, attrURL}, but parsing one extra rune:
|
||||
// `<a href=x`
|
||||
//
|
||||
// `<a href=x`
|
||||
//
|
||||
// will end in context{stateURL, delimSpaceOrTagEnd, ...}.
|
||||
// There are two transitions that happen when the 'x' is seen:
|
||||
// (1) Transition from a before-value state to a start-of-value state without
|
||||
// consuming any character.
|
||||
//
|
||||
// consuming any character.
|
||||
//
|
||||
// (2) Consume 'x' and transition past the first value character.
|
||||
// In this case, nudging produces the context after (1) happens.
|
||||
func nudge(c context) context {
|
||||
|
@@ -693,7 +693,7 @@ func TestEscape(t *testing.T) {
|
||||
t.Errorf("%s: tree not set properly", test.name)
|
||||
continue
|
||||
}
|
||||
b := new(bytes.Buffer)
|
||||
b := new(strings.Builder)
|
||||
if err := tmpl.Execute(b, data); err != nil {
|
||||
t.Errorf("%s: template execution failed: %s", test.name, err)
|
||||
continue
|
||||
@@ -740,7 +740,7 @@ func TestEscapeMap(t *testing.T) {
|
||||
},
|
||||
} {
|
||||
tmpl := Must(New("").Parse(test.input))
|
||||
b := new(bytes.Buffer)
|
||||
b := new(strings.Builder)
|
||||
if err := tmpl.Execute(b, data); err != nil {
|
||||
t.Errorf("%s: template execution failed: %s", test.desc, err)
|
||||
continue
|
||||
@@ -882,7 +882,7 @@ func TestEscapeSet(t *testing.T) {
|
||||
t.Errorf("error parsing %q: %v", source, err)
|
||||
continue
|
||||
}
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
|
||||
if err := tmpl.ExecuteTemplate(&b, "main", data); err != nil {
|
||||
t.Errorf("%q executing %v", err.Error(), tmpl.Lookup("main"))
|
||||
@@ -1833,7 +1833,7 @@ func TestIndirectPrint(t *testing.T) {
|
||||
bp := &b
|
||||
bpp := &bp
|
||||
tmpl := Must(New("t").Parse(`{{.}}`))
|
||||
var buf bytes.Buffer
|
||||
var buf strings.Builder
|
||||
err := tmpl.Execute(&buf, ap)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %s", err)
|
||||
@@ -1876,7 +1876,7 @@ func TestPipeToMethodIsEscaped(t *testing.T) {
|
||||
t.Errorf("panicked: %v\n", panicValue)
|
||||
}
|
||||
}()
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
tmpl.Execute(&b, Issue7379(0))
|
||||
return b.String()
|
||||
}
|
||||
@@ -1909,7 +1909,7 @@ func TestIdempotentExecute(t *testing.T) {
|
||||
Parse(`{{define "main"}}<body>{{template "hello"}}</body>{{end}}`))
|
||||
Must(tmpl.
|
||||
Parse(`{{define "hello"}}Hello, {{"Ladies & Gentlemen!"}}{{end}}`))
|
||||
got := new(bytes.Buffer)
|
||||
got := new(strings.Builder)
|
||||
var err error
|
||||
// Ensure that "hello" produces the same output when executed twice.
|
||||
want := "Hello, Ladies & Gentlemen!"
|
||||
@@ -1952,7 +1952,7 @@ func TestOrphanedTemplate(t *testing.T) {
|
||||
t1 := Must(New("foo").Parse(`<a href="{{.}}">link1</a>`))
|
||||
t2 := Must(t1.New("foo").Parse(`bar`))
|
||||
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
const wantError = `template: "foo" is an incomplete or empty template`
|
||||
if err := t1.Execute(&b, "javascript:alert(1)"); err == nil {
|
||||
t.Fatal("expected error executing t1")
|
||||
@@ -1981,7 +1981,7 @@ func TestAliasedParseTreeDoesNotOverescape(t *testing.T) {
|
||||
if _, err := tpl.AddParseTree("bar", tpl.Tree); err != nil {
|
||||
t.Fatalf("AddParseTree error: %v", err)
|
||||
}
|
||||
var b1, b2 bytes.Buffer
|
||||
var b1, b2 strings.Builder
|
||||
if err := tpl.ExecuteTemplate(&b1, "foo", data); err != nil {
|
||||
t.Fatalf(`ExecuteTemplate failed for "foo": %v`, err)
|
||||
}
|
||||
|
@@ -769,7 +769,7 @@ func mapOfThree() any {
|
||||
}
|
||||
|
||||
func testExecute(execTests []execTest, template *Template, t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
b := new(strings.Builder)
|
||||
funcs := FuncMap{
|
||||
"add": add,
|
||||
"count": count,
|
||||
@@ -861,7 +861,7 @@ func TestDelims(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("delim %q text %q parse err %s", left, text, err)
|
||||
}
|
||||
var b = new(bytes.Buffer)
|
||||
var b = new(strings.Builder)
|
||||
err = tmpl.Execute(b, value)
|
||||
if err != nil {
|
||||
t.Fatalf("delim %q exec err %s", left, err)
|
||||
@@ -1002,7 +1002,7 @@ func TestTree(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal("parse error:", err)
|
||||
}
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
const expect = "[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]"
|
||||
// First by looking up the template.
|
||||
err = tmpl.Lookup("tree").Execute(&b, tree)
|
||||
@@ -1196,33 +1196,39 @@ var cmpTests = []cmpTest{
|
||||
{"eq .Iface1 .Iface1", "true", true},
|
||||
{"eq .Iface1 .Iface2", "false", true},
|
||||
{"eq .Iface2 .Iface2", "true", true},
|
||||
{"eq .Map .Map", "true", true}, // Uncomparable types but nil is OK.
|
||||
{"eq .Map nil", "true", true}, // Uncomparable types but nil is OK.
|
||||
{"eq nil .Map", "true", true}, // Uncomparable types but nil is OK.
|
||||
{"eq .Map .NonNilMap", "false", true}, // Uncomparable types but nil is OK.
|
||||
// Errors
|
||||
{"eq `xy` 1", "", false}, // Different types.
|
||||
{"eq 2 2.0", "", false}, // Different types.
|
||||
{"lt true true", "", false}, // Unordered types.
|
||||
{"lt 1+0i 1+0i", "", false}, // Unordered types.
|
||||
{"eq .Ptr 1", "", false}, // Incompatible types.
|
||||
{"eq .Ptr .NegOne", "", false}, // Incompatible types.
|
||||
{"eq .Map .Map", "", false}, // Uncomparable types.
|
||||
{"eq .Map .V1", "", false}, // Uncomparable types.
|
||||
{"eq `xy` 1", "", false}, // Different types.
|
||||
{"eq 2 2.0", "", false}, // Different types.
|
||||
{"lt true true", "", false}, // Unordered types.
|
||||
{"lt 1+0i 1+0i", "", false}, // Unordered types.
|
||||
{"eq .Ptr 1", "", false}, // Incompatible types.
|
||||
{"eq .Ptr .NegOne", "", false}, // Incompatible types.
|
||||
{"eq .Map .V1", "", false}, // Uncomparable types.
|
||||
{"eq .NonNilMap .NonNilMap", "", false}, // Uncomparable types.
|
||||
}
|
||||
|
||||
func TestComparison(t *testing.T) {
|
||||
b := new(bytes.Buffer)
|
||||
b := new(strings.Builder)
|
||||
var cmpStruct = struct {
|
||||
Uthree, Ufour uint
|
||||
NegOne, Three int
|
||||
Ptr, NilPtr *int
|
||||
NonNilMap map[int]int
|
||||
Map map[int]int
|
||||
V1, V2 V
|
||||
Iface1, Iface2 fmt.Stringer
|
||||
}{
|
||||
Uthree: 3,
|
||||
Ufour: 4,
|
||||
NegOne: -1,
|
||||
Three: 3,
|
||||
Ptr: new(int),
|
||||
Iface1: b,
|
||||
Uthree: 3,
|
||||
Ufour: 4,
|
||||
NegOne: -1,
|
||||
Three: 3,
|
||||
Ptr: new(int),
|
||||
NonNilMap: make(map[int]int),
|
||||
Iface1: b,
|
||||
}
|
||||
for _, test := range cmpTests {
|
||||
text := fmt.Sprintf("{{if %s}}true{{else}}false{{end}}", test.expr)
|
||||
@@ -1254,7 +1260,7 @@ func TestMissingMapKey(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
// By default, just get "<no value>" // NOTE: not in html/template, get empty string
|
||||
err = tmpl.Execute(&b, data)
|
||||
if err != nil {
|
||||
@@ -1423,7 +1429,7 @@ func TestBlock(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
var buf strings.Builder
|
||||
if err := tmpl.Execute(&buf, "hello"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -1529,7 +1535,7 @@ func TestAddrOfIndex(t *testing.T) {
|
||||
}
|
||||
for _, text := range texts {
|
||||
tmpl := Must(New("tmpl").Parse(text))
|
||||
var buf bytes.Buffer
|
||||
var buf strings.Builder
|
||||
err := tmpl.Execute(&buf, reflect.ValueOf([]V{{1}}))
|
||||
if err != nil {
|
||||
t.Fatalf("%s: Execute: %v", text, err)
|
||||
@@ -1585,7 +1591,7 @@ func TestInterfaceValues(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
tmpl := Must(New("tmpl").Parse(tt.text))
|
||||
var buf bytes.Buffer
|
||||
var buf strings.Builder
|
||||
err := tmpl.Execute(&buf, map[string]any{
|
||||
"PlusOne": func(n int) int {
|
||||
return n + 1
|
||||
@@ -1680,7 +1686,7 @@ func TestIssue31810(t *testing.T) {
|
||||
t.Skip("broken in html/template")
|
||||
|
||||
// A simple value with no arguments is fine.
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
const text = "{{ (.) }}"
|
||||
tmpl, err := New("").Parse(text)
|
||||
if err != nil {
|
||||
|
@@ -84,10 +84,12 @@ var htmlNormReplacementTable = []string{
|
||||
// <script>(function () {
|
||||
// var a = [], d = document.getElementById("d"), i, c, s;
|
||||
// for (i = 0; i < 0x10000; ++i) {
|
||||
// c = String.fromCharCode(i);
|
||||
// d.innerHTML = "<span title=" + c + "lt" + c + "></span>"
|
||||
// s = d.getElementsByTagName("SPAN")[0];
|
||||
// if (!s || s.title !== c + "lt" + c) { a.push(i.toString(16)); }
|
||||
//
|
||||
// c = String.fromCharCode(i);
|
||||
// d.innerHTML = "<span title=" + c + "lt" + c + "></span>"
|
||||
// s = d.getElementsByTagName("SPAN")[0];
|
||||
// if (!s || s.title !== c + "lt" + c) { a.push(i.toString(16)); }
|
||||
//
|
||||
// }
|
||||
// document.write(a.join(", "));
|
||||
// })()</script>
|
||||
@@ -174,7 +176,7 @@ func htmlReplacer(s string, replacementTable []string, badRunes bool) string {
|
||||
// stripTags takes a snippet of HTML and returns only the text content.
|
||||
// For example, `<b>¡Hi!</b> <script>...</script>` -> `¡Hi! `.
|
||||
func stripTags(html string) string {
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
s, c, i, allText := []byte(html), context{}, 0, true
|
||||
// Using the transition funcs helps us avoid mangling
|
||||
// `<div title="1>2">` or `I <3 Ponies!`.
|
||||
|
@@ -8,7 +8,6 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -324,7 +323,7 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
|
||||
|
||||
// Escape it rune by rune to make sure that any
|
||||
// fast-path checking does not break escaping.
|
||||
var buf bytes.Buffer
|
||||
var buf strings.Builder
|
||||
for _, c := range input {
|
||||
buf.WriteString(test.escaper(string(c)))
|
||||
}
|
||||
|
@@ -11,8 +11,8 @@ package template
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
|
||||
@@ -249,7 +249,7 @@ func TestEmptyTemplate(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
buf := &strings.Builder{}
|
||||
if err := m.Execute(buf, c.in); err != nil {
|
||||
t.Error(i, err)
|
||||
continue
|
||||
@@ -284,7 +284,7 @@ func TestIssue19294(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
var buf strings.Builder
|
||||
res.Execute(&buf, 0)
|
||||
if buf.String() != "stylesheet" {
|
||||
t.Fatalf("iteration %d: got %q; expected %q", i, buf.String(), "stylesheet")
|
||||
|
@@ -65,6 +65,7 @@ func (t *Template) Templates() []*Template {
|
||||
//
|
||||
// missingkey: Control the behavior during execution if a map is
|
||||
// indexed with a key that is not present in the map.
|
||||
//
|
||||
// "missingkey=default" or "missingkey=invalid"
|
||||
// The default behavior: Do nothing and continue execution.
|
||||
// If printed, the result of the index operation is the string
|
||||
@@ -73,7 +74,6 @@ func (t *Template) Templates() []*Template {
|
||||
// The operation returns the zero value for the map type's element.
|
||||
// "missingkey=error"
|
||||
// Execution stops immediately with an error.
|
||||
//
|
||||
func (t *Template) Option(opt ...string) *Template {
|
||||
t.text.Option(opt...)
|
||||
return t
|
||||
@@ -329,14 +329,7 @@ func (t *Template) Name() string {
|
||||
return t.text.Name()
|
||||
}
|
||||
|
||||
// FuncMap is the type of the map defining the mapping from names to
|
||||
// functions. Each function must have either a single return value, or two
|
||||
// return values of which the second has type error. In that case, if the
|
||||
// second (error) argument evaluates to non-nil during execution, execution
|
||||
// terminates and Execute returns that error. FuncMap has the same base type
|
||||
// as FuncMap in "text/template", copied here so clients need not import
|
||||
// "text/template".
|
||||
type FuncMap map[string]any
|
||||
type FuncMap = template.FuncMap
|
||||
|
||||
// Funcs adds the elements of the argument map to the template's function map.
|
||||
// It must be called before the template is parsed.
|
||||
@@ -369,6 +362,7 @@ func (t *Template) Lookup(name string) *Template {
|
||||
// Must is a helper that wraps a call to a function returning (*Template, error)
|
||||
// and panics if the error is non-nil. It is intended for use in variable initializations
|
||||
// such as
|
||||
//
|
||||
// var t = template.Must(template.New("name").Parse("html"))
|
||||
func Must(t *Template, err error) *Template {
|
||||
if err != nil {
|
||||
|
@@ -30,7 +30,7 @@ func TestTemplateClone(t *testing.T) {
|
||||
|
||||
const want = "stuff"
|
||||
parsed := Must(clone.Parse(want))
|
||||
var buf bytes.Buffer
|
||||
var buf strings.Builder
|
||||
err = parsed.Execute(&buf, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -211,7 +211,7 @@ func (c *testCase) mustNotParse(t *Template, text string) {
|
||||
}
|
||||
|
||||
func (c *testCase) mustExecute(t *Template, val any, want string) {
|
||||
var buf bytes.Buffer
|
||||
var buf strings.Builder
|
||||
err := t.Execute(&buf, val)
|
||||
if err != nil {
|
||||
c.t.Fatalf("execute: %v", err)
|
||||
|
@@ -5,7 +5,6 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
@@ -19,15 +18,15 @@ import (
|
||||
//
|
||||
// This filter conservatively assumes that all schemes other than the following
|
||||
// are unsafe:
|
||||
// * http: Navigates to a new website, and may open a new window or tab.
|
||||
// These side effects can be reversed by navigating back to the
|
||||
// previous website, or closing the window or tab. No irreversible
|
||||
// changes will take place without further user interaction with
|
||||
// the new website.
|
||||
// * https: Same as http.
|
||||
// * mailto: Opens an email program and starts a new draft. This side effect
|
||||
// is not irreversible until the user explicitly clicks send; it
|
||||
// can be undone by closing the email program.
|
||||
// - http: Navigates to a new website, and may open a new window or tab.
|
||||
// These side effects can be reversed by navigating back to the
|
||||
// previous website, or closing the window or tab. No irreversible
|
||||
// changes will take place without further user interaction with
|
||||
// the new website.
|
||||
// - https: Same as http.
|
||||
// - mailto: Opens an email program and starts a new draft. This side effect
|
||||
// is not irreversible until the user explicitly clicks send; it
|
||||
// can be undone by closing the email program.
|
||||
//
|
||||
// To allow URLs containing other schemes to bypass this filter, developers must
|
||||
// explicitly indicate that such a URL is expected and safe by encapsulating it
|
||||
@@ -76,7 +75,7 @@ func urlProcessor(norm bool, args ...any) string {
|
||||
if t == contentTypeURL {
|
||||
norm = true
|
||||
}
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
if processURLOnto(s, norm, &b) {
|
||||
return b.String()
|
||||
}
|
||||
@@ -85,7 +84,7 @@ func urlProcessor(norm bool, args ...any) string {
|
||||
|
||||
// processURLOnto appends a normalized URL corresponding to its input to b
|
||||
// and reports whether the appended content differs from s.
|
||||
func processURLOnto(s string, norm bool, b *bytes.Buffer) bool {
|
||||
func processURLOnto(s string, norm bool, b *strings.Builder) bool {
|
||||
b.Grow(len(s) + 16)
|
||||
written := 0
|
||||
// The byte loop below assumes that all URLs use UTF-8 as the
|
||||
@@ -149,7 +148,7 @@ func srcsetFilterAndEscaper(args ...any) string {
|
||||
case contentTypeURL:
|
||||
// Normalizing gets rid of all HTML whitespace
|
||||
// which separate the image URL from its metadata.
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
if processURLOnto(s, true, &b) {
|
||||
s = b.String()
|
||||
}
|
||||
@@ -157,7 +156,7 @@ func srcsetFilterAndEscaper(args ...any) string {
|
||||
return strings.ReplaceAll(s, ",", "%2c")
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
var b strings.Builder
|
||||
written := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == ',' {
|
||||
@@ -183,7 +182,7 @@ func isHTMLSpaceOrASCIIAlnum(c byte) bool {
|
||||
return (c < 0x80) && 0 != (htmlSpaceAndASCIIAlnumBytes[c>>3]&(1<<uint(c&0x7)))
|
||||
}
|
||||
|
||||
func filterSrcsetElement(s string, left int, right int, b *bytes.Buffer) {
|
||||
func filterSrcsetElement(s string, left int, right int, b *strings.Builder) {
|
||||
start := left
|
||||
for start < right && isHTMLSpace(s[start]) {
|
||||
start++
|
||||
|
Reference in New Issue
Block a user