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 5package template 6 7import ( 8 "fmt" 9 "reflect" 10) 11 12// Strings of content from a trusted source. 13type ( 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 59type contentType uint8 60 61const ( 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). 77func 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 92var ( 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, 100func 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. 113func 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