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 http://www.w3.org/TR/css3-syntax/#style
20 CSS string
21
22 // HTML encapsulates a known safe HTML document fragment.
23 // It should not be used for HTML from a third-party, or HTML with
24 // unclosed tags or comments. The outputs of a sound HTML sanitizer
25 // and a template escaped by this package are fine for use with HTML.
26 HTML string
27
28 // HTMLAttr encapsulates an HTML attribute from a trusted source,
29 // for example, ` dir="ltr"`.
30 HTMLAttr string
31
32 // JS encapsulates a known safe EcmaScript5 Expression, for example,
33 // `(x + y * z())`.
34 // Template authors are responsible for ensuring that typed expressions
35 // do not break the intended precedence and that there is no
36 // statement/expression ambiguity as when passing an expression like
37 // "{ foo: bar() }\n['foo']()", which is both a valid Expression and a
38 // valid Program with a very different meaning.
39 JS string
40
41 // JSStr encapsulates a sequence of characters meant to be embedded
42 // between quotes in a JavaScript expression.
43 // The string must match a series of StringCharacters:
44 // StringCharacter :: SourceCharacter but not `\` or LineTerminator
45 // | EscapeSequence
46 // Note that LineContinuations are not allowed.
47 // JSStr("foo\\nbar") is fine, but JSStr("foo\\\nbar") is not.
48 JSStr string
49
50 // URL encapsulates a known safe URL as defined in RFC 3896.
51 // A URL like `javascript:checkThatFormNotEditedBeforeLeavingPage()`
52 // from a trusted source should go in the page, but by default dynamic
53 // `javascript:` URLs are filtered out since they are a frequently
54 // exploited injection vector.
55 URL string
56 )
57
58 type contentType uint8
59
60 const (
61 contentTypePlain contentType = iota
62 contentTypeCSS
63 contentTypeHTML
64 contentTypeHTMLAttr
65 contentTypeJS
66 contentTypeJSStr
67 contentTypeURL
68 // contentTypeUnsafe is used in attr.go for values that affect how
69 // embedded content and network messages are formed, vetted,
70 // or interpreted; or which credentials network messages carry.
71 contentTypeUnsafe
72 )
73
74 // indirect returns the value, after dereferencing as many times
75 // as necessary to reach the base type (or nil).
76 func indirect(a interface{}) interface{} {
77 if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
78 // Avoid creating a reflect.Value if it's not a pointer.
79 return a
80 }
81 v := reflect.ValueOf(a)
82 for v.Kind() == reflect.Ptr && !v.IsNil() {
83 v = v.Elem()
84 }
85 return v.Interface()
86 }
87
88 var (
89 errorType = reflect.TypeOf((*error)(nil)).Elem()
90 fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
91 )
92
93 // indirectToStringerOrError returns the value, after dereferencing as many times
94 // as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
95 // or error,
96 func indirectToStringerOrError(a interface{}) interface{} {
97 v := reflect.ValueOf(a)
98 for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
99 v = v.Elem()
100 }
101 return v.Interface()
102 }
103
104 // stringify converts its arguments to a string and the type of the content.
105 // All pointers are dereferenced, as in the text/template package.
106 func stringify(args ...interface{}) (string, contentType) {
107 if len(args) == 1 {
108 switch s := indirect(args[0]).(type) {
109 case string:
110 return s, contentTypePlain
111 case CSS:
112 return string(s), contentTypeCSS
113 case HTML:
114 return string(s), contentTypeHTML
115 case HTMLAttr:
116 return string(s), contentTypeHTMLAttr
117 case JS:
118 return string(s), contentTypeJS
119 case JSStr:
120 return string(s), contentTypeJSStr
121 case URL:
122 return string(s), contentTypeURL
123 }
124 }
125 for i, arg := range args {
126 args[i] = indirectToStringerOrError(arg)
127 }
128 return fmt.Sprint(args...), contentTypePlain
129 }