// 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 https://www.w3.org/TR/css3-syntax/#parsing and // https://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style // // Use of this type presents a security risk: // the encapsulated content should come from a trusted source, // as it will be included verbatim in the template output. 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. // // Use of this type presents a security risk: // the encapsulated content should come from a trusted source, // as it will be included verbatim in the template output. HTML string // HTMLAttr encapsulates an HTML attribute from a trusted source, // for example, ` dir="ltr"`. // // Use of this type presents a security risk: // the encapsulated content should come from a trusted source, // as it will be included verbatim in the template output. 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. // // Use of this type presents a security risk: // the encapsulated content should come from a trusted source, // as it will be included verbatim in the template output. // // Using JS to include valid but untrusted JSON is not safe. // A safe alternative is to parse the JSON with json.Unmarshal and then // pass the resultant object into the template, where it will be // converted to sanitized JSON when presented in a JavaScript context. 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. // // Use of this type presents a security risk: // the encapsulated content should come from a trusted source, // as it will be included verbatim in the template output. 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. // // Use of this type presents a security risk: // the encapsulated content should come from a trusted source, // as it will be included verbatim in the template output. URL string // Srcset encapsulates a known safe srcset attribute // (see https://w3c.github.io/html/semantics-embedded-content.html#element-attrdef-img-srcset). // // Use of this type presents a security risk: // the encapsulated content should come from a trusted source, // as it will be included verbatim in the template output. Srcset string ) type contentType uint8 const ( contentTypePlain contentType = iota contentTypeCSS contentTypeHTML contentTypeHTMLAttr contentTypeJS contentTypeJSStr contentTypeURL contentTypeSrcset // 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 any) any { if a == nil { return nil } if t := reflect.TypeOf(a); t.Kind() != reflect.Pointer { // Avoid creating a reflect.Value if it's not a pointer. return a } v := reflect.ValueOf(a) for v.Kind() == reflect.Pointer && !v.IsNil() { v = v.Elem() } return v.Interface() } var ( errorType = reflect.TypeFor[error]() fmtStringerType = reflect.TypeFor[fmt.Stringer]() ) // 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 any) any { if a == nil { return nil } v := reflect.ValueOf(a) for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Pointer && !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 ...any) (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 case Srcset: return string(s), contentTypeSrcset } } i := 0 for _, arg := range args { // We skip untyped nil arguments for backward compatibility. // Without this they would be output as , escaped. // See issue 25875. if arg == nil { continue } args[i] = indirectToStringerOrError(arg) i++ } return fmt.Sprint(args[:i]...), contentTypePlain }