Home | History | Annotate | Download | only in template
      1 // Copyright 2011 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package template
      6 
      7 import (
      8 	"fmt"
      9 	"reflect"
     10 )
     11 
     12 // Strings of content from a trusted source.
     13 type (
     14 	// CSS encapsulates known safe content that matches any of:
     15 	//   1. The CSS3 stylesheet production, such as `p { color: purple }`.
     16 	//   2. The CSS3 rule production, such as `a[href=~"https:"].foo#bar`.
     17 	//   3. CSS3 declaration productions, such as `color: red; margin: 2px`.
     18 	//   4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`.
     19 	// See http://www.w3.org/TR/css3-syntax/#parsing and
     20 	// https://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style
     21 	//
     22 	// Use of this type presents a security risk:
     23 	// the encapsulated content should come from a trusted source,
     24 	// as it will be included verbatim in the template output.
     25 	CSS string
     26 
     27 	// HTML encapsulates a known safe HTML document fragment.
     28 	// It should not be used for HTML from a third-party, or HTML with
     29 	// unclosed tags or comments. The outputs of a sound HTML sanitizer
     30 	// and a template escaped by this package are fine for use with HTML.
     31 	//
     32 	// Use of this type presents a security risk:
     33 	// the encapsulated content should come from a trusted source,
     34 	// as it will be included verbatim in the template output.
     35 	HTML string
     36 
     37 	// HTMLAttr encapsulates an HTML attribute from a trusted source,
     38 	// for example, ` dir="ltr"`.
     39 	//
     40 	// Use of this type presents a security risk:
     41 	// the encapsulated content should come from a trusted source,
     42 	// as it will be included verbatim in the template output.
     43 	HTMLAttr string
     44 
     45 	// JS encapsulates a known safe EcmaScript5 Expression, for example,
     46 	// `(x + y * z())`.
     47 	// Template authors are responsible for ensuring that typed expressions
     48 	// do not break the intended precedence and that there is no
     49 	// statement/expression ambiguity as when passing an expression like
     50 	// "{ foo: bar() }\n['foo']()", which is both a valid Expression and a
     51 	// valid Program with a very different meaning.
     52 	//
     53 	// Use of this type presents a security risk:
     54 	// the encapsulated content should come from a trusted source,
     55 	// as it will be included verbatim in the template output.
     56 	//
     57 	// Using JS to include valid but untrusted JSON is not safe.
     58 	// A safe alternative is to parse the JSON with json.Unmarshal and then
     59 	// pass the resultant object into the template, where it will be
     60 	// converted to sanitized JSON when presented in a JavaScript context.
     61 	JS string
     62 
     63 	// JSStr encapsulates a sequence of characters meant to be embedded
     64 	// between quotes in a JavaScript expression.
     65 	// The string must match a series of StringCharacters:
     66 	//   StringCharacter :: SourceCharacter but not `\` or LineTerminator
     67 	//                    | EscapeSequence
     68 	// Note that LineContinuations are not allowed.
     69 	// JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not.
     70 	//
     71 	// Use of this type presents a security risk:
     72 	// the encapsulated content should come from a trusted source,
     73 	// as it will be included verbatim in the template output.
     74 	JSStr string
     75 
     76 	// URL encapsulates a known safe URL or URL substring (see RFC 3986).
     77 	// A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()`
     78 	// from a trusted source should go in the page, but by default dynamic
     79 	// `javascript:` URLs are filtered out since they are a frequently
     80 	// exploited injection vector.
     81 	//
     82 	// Use of this type presents a security risk:
     83 	// the encapsulated content should come from a trusted source,
     84 	// as it will be included verbatim in the template output.
     85 	URL string
     86 )
     87 
     88 type contentType uint8
     89 
     90 const (
     91 	contentTypePlain contentType = iota
     92 	contentTypeCSS
     93 	contentTypeHTML
     94 	contentTypeHTMLAttr
     95 	contentTypeJS
     96 	contentTypeJSStr
     97 	contentTypeURL
     98 	// contentTypeUnsafe is used in attr.go for values that affect how
     99 	// embedded content and network messages are formed, vetted,
    100 	// or interpreted; or which credentials network messages carry.
    101 	contentTypeUnsafe
    102 )
    103 
    104 // indirect returns the value, after dereferencing as many times
    105 // as necessary to reach the base type (or nil).
    106 func indirect(a interface{}) interface{} {
    107 	if a == nil {
    108 		return nil
    109 	}
    110 	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
    111 		// Avoid creating a reflect.Value if it's not a pointer.
    112 		return a
    113 	}
    114 	v := reflect.ValueOf(a)
    115 	for v.Kind() == reflect.Ptr && !v.IsNil() {
    116 		v = v.Elem()
    117 	}
    118 	return v.Interface()
    119 }
    120 
    121 var (
    122 	errorType       = reflect.TypeOf((*error)(nil)).Elem()
    123 	fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
    124 )
    125 
    126 // indirectToStringerOrError returns the value, after dereferencing as many times
    127 // as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
    128 // or error,
    129 func indirectToStringerOrError(a interface{}) interface{} {
    130 	if a == nil {
    131 		return nil
    132 	}
    133 	v := reflect.ValueOf(a)
    134 	for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
    135 		v = v.Elem()
    136 	}
    137 	return v.Interface()
    138 }
    139 
    140 // stringify converts its arguments to a string and the type of the content.
    141 // All pointers are dereferenced, as in the text/template package.
    142 func stringify(args ...interface{}) (string, contentType) {
    143 	if len(args) == 1 {
    144 		switch s := indirect(args[0]).(type) {
    145 		case string:
    146 			return s, contentTypePlain
    147 		case CSS:
    148 			return string(s), contentTypeCSS
    149 		case HTML:
    150 			return string(s), contentTypeHTML
    151 		case HTMLAttr:
    152 			return string(s), contentTypeHTMLAttr
    153 		case JS:
    154 			return string(s), contentTypeJS
    155 		case JSStr:
    156 			return string(s), contentTypeJSStr
    157 		case URL:
    158 			return string(s), contentTypeURL
    159 		}
    160 	}
    161 	for i, arg := range args {
    162 		args[i] = indirectToStringerOrError(arg)
    163 	}
    164 	return fmt.Sprint(args...), contentTypePlain
    165 }
    166