...

Source file src/pkg/html/template/content.go

     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 https://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		// Srcset encapsulates a known safe srcset attribute
    88		// (see https://w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset).
    89		//
    90		// Use of this type presents a security risk:
    91		// the encapsulated content should come from a trusted source,
    92		// as it will be included verbatim in the template output.
    93		Srcset string
    94	)
    95	
    96	type contentType uint8
    97	
    98	const (
    99		contentTypePlain contentType = iota
   100		contentTypeCSS
   101		contentTypeHTML
   102		contentTypeHTMLAttr
   103		contentTypeJS
   104		contentTypeJSStr
   105		contentTypeURL
   106		contentTypeSrcset
   107		// contentTypeUnsafe is used in attr.go for values that affect how
   108		// embedded content and network messages are formed, vetted,
   109		// or interpreted; or which credentials network messages carry.
   110		contentTypeUnsafe
   111	)
   112	
   113	// indirect returns the value, after dereferencing as many times
   114	// as necessary to reach the base type (or nil).
   115	func indirect(a interface{}) interface{} {
   116		if a == nil {
   117			return nil
   118		}
   119		if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
   120			// Avoid creating a reflect.Value if it's not a pointer.
   121			return a
   122		}
   123		v := reflect.ValueOf(a)
   124		for v.Kind() == reflect.Ptr && !v.IsNil() {
   125			v = v.Elem()
   126		}
   127		return v.Interface()
   128	}
   129	
   130	var (
   131		errorType       = reflect.TypeOf((*error)(nil)).Elem()
   132		fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
   133	)
   134	
   135	// indirectToStringerOrError returns the value, after dereferencing as many times
   136	// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
   137	// or error,
   138	func indirectToStringerOrError(a interface{}) interface{} {
   139		if a == nil {
   140			return nil
   141		}
   142		v := reflect.ValueOf(a)
   143		for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
   144			v = v.Elem()
   145		}
   146		return v.Interface()
   147	}
   148	
   149	// stringify converts its arguments to a string and the type of the content.
   150	// All pointers are dereferenced, as in the text/template package.
   151	func stringify(args ...interface{}) (string, contentType) {
   152		if len(args) == 1 {
   153			switch s := indirect(args[0]).(type) {
   154			case string:
   155				return s, contentTypePlain
   156			case CSS:
   157				return string(s), contentTypeCSS
   158			case HTML:
   159				return string(s), contentTypeHTML
   160			case HTMLAttr:
   161				return string(s), contentTypeHTMLAttr
   162			case JS:
   163				return string(s), contentTypeJS
   164			case JSStr:
   165				return string(s), contentTypeJSStr
   166			case URL:
   167				return string(s), contentTypeURL
   168			case Srcset:
   169				return string(s), contentTypeSrcset
   170			}
   171		}
   172		i := 0
   173		for _, arg := range args {
   174			// We skip untyped nil arguments for backward compatibility.
   175			// Without this they would be output as <nil>, escaped.
   176			// See issue 25875.
   177			if arg == nil {
   178				continue
   179			}
   180	
   181			args[i] = indirectToStringerOrError(arg)
   182			i++
   183		}
   184		return fmt.Sprint(args[:i]...), contentTypePlain
   185	}
   186	

View as plain text