shithub: hugo

Download patch

ref: c5a4c07b892128f36ec4638bfd44e5404437e066
parent: 7c9a2dfee242481f37b30d5f053ee3275126e9dd
author: Cameron Moore <[email protected]>
date: Sun Nov 15 09:30:57 EST 2015

Add SafeJS template function

This commit adds a SafeJS template function.  Tests and documentation are
included.

Fixes #1579

--- a/docs/content/templates/functions.md
+++ b/docs/content/templates/functions.md
@@ -456,6 +456,21 @@
 Note: "ZgotmplZ" is a special value that indicates that unsafe content reached a
 CSS or URL context.
 
+### safeJS
+
+Declares the provided string as a known "safe" Javascript string so Go
+html/templates will not escape it.  "Safe" means the string encapsulates a known
+safe EcmaScript5 Expression, for example, `(x + y * z())`. Template authors
+are responsible for ensuring that typed expressions do not break the intended
+precedence and that there is no statement/expression ambiguity as when passing
+an expression like `{ foo:bar() }\n['foo']()`, which is both a valid Expression
+and a valid Program with a very different meaning.
+
+Example: Given `hash = "619c16f"` defined in the front matter of your `.md` file:
+
+* `<script>var form_{{ .Params.hash | safeJS }};…</script>` ⇒ `<script>var form_619c16f;…</script>` (Good!)
+* `<script>var form_{{ .Params.hash }};…</script>` ⇒ `<script>var form_"619c16f";…</script>` (Bad!)
+
 ### singularize
 Singularize the given word with a set of common English singularization rules.
 
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -14,7 +14,6 @@
 package tpl
 
 import (
-	"bitbucket.org/pkg/inflect"
 	"bytes"
 	"encoding/base64"
 	"errors"
@@ -28,6 +27,8 @@
 	"strings"
 	"time"
 
+	"bitbucket.org/pkg/inflect"
+
 	"github.com/spf13/cast"
 	"github.com/spf13/hugo/helpers"
 	jww "github.com/spf13/jwalterweatherman"
@@ -1192,6 +1193,9 @@
 
 func SafeHTML(a string) template.HTML { return template.HTML(a) }
 
+// SafeJS returns the given string as a template.JS type from html/template.
+func SafeJS(a string) template.JS { return template.JS(a) }
+
 func doArithmetic(a, b interface{}, op rune) (interface{}, error) {
 	av := reflect.ValueOf(a)
 	bv := reflect.ValueOf(b)
@@ -1393,6 +1397,7 @@
 		"echoParam":    ReturnWhenSet,
 		"safeHTML":     SafeHTML,
 		"safeCSS":      SafeCSS,
+		"safeJS":       SafeJS,
 		"safeURL":      SafeURL,
 		"absURL":       func(a string) template.HTML { return template.HTML(helpers.AbsURL(a)) },
 		"relURL":       func(a string) template.HTML { return template.HTML(helpers.RelURL(a)) },
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -1580,6 +1580,41 @@
 	}
 }
 
+func TestSafeJS(t *testing.T) {
+	for i, this := range []struct {
+		str                 string
+		tmplStr             string
+		expectWithoutEscape string
+		expectWithEscape    string
+	}{
+		{`619c16f`, `<script>var x{{ . }};</script>`, `<script>var x"619c16f";</script>`, `<script>var x619c16f;</script>`},
+	} {
+		tmpl, err := template.New("test").Parse(this.tmplStr)
+		if err != nil {
+			t.Errorf("[%d] unable to create new html template %q: %s", i, this.tmplStr, err)
+			continue
+		}
+
+		buf := new(bytes.Buffer)
+		err = tmpl.Execute(buf, this.str)
+		if err != nil {
+			t.Errorf("[%d] execute template with a raw string value returns unexpected error: %s", i, err)
+		}
+		if buf.String() != this.expectWithoutEscape {
+			t.Errorf("[%d] execute template with a raw string value, got %v but expected %v", i, buf.String(), this.expectWithoutEscape)
+		}
+
+		buf.Reset()
+		err = tmpl.Execute(buf, SafeJS(this.str))
+		if err != nil {
+			t.Errorf("[%d] execute template with an escaped string value by SafeJS returns unexpected error: %s", i, err)
+		}
+		if buf.String() != this.expectWithEscape {
+			t.Errorf("[%d] execute template with an escaped string value by SafeJS, got %v but expected %v", i, buf.String(), this.expectWithEscape)
+		}
+	}
+}
+
 func TestSafeURL(t *testing.T) {
 	for i, this := range []struct {
 		str                 string