Use html/template for escaping by default

Allow HTML only with a few specific functions
This commit is contained in:
Matthew Holt 2019-06-27 13:30:41 -06:00
parent a63cb3e3fd
commit 006dc1792f
2 changed files with 17 additions and 11 deletions

View file

@ -3,13 +3,13 @@ package templates
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"html/template"
"io" "io"
"net" "net"
"net/http" "net/http"
"path" "path"
"strings" "strings"
"sync" "sync"
"text/template"
"github.com/Masterminds/sprig" "github.com/Masterminds/sprig"
"github.com/caddyserver/caddy/modules/caddyhttp" "github.com/caddyserver/caddy/modules/caddyhttp"
@ -27,7 +27,10 @@ type templateContext struct {
} }
// Include returns the contents of filename relative to the site root. // Include returns the contents of filename relative to the site root.
func (c templateContext) Include(filename string, args ...interface{}) (string, error) { // 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) Include(filename string, args ...interface{}) (template.HTML, error) {
if c.Root == nil { if c.Root == nil {
return "", fmt.Errorf("root file system not specified") return "", fmt.Errorf("root file system not specified")
} }
@ -54,12 +57,14 @@ func (c templateContext) Include(filename string, args ...interface{}) (string,
return "", err return "", err
} }
return bodyBuf.String(), nil return template.HTML(bodyBuf.String()), nil
} }
// HTTPInclude returns the body of a virtual (lightweight) request // HTTPInclude returns the body of a virtual (lightweight) request
// to the given URI on the same server. // to the given URI on the same server. Note that included bodies
func (c templateContext) HTTPInclude(uri string) (string, error) { // 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) HTTPInclude(uri string) (template.HTML, error) {
if c.Req.Header.Get(recursionPreventionHeader) == "1" { if c.Req.Header.Get(recursionPreventionHeader) == "1" {
return "", fmt.Errorf("virtual include cycle") return "", fmt.Errorf("virtual include cycle")
} }
@ -87,11 +92,11 @@ func (c templateContext) HTTPInclude(uri string) (string, error) {
return "", err return "", err
} }
return buf.String(), nil return template.HTML(buf.String()), nil
} }
func (c templateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error { func (c templateContext) executeTemplateInBuffer(tplName string, buf *bytes.Buffer) error {
tpl := template.New(tplName).Funcs(sprig.TxtFuncMap()) tpl := template.New(tplName).Funcs(sprig.FuncMap())
if len(c.config.Delimiters) == 2 { if len(c.config.Delimiters) == 2 {
tpl.Delims(c.config.Delimiters[0], c.config.Delimiters[1]) tpl.Delims(c.config.Delimiters[0], c.config.Delimiters[1])
} }
@ -186,9 +191,10 @@ func (c templateContext) StripHTML(s string) string {
return buf.String() return buf.String()
} }
// Markdown renders the markdown body as HTML. // Markdown renders the markdown body as HTML. The resulting
func (c templateContext) Markdown(body string) string { // HTML is NOT escaped so that it can be rendered as HTML.
return string(blackfriday.Run([]byte(body))) func (c templateContext) Markdown(body string) template.HTML {
return template.HTML(blackfriday.Run([]byte(body)))
} }
// ListFiles reads and returns a slice of names from the given // ListFiles reads and returns a slice of names from the given

View file

@ -40,7 +40,7 @@ func TestMarkdown(t *testing.T) {
expect: "<ul>\n<li>str1</li>\n<li>str2</li>\n</ul>\n", expect: "<ul>\n<li>str1</li>\n<li>str2</li>\n</ul>\n",
}, },
} { } {
result := context.Markdown(test.body) result := string(context.Markdown(test.body))
if result != test.expect { if result != test.expect {
t.Errorf("Test %d: expected '%s' but got '%s'", i, test.expect, result) t.Errorf("Test %d: expected '%s' but got '%s'", i, test.expect, result)
} }