| // 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 ( | |
| "strings" | |
| ) | |
| // attrTypeMap[n] describes the value of the given attribute. | |
| // If an attribute affects (or can mask) the encoding or interpretation of | |
| // other content, or affects the contents, idempotency, or credentials of a | |
| // network message, then the value in this map is contentTypeUnsafe. | |
| // This map is derived from HTML5, specifically | |
| // http://www.w3.org/TR/html5/Overview.html#attributes-1 | |
| // as well as "%URI"-typed attributes from | |
| // http://www.w3.org/TR/html4/index/attributes.html | |
| var attrTypeMap = map[string]contentType{ | |
| "accept": contentTypePlain, | |
| "accept-charset": contentTypeUnsafe, | |
| "action": contentTypeURL, | |
| "alt": contentTypePlain, | |
| "archive": contentTypeURL, | |
| "async": contentTypeUnsafe, | |
| "autocomplete": contentTypePlain, | |
| "autofocus": contentTypePlain, | |
| "autoplay": contentTypePlain, | |
| "background": contentTypeURL, | |
| "border": contentTypePlain, | |
| "checked": contentTypePlain, | |
| "cite": contentTypeURL, | |
| "challenge": contentTypeUnsafe, | |
| "charset": contentTypeUnsafe, | |
| "class": contentTypePlain, | |
| "classid": contentTypeURL, | |
| "codebase": contentTypeURL, | |
| "cols": contentTypePlain, | |
| "colspan": contentTypePlain, | |
| "content": contentTypeUnsafe, | |
| "contenteditable": contentTypePlain, | |
| "contextmenu": contentTypePlain, | |
| "controls": contentTypePlain, | |
| "coords": contentTypePlain, | |
| "crossorigin": contentTypeUnsafe, | |
| "data": contentTypeURL, | |
| "datetime": contentTypePlain, | |
| "default": contentTypePlain, | |
| "defer": contentTypeUnsafe, | |
| "dir": contentTypePlain, | |
| "dirname": contentTypePlain, | |
| "disabled": contentTypePlain, | |
| "draggable": contentTypePlain, | |
| "dropzone": contentTypePlain, | |
| "enctype": contentTypeUnsafe, | |
| "for": contentTypePlain, | |
| "form": contentTypeUnsafe, | |
| "formaction": contentTypeURL, | |
| "formenctype": contentTypeUnsafe, | |
| "formmethod": contentTypeUnsafe, | |
| "formnovalidate": contentTypeUnsafe, | |
| "formtarget": contentTypePlain, | |
| "headers": contentTypePlain, | |
| "height": contentTypePlain, | |
| "hidden": contentTypePlain, | |
| "high": contentTypePlain, | |
| "href": contentTypeURL, | |
| "hreflang": contentTypePlain, | |
| "http-equiv": contentTypeUnsafe, | |
| "icon": contentTypeURL, | |
| "id": contentTypePlain, | |
| "ismap": contentTypePlain, | |
| "keytype": contentTypeUnsafe, | |
| "kind": contentTypePlain, | |
| "label": contentTypePlain, | |
| "lang": contentTypePlain, | |
| "language": contentTypeUnsafe, | |
| "list": contentTypePlain, | |
| "longdesc": contentTypeURL, | |
| "loop": contentTypePlain, | |
| "low": contentTypePlain, | |
| "manifest": contentTypeURL, | |
| "max": contentTypePlain, | |
| "maxlength": contentTypePlain, | |
| "media": contentTypePlain, | |
| "mediagroup": contentTypePlain, | |
| "method": contentTypeUnsafe, | |
| "min": contentTypePlain, | |
| "multiple": contentTypePlain, | |
| "name": contentTypePlain, | |
| "novalidate": contentTypeUnsafe, | |
| // Skip handler names from | |
| // http://www.w3.org/TR/html5/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects | |
| // since we have special handling in attrType. | |
| "open": contentTypePlain, | |
| "optimum": contentTypePlain, | |
| "pattern": contentTypeUnsafe, | |
| "placeholder": contentTypePlain, | |
| "poster": contentTypeURL, | |
| "profile": contentTypeURL, | |
| "preload": contentTypePlain, | |
| "pubdate": contentTypePlain, | |
| "radiogroup": contentTypePlain, | |
| "readonly": contentTypePlain, | |
| "rel": contentTypeUnsafe, | |
| "required": contentTypePlain, | |
| "reversed": contentTypePlain, | |
| "rows": contentTypePlain, | |
| "rowspan": contentTypePlain, | |
| "sandbox": contentTypeUnsafe, | |
| "spellcheck": contentTypePlain, | |
| "scope": contentTypePlain, | |
| "scoped": contentTypePlain, | |
| "seamless": contentTypePlain, | |
| "selected": contentTypePlain, | |
| "shape": contentTypePlain, | |
| "size": contentTypePlain, | |
| "sizes": contentTypePlain, | |
| "span": contentTypePlain, | |
| "src": contentTypeURL, | |
| "srcdoc": contentTypeHTML, | |
| "srclang": contentTypePlain, | |
| "srcset": contentTypeSrcset, | |
| "start": contentTypePlain, | |
| "step": contentTypePlain, | |
| "style": contentTypeCSS, | |
| "tabindex": contentTypePlain, | |
| "target": contentTypePlain, | |
| "title": contentTypePlain, | |
| "type": contentTypeUnsafe, | |
| "usemap": contentTypeURL, | |
| "value": contentTypeUnsafe, | |
| "width": contentTypePlain, | |
| "wrap": contentTypePlain, | |
| "xmlns": contentTypeURL, | |
| } | |
| // attrType returns a conservative (upper-bound on authority) guess at the | |
| // type of the lowercase named attribute. | |
| func attrType(name string) contentType { | |
| if strings.HasPrefix(name, "data-") { | |
| // Strip data- so that custom attribute heuristics below are | |
| // widely applied. | |
| // Treat data-action as URL below. | |
| name = name[5:] | |
| } else if colon := strings.IndexRune(name, ':'); colon != -1 { | |
| if name[:colon] == "xmlns" { | |
| return contentTypeURL | |
| } | |
| // Treat svg:href and xlink:href as href below. | |
| name = name[colon+1:] | |
| } | |
| if t, ok := attrTypeMap[name]; ok { | |
| return t | |
| } | |
| // Treat partial event handler names as script. | |
| if strings.HasPrefix(name, "on") { | |
| return contentTypeJS | |
| } | |
| // Heuristics to prevent "javascript:..." injection in custom | |
| // data attributes and custom attributes like g:tweetUrl. | |
| // http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes | |
| // "Custom data attributes are intended to store custom data | |
| // private to the page or application, for which there are no | |
| // more appropriate attributes or elements." | |
| // Developers seem to store URL content in data URLs that start | |
| // or end with "URI" or "URL". | |
| if strings.Contains(name, "src") || | |
| strings.Contains(name, "uri") || | |
| strings.Contains(name, "url") { | |
| return contentTypeURL | |
| } | |
| return contentTypePlain | |
| } |