| // 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 html |
| |
| 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/Overview.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, |
| "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 named attribute. |
| func attrType(name string) contentType { |
| name = strings.ToLower(name) |
| 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/elements.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 |
| } |