templates: Use text/template; add experimental notice to docs

Using html/template.HTML like we were doing before caused nested include
to be HTML-escaped, which breaks sites. Now we do not escape any of the
output; template input is usually trusted, and if it's not, users should
employ escaping actions within their templates to keep it safe. The docs
already said this.
This commit is contained in:
Matthew Holt 2020-04-06 12:50:54 -06:00
parent 145aebbba5
commit 437d5095a6
No known key found for this signature in database
GPG Key ID: 2A349DD577D586A5
3 changed files with 12 additions and 13 deletions

View File

@ -33,6 +33,8 @@ func init() {
// The syntax is documented in the Go standard library's
// [text/template package](https://golang.org/pkg/text/template/).
//
// ⚠️ Template functions/actions are still experimental, so they are subject to change.
//
// [All Sprig functions](https://masterminds.github.io/sprig/) are supported.
//
// In addition to the standard functions and Sprig functions, Caddy adds

View File

@ -17,7 +17,6 @@ package templates
import (
"bytes"
"fmt"
"html/template"
"io"
"net"
"net/http"
@ -25,6 +24,7 @@ import (
"strconv"
"strings"
"sync"
"text/template"
"github.com/Masterminds/sprig/v3"
"github.com/alecthomas/chroma/formatters/html"
@ -57,7 +57,7 @@ func (c templateContext) OriginalReq() http.Request {
// Note that included files are NOT escaped, so you should only include
// trusted files. If it is not trusted, be sure to use escaping functions
// in your template.
func (c templateContext) funcInclude(filename string, args ...interface{}) (template.HTML, error) {
func (c templateContext) funcInclude(filename string, args ...interface{}) (string, error) {
if c.Root == nil {
return "", fmt.Errorf("root file system not specified")
}
@ -84,14 +84,14 @@ func (c templateContext) funcInclude(filename string, args ...interface{}) (temp
return "", err
}
return template.HTML(bodyBuf.String()), nil
return bodyBuf.String(), nil
}
// funcHTTPInclude returns the body of a virtual (lightweight) request
// to the given URI on the same server. Note that included bodies
// are NOT escaped, so you should only include trusted resources.
// If it is not trusted, be sure to use escaping functions yourself.
func (c templateContext) funcHTTPInclude(uri string) (template.HTML, error) {
func (c templateContext) funcHTTPInclude(uri string) (string, error) {
// prevent virtual request loops by counting how many levels
// deep we are; and if we get too deep, return an error
recursionCount := 1
@ -132,7 +132,7 @@ func (c templateContext) funcHTTPInclude(uri string) (template.HTML, error) {
return "", err
}
return template.HTML(buf.String()), nil
return buf.String(), nil
}
func (c templateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
@ -231,7 +231,7 @@ func (c templateContext) funcStripHTML(s string) string {
// funcMarkdown renders the markdown body as HTML. The resulting
// HTML is NOT escaped so that it can be rendered as HTML.
func (c templateContext) funcMarkdown(input interface{}) (template.HTML, error) {
func (c templateContext) funcMarkdown(input interface{}) (string, error) {
inputStr := toString(input)
md := goldmark.New(
@ -259,7 +259,7 @@ func (c templateContext) funcMarkdown(input interface{}) (template.HTML, error)
md.Convert([]byte(inputStr), buf)
return template.HTML(buf.String()), nil
return buf.String(), nil
}
// splitFrontMatter parses front matter out from the beginning of input,
@ -338,14 +338,12 @@ func toString(input interface{}) string {
switch v := input.(type) {
case string:
return v
case template.HTML:
return string(v)
case fmt.Stringer:
return v.String()
case error:
return v.Error()
default:
return fmt.Sprintf("%s", input)
return fmt.Sprintf("%v", input)
}
}
@ -357,6 +355,6 @@ var bufPool = sync.Pool{
// at time of writing, sprig.FuncMap() makes a copy, thus
// involves iterating the whole map, so do it just once
var sprigFuncMap = sprig.FuncMap()
var sprigFuncMap = sprig.TxtFuncMap()
const recursionPreventionHeader = "Caddy-Templates-Include"

View File

@ -31,7 +31,6 @@ package templates
import (
"bytes"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"os"
@ -48,7 +47,7 @@ func TestMarkdown(t *testing.T) {
for i, test := range []struct {
body string
expect template.HTML
expect string
}{
{
body: "- str1\n- str2\n",