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 	CSS string
     22 
     23 	// HTML encapsulates a known safe HTML document fragment.
     24 	// It should not be used for HTML from a third-party, or HTML with
     25 	// unclosed tags or comments. The outputs of a sound HTML sanitizer
     26 	// and a template escaped by this package are fine for use with HTML.
     27 	HTML string
     28 
     29 	// HTMLAttr encapsulates an HTML attribute from a trusted source,
     30 	// for example, ` dir="ltr"`.
     31 	HTMLAttr string
     32 
     33 	// JS encapsulates a known safe EcmaScript5 Expression, for example,
     34 	// `(x + y * z())`.
     35 	// Template authors are responsible for ensuring that typed expressions
     36 	// do not break the intended precedence and that there is no
     37 	// statement/expression ambiguity as when passing an expression like
     38 	// "{ foo: bar() }\n['foo']()", which is both a valid Expression and a
     39 	// valid Program with a very different meaning.
     40 	JS string
     41 
     42 	// JSStr encapsulates a sequence of characters meant to be embedded
     43 	// between quotes in a JavaScript expression.
     44 	// The string must match a series of StringCharacters:
     45 	//   StringCharacter :: SourceCharacter but not `\` or LineTerminator
     46 	//                    | EscapeSequence
     47 	// Note that LineContinuations are not allowed.
     48 	// JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not.
     49 	JSStr string
     50 
     51 	// URL encapsulates a known safe URL or URL substring (see RFC 3986).
     52 	// A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()`
     53 	// from a trusted source should go in the page, but by default dynamic
     54 	// `javascript:` URLs are filtered out since they are a frequently
     55 	// exploited injection vector.
     56 	URL string
     57 )
     58 
     59 type contentType uint8
     60 
     61 const (
     62 	contentTypePlain contentType = iota
     63 	contentTypeCSS
     64 	contentTypeHTML
     65 	contentTypeHTMLAttr
     66 	contentTypeJS
     67 	contentTypeJSStr
     68 	contentTypeURL
     69 	// contentTypeUnsafe is used in attr.go for values that affect how
     70 	// embedded content and network messages are formed, vetted,
     71 	// or interpreted; or which credentials network messages carry.
     72 	contentTypeUnsafe
     73 )
     74 
     75 // indirect returns the value, after dereferencing as many times
     76 // as necessary to reach the base type (or nil).
     77 func indirect(a interface{}) interface{} {
     78 	if a == nil {
     79 		return nil
     80 	}
     81 	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
     82 		// Avoid creating a reflect.Value if it's not a pointer.
     83 		return a
     84 	}
     85 	v := reflect.ValueOf(a)
     86 	for v.Kind() == reflect.Ptr && !v.IsNil() {
     87 		v = v.Elem()
     88 	}
     89 	return v.Interface()
     90 }
     91 
     92 var (
     93 	errorType       = reflect.TypeOf((*error)(nil)).Elem()
     94 	fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
     95 )
     96 
     97 // indirectToStringerOrError returns the value, after dereferencing as many times
     98 // as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
     99 // or error,
    100 func indirectToStringerOrError(a interface{}) interface{} {
    101 	if a == nil {
    102 		return nil
    103 	}
    104 	v := reflect.ValueOf(a)
    105 	for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
    106 		v = v.Elem()
    107 	}
    108 	return v.Interface()
    109 }
    110 
    111 // stringify converts its arguments to a string and the type of the content.
    112 // All pointers are dereferenced, as in the text/template package.
    113 func stringify(args ...interface{}) (string, contentType) {
    114 	if len(args) == 1 {
    115 		switch s := indirect(args[0]).(type) {
    116 		case string:
    117 			return s, contentTypePlain
    118 		case CSS:
    119 			return string(s), contentTypeCSS
    120 		case HTML:
    121 			return string(s), contentTypeHTML
    122 		case HTMLAttr:
    123 			return string(s), contentTypeHTMLAttr
    124 		case JS:
    125 			return string(s), contentTypeJS
    126 		case JSStr:
    127 			return string(s), contentTypeJSStr
    128 		case URL:
    129 			return string(s), contentTypeURL
    130 		}
    131 	}
    132 	for i, arg := range args {
    133 		args[i] = indirectToStringerOrError(arg)
    134 	}
    135 	return fmt.Sprint(args...), contentTypePlain
    136 }
    137