Skip to content

Commit

Permalink
templates: Use text/template; add experimental notice to docs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mholt committed Apr 6, 2020
1 parent 145aebb commit 437d509
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 13 deletions.
2 changes: 2 additions & 0 deletions modules/caddyhttp/templates/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
20 changes: 9 additions & 11 deletions modules/caddyhttp/templates/tplcontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ package templates
import (
"bytes"
"fmt"
"html/template"
"io"
"net"
"net/http"
"path"
"strconv"
"strings"
"sync"
"text/template"

"github.com/Masterminds/sprig/v3"
"github.com/alecthomas/chroma/formatters/html"
Expand Down Expand Up @@ -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")
}
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}
}

Expand All @@ -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"
3 changes: 1 addition & 2 deletions modules/caddyhttp/templates/tplcontext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ package templates
import (
"bytes"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"os"
Expand All @@ -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",
Expand Down

0 comments on commit 437d509

Please sign in to comment.