// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package template import ( "fmt" "reflect" ) // Strings of content from a trusted source. type ( // CSS encapsulates known safe content that matches any of: // 1. The CSS3 stylesheet production, such as `p { color: purple }`. // 2. The CSS3 rule production, such as `a[href=~"https:"].foo#bar`. // 3. CSS3 declaration productions, such as `color: red; margin: 2px`. // 4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`. // See http://www.w3.org/TR/css3-syntax/#style CSS string // HTML encapsulates a known safe HTML document fragment. // It should not be used for HTML from a third-party, or HTML with // unclosed tags or comments. The outputs of a sound HTML sanitizer // and a template escaped by this package are fine for use with HTML. HTML string // HTMLAttr encapsulates an HTML attribute from a trusted source, // for example, ` dir="ltr"`. HTMLAttr string // JS encapsulates a known safe EcmaScript5 Expression, for example, // `(x + y * z())`. // Template authors are responsible for ensuring that typed expressions // do not break the intended precedence and that there is no // statement/expression ambiguity as when passing an expression like // "{ foo: bar() }\n['foo']()", which is both a valid Expression and a // valid Program with a very different meaning. JS string // JSStr encapsulates a sequence of characters meant to be embedded // between quotes in a JavaScript expression. // The string must match a series of StringCharacters: // StringCharacter :: SourceCharacter but not `\` or LineTerminator // | EscapeSequence // Note that LineContinuations are not allowed. // JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not. JSStr string // URL encapsulates a known safe URL or URL substring (see RFC 3986). // A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()` // from a trusted source should go in the page, but by default dynamic // `javascript:` URLs are filtered out since they are a frequently // exploited injection vector. URL string ) type contentType uint8 const ( contentTypePlain contentType = iota contentTypeCSS contentTypeHTML contentTypeHTMLAttr contentTypeJS contentTypeJSStr contentTypeURL // contentTypeUnsafe is used in attr.go for values that affect how // embedded content and network messages are formed, vetted, // or interpreted; or which credentials network messages carry. contentTypeUnsafe ) // indirect returns the value, after dereferencing as many times // as necessary to reach the base type (or nil). func indirect(a interface{}) interface{} { if a == nil { return nil } if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr { // Avoid creating a reflect.Value if it's not a pointer. return a } v := reflect.ValueOf(a) for v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() } return v.Interface() } var ( errorType = reflect.TypeOf((*error)(nil)).Elem() fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() ) // indirectToStringerOrError returns the value, after dereferencing as many times // as necessary to reach the base type (or nil) or an implementation of fmt.Stringer // or error, func indirectToStringerOrError(a interface{}) interface{} { if a == nil { return nil } v := reflect.ValueOf(a) for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() } return v.Interface() } // stringify converts its arguments to a string and the type of the content. // All pointers are dereferenced, as in the text/template package. func stringify(args ...interface{}) (string, contentType) { if len(args) == 1 { switch s := indirect(args[0]).(type) { case string: return s, contentTypePlain case CSS: return string(s), contentTypeCSS case HTML: return string(s), contentTypeHTML case HTMLAttr: return string(s), contentTypeHTMLAttr case JS: return string(s), contentTypeJS case JSStr: return string(s), contentTypeJSStr case URL: return string(s), contentTypeURL } } for i, arg := range args { args[i] = indirectToStringerOrError(arg) } return fmt.Sprint(args...), contentTypePlain }