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