// 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 // https://www.w3.org/TR/html5/Overview.html#attributes-1 // as well as "%URI"-typed attributes from // https://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 // https://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 prefix, short, ok := strings.Cut(name, ":"); ok { if prefix == "xmlns" { return contentTypeURL } // Treat svg:href and xlink:href as href below. name = short } 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. // https://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 }