Commit cc9fed7c authored by Rob Pike's avatar Rob Pike

exp/template: add an html escaping function.

R=golang-dev, dsymonds, adg
CC=golang-dev
https://golang.org/cl/4626092
parent 104f57ad
...@@ -150,6 +150,10 @@ var execTests = []execTest{ ...@@ -150,6 +150,10 @@ var execTests = []execTest{
{"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true}, {"printf field", `{{printf "%s" .U.V}}`, "v", tVal, true},
{"printf method", `{{printf "%s" .Method0}}`, "resultOfMethod0", tVal, true}, {"printf method", `{{printf "%s" .Method0}}`, "resultOfMethod0", tVal, true},
{"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) resultOfMethod0", tVal, true}, {"printf lots", `{{printf "%d %s %g %s" 127 "hello" 7-3i .Method0}}`, "127 hello (7-3i) resultOfMethod0", tVal, true},
{"html", `{{html "<script>alert(\"XSS\");</script>"}}`,
"&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", tVal, true},
{"html pipeline", `{{printf "<script>alert(\"XSS\");</script>" | html}}`,
"&lt;script&gt;alert(&#34;XSS&#34;);&lt;/script&gt;", tVal, true},
// With. // With.
{"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true}, {"with true", "{{with true}}{{.}}{{end}}", "true", tVal, true},
{"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true}, {"with false", "{{with false}}{{.}}{{else}}FALSE{{end}}", "FALSE", tVal, true},
......
...@@ -5,8 +5,11 @@ ...@@ -5,8 +5,11 @@
package template package template
import ( import (
"bytes"
"io"
"fmt" "fmt"
"reflect" "reflect"
"strings"
) )
// FuncMap is the type of the map defining the mapping from names to functions. // FuncMap is the type of the map defining the mapping from names to functions.
...@@ -16,6 +19,7 @@ type FuncMap map[string]interface{} ...@@ -16,6 +19,7 @@ type FuncMap map[string]interface{}
var funcs = map[string]reflect.Value{ var funcs = map[string]reflect.Value{
"printf": reflect.ValueOf(fmt.Sprintf), "printf": reflect.ValueOf(fmt.Sprintf),
"html": reflect.ValueOf(HTMLEscaper),
} }
// addFuncs adds to values the functions in funcs, converting them to reflect.Values. // addFuncs adds to values the functions in funcs, converting them to reflect.Values.
...@@ -61,3 +65,64 @@ func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) { ...@@ -61,3 +65,64 @@ func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) {
} }
return reflect.Value{}, false return reflect.Value{}, false
} }
// HTML escaping
var (
escQuot = []byte("&#34;") // shorter than "&quot;"
escApos = []byte("&#39;") // shorter than "&apos;"
escAmp = []byte("&amp;")
escLt = []byte("&lt;")
escGt = []byte("&gt;")
)
// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
func HTMLEscape(w io.Writer, b []byte) {
last := 0
for i, c := range b {
var esc []byte
switch c {
case '"':
esc = escQuot
case '\'':
esc = escApos
case '&':
esc = escAmp
case '<':
esc = escLt
case '>':
esc = escGt
default:
continue
}
w.Write(b[last:i])
w.Write(esc)
last = i + 1
}
w.Write(b[last:])
}
// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
func HTMLEscapeString(s string) string {
// Avoid allocation if we can.
if strings.IndexAny(s, `'"&<>`) < 0 {
return s
}
var b bytes.Buffer
HTMLEscape(&b, []byte(s))
return b.String()
}
// HTMLEscaper returns the escaped HTML equivalent of the textual
// representation of its arguments.
func HTMLEscaper(args ...interface{}) string {
ok := false
var s string
if len(args) == 1 {
s, ok = args[0].(string)
}
if !ok {
s = fmt.Sprint(args...)
}
return HTMLEscapeString(s)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment