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	"strings"
9)
10
11// attrTypeMap[n] describes the value of the given attribute.
12// If an attribute affects (or can mask) the encoding or interpretation of
13// other content, or affects the contents, idempotency, or credentials of a
14// network message, then the value in this map is contentTypeUnsafe.
15// This map is derived from HTML5, specifically
16// https://www.w3.org/TR/html5/Overview.html#attributes-1
17// as well as "%URI"-typed attributes from
18// https://www.w3.org/TR/html4/index/attributes.html
19var attrTypeMap = map[string]contentType{
20	"accept":          contentTypePlain,
21	"accept-charset":  contentTypeUnsafe,
22	"action":          contentTypeURL,
23	"alt":             contentTypePlain,
24	"archive":         contentTypeURL,
25	"async":           contentTypeUnsafe,
26	"autocomplete":    contentTypePlain,
27	"autofocus":       contentTypePlain,
28	"autoplay":        contentTypePlain,
29	"background":      contentTypeURL,
30	"border":          contentTypePlain,
31	"checked":         contentTypePlain,
32	"cite":            contentTypeURL,
33	"challenge":       contentTypeUnsafe,
34	"charset":         contentTypeUnsafe,
35	"class":           contentTypePlain,
36	"classid":         contentTypeURL,
37	"codebase":        contentTypeURL,
38	"cols":            contentTypePlain,
39	"colspan":         contentTypePlain,
40	"content":         contentTypeUnsafe,
41	"contenteditable": contentTypePlain,
42	"contextmenu":     contentTypePlain,
43	"controls":        contentTypePlain,
44	"coords":          contentTypePlain,
45	"crossorigin":     contentTypeUnsafe,
46	"data":            contentTypeURL,
47	"datetime":        contentTypePlain,
48	"default":         contentTypePlain,
49	"defer":           contentTypeUnsafe,
50	"dir":             contentTypePlain,
51	"dirname":         contentTypePlain,
52	"disabled":        contentTypePlain,
53	"draggable":       contentTypePlain,
54	"dropzone":        contentTypePlain,
55	"enctype":         contentTypeUnsafe,
56	"for":             contentTypePlain,
57	"form":            contentTypeUnsafe,
58	"formaction":      contentTypeURL,
59	"formenctype":     contentTypeUnsafe,
60	"formmethod":      contentTypeUnsafe,
61	"formnovalidate":  contentTypeUnsafe,
62	"formtarget":      contentTypePlain,
63	"headers":         contentTypePlain,
64	"height":          contentTypePlain,
65	"hidden":          contentTypePlain,
66	"high":            contentTypePlain,
67	"href":            contentTypeURL,
68	"hreflang":        contentTypePlain,
69	"http-equiv":      contentTypeUnsafe,
70	"icon":            contentTypeURL,
71	"id":              contentTypePlain,
72	"ismap":           contentTypePlain,
73	"keytype":         contentTypeUnsafe,
74	"kind":            contentTypePlain,
75	"label":           contentTypePlain,
76	"lang":            contentTypePlain,
77	"language":        contentTypeUnsafe,
78	"list":            contentTypePlain,
79	"longdesc":        contentTypeURL,
80	"loop":            contentTypePlain,
81	"low":             contentTypePlain,
82	"manifest":        contentTypeURL,
83	"max":             contentTypePlain,
84	"maxlength":       contentTypePlain,
85	"media":           contentTypePlain,
86	"mediagroup":      contentTypePlain,
87	"method":          contentTypeUnsafe,
88	"min":             contentTypePlain,
89	"multiple":        contentTypePlain,
90	"name":            contentTypePlain,
91	"novalidate":      contentTypeUnsafe,
92	// Skip handler names from
93	// https://www.w3.org/TR/html5/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects
94	// since we have special handling in attrType.
95	"open":        contentTypePlain,
96	"optimum":     contentTypePlain,
97	"pattern":     contentTypeUnsafe,
98	"placeholder": contentTypePlain,
99	"poster":      contentTypeURL,
100	"profile":     contentTypeURL,
101	"preload":     contentTypePlain,
102	"pubdate":     contentTypePlain,
103	"radiogroup":  contentTypePlain,
104	"readonly":    contentTypePlain,
105	"rel":         contentTypeUnsafe,
106	"required":    contentTypePlain,
107	"reversed":    contentTypePlain,
108	"rows":        contentTypePlain,
109	"rowspan":     contentTypePlain,
110	"sandbox":     contentTypeUnsafe,
111	"spellcheck":  contentTypePlain,
112	"scope":       contentTypePlain,
113	"scoped":      contentTypePlain,
114	"seamless":    contentTypePlain,
115	"selected":    contentTypePlain,
116	"shape":       contentTypePlain,
117	"size":        contentTypePlain,
118	"sizes":       contentTypePlain,
119	"span":        contentTypePlain,
120	"src":         contentTypeURL,
121	"srcdoc":      contentTypeHTML,
122	"srclang":     contentTypePlain,
123	"srcset":      contentTypeSrcset,
124	"start":       contentTypePlain,
125	"step":        contentTypePlain,
126	"style":       contentTypeCSS,
127	"tabindex":    contentTypePlain,
128	"target":      contentTypePlain,
129	"title":       contentTypePlain,
130	"type":        contentTypeUnsafe,
131	"usemap":      contentTypeURL,
132	"value":       contentTypeUnsafe,
133	"width":       contentTypePlain,
134	"wrap":        contentTypePlain,
135	"xmlns":       contentTypeURL,
136}
137
138// attrType returns a conservative (upper-bound on authority) guess at the
139// type of the lowercase named attribute.
140func attrType(name string) contentType {
141	if strings.HasPrefix(name, "data-") {
142		// Strip data- so that custom attribute heuristics below are
143		// widely applied.
144		// Treat data-action as URL below.
145		name = name[5:]
146	} else if colon := strings.IndexRune(name, ':'); colon != -1 {
147		if name[:colon] == "xmlns" {
148			return contentTypeURL
149		}
150		// Treat svg:href and xlink:href as href below.
151		name = name[colon+1:]
152	}
153	if t, ok := attrTypeMap[name]; ok {
154		return t
155	}
156	// Treat partial event handler names as script.
157	if strings.HasPrefix(name, "on") {
158		return contentTypeJS
159	}
160
161	// Heuristics to prevent "javascript:..." injection in custom
162	// data attributes and custom attributes like g:tweetUrl.
163	// https://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
164	// "Custom data attributes are intended to store custom data
165	//  private to the page or application, for which there are no
166	//  more appropriate attributes or elements."
167	// Developers seem to store URL content in data URLs that start
168	// or end with "URI" or "URL".
169	if strings.Contains(name, "src") ||
170		strings.Contains(name, "uri") ||
171		strings.Contains(name, "url") {
172		return contentTypeURL
173	}
174	return contentTypePlain
175}
176