aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/google/safehtml/template
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2025-01-22 16:07:17 +0100
committerTaras Madan <tarasmadan@google.com>2025-01-23 10:42:36 +0000
commit7b4377ad9d8a7205416df8d6217ef2b010f89481 (patch)
treee6fec4fd12ff807a16d847923f501075bf71d16c /vendor/github.com/google/safehtml/template
parent475a4c203afb8b7d3af51c4fd32bb170ff32a45e (diff)
vendor: delete
Diffstat (limited to 'vendor/github.com/google/safehtml/template')
-rw-r--r--vendor/github.com/google/safehtml/template/context.go183
-rw-r--r--vendor/github.com/google/safehtml/template/delim_string.go16
-rw-r--r--vendor/github.com/google/safehtml/template/doc.go291
-rw-r--r--vendor/github.com/google/safehtml/template/error.go280
-rw-r--r--vendor/github.com/google/safehtml/template/escape.go884
-rw-r--r--vendor/github.com/google/safehtml/template/init.go28
-rw-r--r--vendor/github.com/google/safehtml/template/sanitize.go258
-rw-r--r--vendor/github.com/google/safehtml/template/sanitizers.go599
-rw-r--r--vendor/github.com/google/safehtml/template/state_string.go16
-rw-r--r--vendor/github.com/google/safehtml/template/template.go651
-rw-r--r--vendor/github.com/google/safehtml/template/transition.go312
-rw-r--r--vendor/github.com/google/safehtml/template/trustedfs.go98
-rw-r--r--vendor/github.com/google/safehtml/template/trustedsource.go105
-rw-r--r--vendor/github.com/google/safehtml/template/trustedtemplate.go36
-rw-r--r--vendor/github.com/google/safehtml/template/url.go122
15 files changed, 0 insertions, 3879 deletions
diff --git a/vendor/github.com/google/safehtml/template/context.go b/vendor/github.com/google/safehtml/template/context.go
deleted file mode 100644
index dd7886dc6..000000000
--- a/vendor/github.com/google/safehtml/template/context.go
+++ /dev/null
@@ -1,183 +0,0 @@
-// Copyright 2017 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"
-)
-
-// context describes the state an HTML parser must be in when it reaches the
-// portion of HTML produced by evaluating a particular template node.
-//
-// The zero value of type Context is the start context for a template that
-// produces an HTML fragment as defined at
-// http://www.w3.org/TR/html5/syntax.html#the-end
-// where the context element is null.
-type context struct {
- state state
- delim delim
- element element
- attr attr
- err *Error
- // scriptType is the lowercase value of the "type" attribute inside the current "script"
- // element (see https://dev.w3.org/html5/spec-preview/the-script-element.html#attr-script-type).
- // This field will be empty if the parser is currently not in a script element,
- // the type attribute has not already been parsed in the current element, or if the
- // value of the type attribute cannot be determined at parse time.
- scriptType string
- // linkRel is the value of the "rel" attribute inside the current "link"
- // element (see https://html.spec.whatwg.org/multipage/semantics.html#attr-link-rel).
- // This value has been normalized to lowercase with exactly one space between tokens
- // and exactly one space at start and end, so that a lookup of any token foo can
- // be performed by searching for the substring " foo ".
- // This field will be empty if the parser is currently not in a link element,
- // the rel attribute has not already been parsed in the current element, or if the
- // value of the rel attribute cannot be determined at parse time.
- linkRel string
-}
-
-// eq returns whether Context c is equal to Context d.
-func (c context) eq(d context) bool {
- return c.state == d.state &&
- c.delim == d.delim &&
- c.element.eq(d.element) &&
- c.attr.eq(d.attr) &&
- c.err == d.err &&
- c.scriptType == d.scriptType &&
- c.linkRel == d.linkRel
-}
-
-// state describes a high-level HTML parser state.
-//
-// It bounds the top of the element stack, and by extension the HTML insertion
-// mode, but also contains state that does not correspond to anything in the
-// HTML5 parsing algorithm because a single token production in the HTML
-// grammar may contain embedded actions in a template. For instance, the quoted
-// HTML attribute produced by
-// <div title="Hello {{.World}}">
-// is a single token in HTML's grammar but in a template spans several nodes.
-type state uint8
-
-//go:generate stringer -type state
-
-const (
- // stateText is parsed character data. An HTML parser is in
- // this state when its parse position is outside an HTML tag,
- // directive, comment, and special element body.
- stateText state = iota
- // stateSpecialElementBody occurs inside a specal HTML element body.
- stateSpecialElementBody
- // stateTag occurs before an HTML attribute or the end of a tag.
- stateTag
- // stateAttrName occurs inside an attribute name.
- // It occurs between the ^'s in ` ^name^ = value`.
- stateAttrName
- // stateAfterName occurs after an attr name has ended but before any
- // equals sign. It occurs between the ^'s in ` name^ ^= value`.
- stateAfterName
- // stateBeforeValue occurs after the equals sign but before the value.
- // It occurs between the ^'s in ` name =^ ^value`.
- stateBeforeValue
- // stateHTMLCmt occurs inside an <!-- HTML comment -->.
- stateHTMLCmt
- // stateAttr occurs inside an HTML attribute whose content is text.
- stateAttr
- // stateError is an infectious error state outside any valid
- // HTML/CSS/JS construct.
- stateError
-)
-
-// isComment reports whether a state contains content meant for template
-// authors & maintainers, not for end-users or machines.
-func isComment(s state) bool {
- switch s {
- case stateHTMLCmt:
- return true
- }
- return false
-}
-
-// isInTag reports whether s occurs solely inside an HTML tag.
-func isInTag(s state) bool {
- switch s {
- case stateTag, stateAttrName, stateAfterName, stateBeforeValue, stateAttr:
- return true
- }
- return false
-}
-
-// delim is the delimiter that will end the current HTML attribute.
-type delim uint8
-
-//go:generate stringer -type delim
-
-const (
- // delimNone occurs outside any attribute.
- delimNone delim = iota
- // delimDoubleQuote occurs when a double quote (") closes the attribute.
- delimDoubleQuote
- // delimSingleQuote occurs when a single quote (') closes the attribute.
- delimSingleQuote
- // delimSpaceOrTagEnd occurs when a space or right angle bracket (>)
- // closes the attribute.
- delimSpaceOrTagEnd
-)
-
-type element struct {
- // name is the lowercase name of the element. If context joining has occurred, name
- // will be arbitrarily assigned the element name from one of the joined contexts.
- name string
- // names contains all possible names the element could assume because of context joining.
- // For example, after joining the contexts in the "if" and "else" branches of
- // {{if .C}}<img{{else}}<audio{{end}} src="/some/path">`,
- // names will contain "img" and "audio".
- // names can also contain empty strings, which represent joined contexts with no element name.
- // names will be empty if no context joining occurred.
- names []string
-}
-
-// eq reports whether a and b have the same name. All other fields are ignored.
-func (e element) eq(d element) bool {
- return e.name == d.name
-}
-
-// String returns the string representation of the element.
-func (e element) String() string {
- return "element" + strings.Title(e.name)
-}
-
-// attr represents the attribute that the parser is in, that is,
-// starting from stateAttrName until stateTag/stateText (exclusive).
-type attr struct {
- // name is the lowercase name of the attribute. If context joining has occurred, name
- // will be arbitrarily assigned the attribute name from one of the joined contexts.
- name string
- // value is the value of the attribute. If context joining has occurred, value
- // will be arbitrarily assigned the attribute value from one of the joined contexts.
- // If there are multiple actions in the attribute value, value will contain the
- // concatenation of all values seen so far. For example, in
- // <a name="foo{{.X}}bar{{.Y}}">
- // value is "foo" at "{{.X}}" and "foobar" at "{{.Y}}".
- value string
- // ambiguousValue indicates whether value contains an ambiguous value due to context-joining.
- ambiguousValue bool
- // names contains all possible names the attribute could assume because of context joining.
- // For example, after joining the contexts in the "if" and "else" branches of
- // <a {{if .C}}title{{else}}name{{end}}="foo">
- // names will contain "title" and "name".
- // names can also contain empty strings, which represent joined contexts with no attribute name.
- // names will be empty if no context joining occurred.
- names []string
-}
-
-// eq reports whether a and b have the same name. All other fields are ignored.
-func (a attr) eq(b attr) bool {
- return a.name == b.name
-}
-
-// String returns the string representation of the attr.
-func (a attr) String() string {
- return "attr" + strings.Title(a.name)
-}
diff --git a/vendor/github.com/google/safehtml/template/delim_string.go b/vendor/github.com/google/safehtml/template/delim_string.go
deleted file mode 100644
index 0ef2c2510..000000000
--- a/vendor/github.com/google/safehtml/template/delim_string.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Code generated by "stringer -type Delim"; DO NOT EDIT
-
-package template
-
-import "fmt"
-
-const _Delim_name = "DelimNoneDelimDoubleQuoteDelimSingleQuoteDelimSpaceOrTagEnd"
-
-var _Delim_index = [...]uint8{0, 9, 25, 41, 59}
-
-func (i delim) String() string {
- if i >= delim(len(_Delim_index)-1) {
- return fmt.Sprintf("delim(%d)", i)
- }
- return _Delim_name[_Delim_index[i]:_Delim_index[i+1]]
-}
diff --git a/vendor/github.com/google/safehtml/template/doc.go b/vendor/github.com/google/safehtml/template/doc.go
deleted file mode 100644
index fab552b25..000000000
--- a/vendor/github.com/google/safehtml/template/doc.go
+++ /dev/null
@@ -1,291 +0,0 @@
-// Copyright (c) 2017 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 or at
-// https://developers.google.com/open-source/licenses/bsd
-
-/*
-Package template (safehtml/template) implements data-driven templates for
-generating HTML output safe against code injection. It provides an interface
-similar to that of package html/template, but produces HTML output that is more
-secure. Therefore, it should be used instead of html/template to render HTML.
-
-The documentation here focuses on the security features of the package. For
-information about how to program the templates themselves, see the
-documentation for text/template.
-
-
-Basic usage
-
-This package provides an API almost identical to that of text/template and
-html/template to parse and execute HTML templates safely.
-
- tmpl := template.Must(template.New("name").Parse(`<div>Hello {{.}}</div>`))
- err := tmpl.Execute(out, data)
-
-If successful, out will contain code-injection-safe HTML. Otherwise, err's
-string representation will describe the error that occurred.
-
-Elements of data might be modified at run time before being included in out, or
-rejected completely if such a conversion is not possible. Pass values of
-appropriate types from package safehtml to ensure that they are included in the
-template's HTML output in their expected form. More details are provided below
-in "Contextual autosanitization" and "Sanitization contexts".
-
-
-Security improvements
-
-safehtml/template produces HTML more resistant to code injection than
-html/template because it:
- * Allows values of types only from package safehtml to bypass run-time
- sanitization. These types represent values that are known---by construction
- or by run-time sanitization---to be safe for use in various HTML contexts
- without being processed by certain sanitization functions.
- * Does not attempt to escape CSS or JavaScript. Instead of attempting to
- parse and escape these complex languages, safehtml/template allows values
- of only the appropriate types from package safehtml (e.g. safehtml.Style,
- safehtml.Script) to be used in these contexts, since they are already
- guaranteed to be safe.
- * Emits an error if user data is interpolated in unsafe contexts, such as
- within disallowed elements or unquoted attribute values.
- * Only loads templates from trusted sources. This ensures that the contents
- of the template are always under programmer control. More details are
- provided below in "Trusted template sources".
- * Differentiates between URLs that load code and those that do not. URLs in
- the former category must be supplied to the template as values of type
- safehtml.TrustedResourceURL, whose type contract promises that the URL
- identifies a trustworthy resource. URLs in the latter category can be
- sanitized at run time.
-
-
-Threat model
-
-safehtml/template assumes that programmers are trustworthy. Therefore, data
-fully under programmer control, such as string literals, are considered safe.
-The types from package safehtml are designed around this same assumption, so
-their type contracts are trusted by this package.
-
-safehtml/template considers all other data values untrustworthy and
-conservatively assumes that such values could result in a code-injection
-vulnerability if included verbatim in HTML.
-
-
-Trusted template sources
-
-safehtml/template loads templates only from trusted sources. Therefore, template
-text, file paths, and file patterns passed to Parse* functions and methods must
-be entirely under programmer control.
-
-This constraint is enforced by using unexported string types for the parameters
-of Parse* functions and methods, such as trustedFilePattern for ParseGlob.
-The only values that may be assigned to these types (and thus provided as
-arguments) are untyped string constants such as string literals, which are
-always under programmer control.
-
-
-Contextual autosanitization
-
-Code injection vulnerabilities, such as cross-site scripting (XSS), occur when
-untrusted data values are embedded in a HTML document. For example,
-
- import "text/template"
- ...
- var t = template.Must(template.New("foo").Parse(`<a href="{{ .X }}">{{ .Y }}</a>`))
- func renderHTML(x, y string) string {
- var out bytes.Buffer
- err := t.Execute(&out, struct{ X, Y string }{x, y})
- // Error checking elided
- return out.String()
- }
-
-If x and y originate from user-provided data, an attacker who controls these
-strings could arrange for them to contain the following values:
-
- x = "javascript:evil()"
- y = "</a><script>alert('pwned')</script><a>"
-
-which will cause renderHTML to return the following unsafe HTML:
-
- <a href="javascript:evil()"></a><script>alert('pwned')</script><a></a>
-
-To prevent such vulnerabilities, untrusted data must be sanitized before being
-included in HTML. A sanitization function takes untrusted data and returns a
-string that will not create a code-injection vulnerability in the destination
-context. The function might return the input unchanged if it deems it safe,
-escape special runes in the input's string representation to prevent them from
-triggering undesired state changes in the HTML parser, or entirely replace the
-input by an innocuous string (also known as "filtering"). If none of these
-conversions are possible, the sanitization function aborts template processing.
-
-safehtml/template contextually autosanitizes untrusted data by adding
-appropriate sanitization functions to template actions to ensure that the
-action output is safe to include in the HTML context in which the action
-appears. For example, in
-
- import "safehtml/template"
- ...
- var t = template.Must(template.New("foo").Parse(`<a href="{{ .X }}">{{ .Y }}</a>`))
- func renderHTML(x, y string) string {
- var out bytes.Buffer
- err := t.Execute(&out, struct{ X, Y string }{x, y})
- // Error checking elided
- return out.String()
- }
-
-the contextual autosanitizer rewrites the template to
-
- <a href="{{ .X | _sanitizeTrustedResourceURLOrURL | _sanitizeHTML }}">{{ .Y | _sanitizeHTML }}</a>
-
-so that the template produces the following safe, sanitized HTML output (split
-across multiple lines for clarity):
-
- <a href="about:invalid#zGoSafez">
- &lt;/a&gt;&lt;script&gt;alert(&#39;pwned&#39;)&lt;/script&gt;&lt;a&gt;
- </a>
-
-Similar template systems such as html/template, Soy, and Angular, refer to this
-functionality as "contextual autoescaping". safehtml/template uses the term
-"autosanitization" instead of "autoescaping" since "sanitization" broadly
-captures the operations of escaping and filtering.
-
-
-Sanitization contexts
-
-The types of sanitization functions inserted into an action depend on the
-action's sanitization context, which is determined by its surrounding text.
-The following table describes these sanitization contexts.
-
- +--------------------+----------------------------------+------------------------------+-----------------------+
- | Context | Examples | Safe types | Run-time sanitizer |
- |--------------------+----------------------------------+------------------------------+-----------------------+
- | HTMLContent | Hello {{.}} | safehtml.HTML | safehtml.HTMLEscaped |
- | | <title>{{.}}</title> | | |
- +--------------------------------------------------------------------------------------------------------------+
- | HTMLValOnly | <iframe srcdoc="{{.}}"></iframe> | safehtml.HTML* | N/A |
- +--------------------------------------------------------------------------------------------------------------+
- | URL | <q cite="{{.}}">Cite</q> | safehtml.URL | safehtml.URLSanitized |
- +--------------------------------------------------------------------------------------------------------------+
- | URL or | <a href="{{.}}">Link</a> | safehtml.URL | safehtml.URLSanitized |
- | TrustedResourceURL | | safehtml.TrustedResourceURL | |
- +--------------------------------------------------------------------------------------------------------------+
- | TrustedResourceURL | <script src="{{.}}"></script> | safehtml.TrustedResourceURL† | N/A |
- +--------------------------------------------------------------------------------------------------------------+
- | Script | <script>{{.}}</script> | safehtml.Script* | N/A |
- +--------------------------------------------------------------------------------------------------------------+
- | Style | <p style="{{.}}">Paragraph</p> | safehtml.Style* | N/A |
- +--------------------------------------------------------------------------------------------------------------+
- | Stylesheet | <style>{{.}}</style> | safehtml.StyleSheet* | N/A |
- +--------------------------------------------------------------------------------------------------------------+
- | Identifier | <h1 id="{{.}}">Hello</h1> | safehtml.Identifier* | N/A |
- +--------------------------------------------------------------------------------------------------------------+
- | Enumerated value | <a target="{{.}}">Link</a> | Allowed string values | N/A |
- | | | ("_self" or "_blank" for | |
- | | | the given example) | |
- +--------------------------------------------------------------------------------------------------------------+
- | None | <h1 class="{{.}}">Hello</h1> | N/A (any type allowed) | N/A (any type |
- | | | | allowed) |
- +--------------------+----------------------------------+------------------------------+-----------------------+
- *: Values only of this type are allowed in this context. Other values will trigger a run-time error.
- †: If the action is a prefix of the attribute value, values only of this type are allowed.
- Otherwise, values of any type are allowed. See "Substitutions in URLs" for more details.
-
-For each context, the function named in "Run-time sanitizer" is called to
-sanitize the output of the action. However, if the action outputs a value of
-any of the types listed in "Safe types", the run-time sanitizer is not called.
-For example, in
-
- <title>{{ .X }}</title>
-
-if X is a string value, a HTML sanitizer that calls safehtml.HTMLEscaped will be
-added to the action to sanitize X.
-
- // _sanitizeHTML calls safehtml.HTMLEscaped.
- <title>{{ .X | _sanitizeHTML }}</title>
-
-However, if X is a safehtml.HTML value, _sanitizeHTML will not change its
-value, since safehtml.HTML values are already safe to use in HTML contexts.
-Therefore, the string contents of X will bypass context-specific
-sanitization (in this case, HTML escaping) and appear unchanged in the
-template's HTML output. Note that in attribute value contexts, HTML escaping
-will always take place, whether or not context-specific sanitization is
-performed. More details can be found at the end of this section.
-
-In certain contexts, the autosanitizer allows values only of that context's
-"Safe types". Any other values will trigger an error and abort template
-processing. For example, the template
-
- <style>{{ .X }}</style>
-
-triggers a run-time error if X is not a safehtml.StyleSheet. Otherwise, the
-string form of X will appear unchanged in the output. The only exception to
-this behavior is in TrustedResourceURL sanitization contexts, where actions may
-output data of any type if the action occurs after a safe attribute value prefix.
-More details can be found below in "Substitutions in URLs".
-
-
-Unconditional sanitization
-
-In attribute value contexts, action outputs are always HTML-escaped after
-context-specific sanitization to ensure that the attribute values cannot change
-change the structure of the surrounding HTML tag. In URL or TrustedResourceURL
-sanitization contexts, action outputs are additionally URL-normalized to reduce
-the likelihood of downstream URL-parsing bugs. For example, the template
-
- <a href="{{ .X }}">Link</a>
- <p id="{{ .Y }}">Text</p>
-
-is rewritten by the autosanitizer into
-
- // _sanitizeHTML calls safehtml.HTMLEscaped.
- <a href="{{ .X | _sanitizeTrustedResourceURLOrURL | _normalizeURL | _sanitizeHTML }}">Link</a>
- <p id="{{ .Y | _sanitizeIdentifier | _sanitizeHTML }}">Text</p>
-
-Even if X is a safehtml.URL or safehtml.TrustedResourceURL value, which
-remains unchanged after _sanitizeTrustedResourceURLOrURL, X will still be
-URL-normalized and HTML-escaped. Likewise, Y will still be HTML-escaped even if
-its string form is left unchanged by _sanitizeIdentifier.
-
-
-Substitutions in URLs
-
-Values of any type may be substituted into attribute values in URL and
-TrustedResourceURL sanitization contexts only if the action is preceded by a
-safe URL prefix. For example, in
-
- <q cite="http://www.foo.com/{{ .PathComponent }}">foo</q>
-
-Since "http://www.foo.com/" is a safe URL prefix, PathComponent can safely be
-interpolated into this URL sanitization context after URL normalization.
-Similarly, in
-
- <script src="https://www.bar.com/{{ .PathComponent }}"></script>
-
-Since "https://www.bar.com/" is a safe TrustedResourceURL prefix, PathComponent
-can safely be interpolated into this TrustedResourceURL sanitization context
-after URL escaping. Substitutions after a safe TrustedResourceURL prefix are
-escaped instead of normalized to prevent the injection of any new URL
-components, including additional path components. URL escaping also takes place
-in URL sanitization contexts where the substitutions occur in the query or
-fragment part of the URL, such as in:
-
- <a href="/foo?q={{ .Query }}&hl={{ .LangCode }}">Link</a>
-
-A URL prefix is considered safe in a URL sanitization context if it does
-not end in an incomplete HTML character reference (e.g. https&#1) or incomplete
-percent-encoding character triplet (e.g. /fo%6), does not contain whitespace or control
-characters, and one of the following is true:
- * The prefix has a safe scheme (i.e. http, https, mailto, or ftp).
- * The prefix has the data scheme with base64 encoding and an allowed audio, image,
- or video MIME type (e.g. data:img/jpeg;base64, data:video/mp4;base64).
- * The prefix has no scheme at all, and cannot be interpreted as a scheme prefix (e.g. /path).
-
-A URL prefix is considered safe in a TrustedResourceURL sanitization context if it does
-not end in an incomplete HTML character reference (e.g. https&#1) or incomplete
-percent-encoding character triplet (e.g. /fo%6), does not contain white space or control
-characters, and one of the following is true:
- * The prefix has the https scheme and contains a domain name (e.g. https://www.foo.com).
- * The prefix is scheme-relative and contains a domain name (e.g. //www.foo.com/).
- * The prefix is path-absolute and contains a path (e.g. /path).
- * The prefix is "about:blank".
-*/
-package template
diff --git a/vendor/github.com/google/safehtml/template/error.go b/vendor/github.com/google/safehtml/template/error.go
deleted file mode 100644
index fe7821433..000000000
--- a/vendor/github.com/google/safehtml/template/error.go
+++ /dev/null
@@ -1,280 +0,0 @@
-// Copyright 2017 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 (
- "fmt"
- "text/template/parse"
-)
-
-// Error describes a problem encountered during template Escaping.
-type Error struct {
- // ErrorCode describes the kind of error.
- ErrorCode ErrorCode
- // Node is the node that caused the problem, if known.
- // If not nil, it overrides Name and Line.
- Node parse.Node
- // Name is the name of the template in which the error was encountered.
- Name string
- // Line is the line number of the error in the template source or 0.
- Line int
- // Description is a human-readable description of the problem.
- Description string
-}
-
-// ErrorCode is a code for a kind of error.
-type ErrorCode int
-
-// We define codes for each error that manifests while escaping templates, but
-// escaped templates may also fail at runtime.
-//
-// Output: "ZgotmplZ"
-// Example:
-// <img src="{{.X}}">
-// where {{.X}} evaluates to `javascript:...`
-// Discussion:
-// "ZgotmplZ" is a special value that indicates that unsafe content reached a
-// CSS or URL context at runtime. The output of the example will be
-// <img src="#ZgotmplZ">
-// If the data comes from a trusted source, use content types to exempt it
-// from filtering: URL(`javascript:...`).
-const (
- // OK indicates the lack of an error.
- OK ErrorCode = iota
-
- // ErrAmbigContext: "... appears in an ambiguous context within a URL"
- // Example:
- // <a href="
- // {{if .C}}
- // /path/
- // {{else}}
- // /search?q=
- // {{end}}
- // {{.X}}
- // ">
- // Discussion:
- // {{.X}} is in an ambiguous URL context since, depending on {{.C}},
- // it may be either a URL suffix or a query parameter.
- // Moving {{.X}} into the condition removes the ambiguity:
- // <a href="{{if .C}}/path/{{.X}}{{else}}/search?q={{.X}}">
- ErrAmbigContext
-
- // ErrBadHTML: "expected space, attr name, or end of tag, but got ...",
- // "... in unquoted attr", "... in attribute name"
- // Example:
- // <a href = /search?q=foo>
- // <href=foo>
- // <form na<e=...>
- // <option selected<
- // Discussion:
- // This is often due to a typo in an HTML element, but some runes
- // are banned in tag names, attribute names, and unquoted attribute
- // values because they can tickle parser ambiguities.
- // Quoting all attributes is the best policy.
- ErrBadHTML
-
- // ErrBranchEnd: "{{if}} branches end in different contexts"
- // Example:
- // {{if .C}}<a href="{{end}}{{.X}}
- // Discussion:
- // Package html/template statically examines each path through an
- // {{if}}, {{range}}, or {{with}} to escape any following pipelines.
- // The example is ambiguous since {{.X}} might be an HTML text node,
- // or a URL prefix in an HTML attribute. The context of {{.X}} is
- // used to figure out how to escape it, but that context depends on
- // the run-time value of {{.C}} which is not statically known.
- //
- // The problem is usually something like missing quotes or angle
- // brackets, or can be avoided by refactoring to put the two contexts
- // into different branches of an if, range or with. If the problem
- // is in a {{range}} over a collection that should never be empty,
- // adding a dummy {{else}} can help.
- ErrBranchEnd
-
- // ErrEndContext: "... ends in a non-text context: ..."
- // Examples:
- // <div
- // <div title="no close quote>
- // <script>f()
- // Discussion:
- // Executed templates should produce a DocumentFragment of HTML.
- // Templates that end without closing tags will trigger this error.
- // Templates that should not be used in an HTML context or that
- // produce incomplete Fragments should not be executed directly.
- //
- // {{define "main"}} <script>{{template "helper"}}</script> {{end}}
- // {{define "helper"}} document.write(' <div title=" ') {{end}}
- //
- // "helper" does not produce a valid document fragment, so should
- // not be Executed directly.
- ErrEndContext
-
- // ErrNoSuchTemplate: "no such template ..."
- // Examples:
- // {{define "main"}}<div {{template "attrs"}}>{{end}}
- // {{define "attrs"}}href="{{.URL}}"{{end}}
- // Discussion:
- // Package html/template looks through template calls to compute the
- // context.
- // Here the {{.URL}} in "attrs" must be treated as a URL when called
- // from "main", but you will get this error if "attrs" is not defined
- // when "main" is parsed.
- ErrNoSuchTemplate
-
- // ErrOutputContext: "cannot compute output context for template ..."
- // Examples:
- // {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}}
- // Discussion:
- // A recursive template does not end in the same context in which it
- // starts, and a reliable output context cannot be computed.
- // Look for typos in the named template.
- // If the template should not be called in the named start context,
- // look for calls to that template in unexpected contexts.
- // Maybe refactor recursive templates to not be recursive.
- ErrOutputContext
-
- // ErrPartialCharset: "unfinished JS regexp charset in ..."
- // Example:
- // <script>var pattern = /foo[{{.Chars}}]/</script>
- // Discussion:
- // Package html/template does not support interpolation into regular
- // expression literal character sets.
- ErrPartialCharset
-
- // ErrPartialEscape: "unfinished escape sequence in ..."
- // Example:
- // <script>alert("\{{.X}}")</script>
- // Discussion:
- // Package html/template does not support actions following a
- // backslash.
- // This is usually an error and there are better solutions; for
- // example
- // <script>alert("{{.X}}")</script>
- // should work, and if {{.X}} is a partial escape sequence such as
- // "xA0", mark the whole sequence as safe content: JSStr(`\xA0`)
- ErrPartialEscape
-
- // ErrRangeLoopReentry: "on range loop re-entry: ..."
- // Example:
- // <script>var x = [{{range .}}'{{.}},{{end}}]</script>
- // Discussion:
- // If an iteration through a range would cause it to end in a
- // different context than an earlier pass, there is no single context.
- // In the example, there is missing a quote, so it is not clear
- // whether {{.}} is meant to be inside a JS string or in a JS value
- // context. The second iteration would produce something like
- //
- // <script>var x = ['firstValue,'secondValue]</script>
- ErrRangeLoopReentry
-
- // ErrSlashAmbig: '/' could start a division or regexp.
- // Example:
- // <script>
- // {{if .C}}var x = 1{{end}}
- // /-{{.N}}/i.test(x) ? doThis : doThat();
- // </script>
- // Discussion:
- // The example above could produce `var x = 1/-2/i.test(s)...`
- // in which the first '/' is a mathematical division operator or it
- // could produce `/-2/i.test(s)` in which the first '/' starts a
- // regexp literal.
- // Look for missing semicolons inside branches, and maybe add
- // parentheses to make it clear which interpretation you intend.
- ErrSlashAmbig
-
- // ErrPredefinedEscaper: "predefined escaper ... disallowed in template"
- // Example:
- // <div class={{. | html}}>Hello<div>
- // Discussion:
- // Package html/template already contextually escapes all pipelines to
- // produce HTML output safe against code injection. Manually escaping
- // pipeline output using the predefined escapers "html" or "urlquery" is
- // unnecessary, and may affect the correctness or safety of the escaped
- // pipeline output in Go 1.8 and earlier.
- //
- // In most cases, such as the given example, this error can be resolved by
- // simply removing the predefined escaper from the pipeline and letting the
- // contextual autoescaper handle the escaping of the pipeline. In other
- // instances, where the predefined escaper occurs in the middle of a
- // pipeline where subsequent commands expect escaped input, e.g.
- // {{.X | html | makeALink}}
- // where makeALink does
- // return `<a href="`+input+`">link</a>`
- // consider refactoring the surrounding template to make use of the
- // contextual autoescaper, i.e.
- // <a href="{{.X}}">link</a>
- //
- // To ease migration to Go 1.9 and beyond, "html" and "urlquery" will
- // continue to be allowed as the last command in a pipeline. However, if the
- // pipeline occurs in an unquoted attribute value context, "html" is
- // disallowed. Avoid using "html" and "urlquery" entirely in new templates.
- ErrPredefinedEscaper
-
- // ErrEscapeAction: "cannot escape action ..."
- // Discussion:
- // Error returned while escaping an action using EscaperForContext.
- // Refer to error message for more details.
- // TODO: remove this error type and replace it with more informative sanitization errors.
- ErrEscapeAction
-
- // ErrCSPCompatibility: `"javascript:" URI disallowed for CSP compatibility`,
- // "inline event handler ... is disallowed for CSP compatibility
- // Examples:
- // <span onclick="doThings();">A thing.</span>
- // <a href="javascript:linkClicked()">foo</a>
- // Discussion:
- // Inline event handlers (onclick="...", onerror="...") and
- // <a href="javascript:..."> links can be used to run scripts,
- // so an attacker who finds an XSS bug could inject such HTML
- // and execute malicious JavaScript. These patterns must be
- // refactored into safer alternatives for compatibility with
- // Content Security Policy (CSP).
- //
- // For example, the following HTML that contains an inline event handler:
- // <script> function doThings() { ... } </script>
- // <span onclick="doThings();">A thing.</span>
- // can be refactored into:
- // <span id="things">A thing.</span>
- // <script nonce="${nonce}">
- // document.addEventListener('DOMContentLoaded', function () {
- // document.getElementById('things')
- // .addEventListener('click', function doThings() { ... });
- // });
- // </script>
- //
- // Likewise, the following HTML containng a javascript: URI:
- // <a href="javascript:linkClicked()">foo</a>
- // can be refactored into:
- // <a id="foo">foo</a>
- // <script nonce="${nonce}">
- // document.addEventListener('DOMContentLoaded', function () {
- // document.getElementById('foo')
- // .addEventListener('click', linkClicked);
- // });
- // </script>
- ErrCSPCompatibility
- // All JS templates inside script literals have to be balanced; otherwise a concatenation such as
- // <script>alert(`x{{.data}}`</script> can contain XSS if data contains user-controlled escaped strings (e.g. as JSON).
- ErrUnbalancedJsTemplate
-)
-
-func (e *Error) Error() string {
- switch {
- case e.Node != nil:
- loc, _ := (*parse.Tree)(nil).ErrorContext(e.Node)
- return fmt.Sprintf("html/template:%s: %s", loc, e.Description)
- case e.Line != 0:
- return fmt.Sprintf("html/template:%s:%d: %s", e.Name, e.Line, e.Description)
- case e.Name != "":
- return fmt.Sprintf("html/template:%s: %s", e.Name, e.Description)
- }
- return "html/template: " + e.Description
-}
-
-// errorf creates an error given a format string f and args.
-// The template Name still needs to be supplied.
-func errorf(k ErrorCode, node parse.Node, line int, f string, args ...interface{}) *Error {
- return &Error{k, node, "", line, fmt.Sprintf(f, args...)}
-}
diff --git a/vendor/github.com/google/safehtml/template/escape.go b/vendor/github.com/google/safehtml/template/escape.go
deleted file mode 100644
index 8a9d53dd5..000000000
--- a/vendor/github.com/google/safehtml/template/escape.go
+++ /dev/null
@@ -1,884 +0,0 @@
-// 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 (
- "bytes"
- "fmt"
- "html"
- "reflect"
- "strings"
- "text/template"
- "text/template/parse"
-)
-
-// TODO: remove all unused escaping logic inherited from html/template.
-// TODO: replace "escape" with "sanitize" in file names and contents to maintain consistency with safehtml/template docs.
-
-// escapeTemplate rewrites the named template, which must be
-// associated with t, to guarantee that the output of any of the named
-// templates is properly escaped. If no error is returned, then the named templates have
-// been modified. Otherwise the named templates have been rendered
-// unusable.
-func escapeTemplate(tmpl *Template, node parse.Node, name string) error {
- c, _ := tmpl.esc.escapeTree(context{}, node, name, 0)
- var err error
- if c.err != nil {
- err, c.err.Name = c.err, name
- } else if c.state != stateText {
- err = &Error{ErrEndContext, nil, name, 0, fmt.Sprintf("ends in a non-text context: %+v", c)}
- }
- if err != nil {
- // Prevent execution of unsafe templates.
- if t := tmpl.set[name]; t != nil {
- t.escapeErr = err
- t.text.Tree = nil
- t.Tree = nil
- }
- return err
- }
- tmpl.esc.commit()
- if t := tmpl.set[name]; t != nil {
- t.escapeErr = errEscapeOK
- t.Tree = t.text.Tree
- }
- return nil
-}
-
-// evalArgs formats the list of arguments into a string. It is equivalent to
-// fmt.Sprint(args...), except that it deferences all pointers.
-func evalArgs(args ...interface{}) string {
- // Optimization for simple common case of a single string argument.
- if len(args) == 1 {
- if s, ok := args[0].(string); ok {
- return s
- }
- }
- for i, arg := range args {
- args[i] = indirectToStringerOrError(arg)
- }
- return fmt.Sprint(args...)
-}
-
-// escaper collects type inferences about templates and changes needed to make
-// templates injection safe.
-type escaper struct {
- // ns is the nameSpace that this escaper is associated with.
- ns *nameSpace
- // output[templateName] is the output context for a templateName that
- // has been mangled to include its input context.
- output map[string]context
- // derived[c.mangle(name)] maps to a template derived from the template
- // named name templateName for the start context c.
- derived map[string]*template.Template
- // called[templateName] is a set of called mangled template names.
- called map[string]bool
- // xxxNodeEdits are the accumulated edits to apply during commit.
- // Such edits are not applied immediately in case a template set
- // executes a given template in different escaping contexts.
- actionNodeEdits map[*parse.ActionNode][]string
- templateNodeEdits map[*parse.TemplateNode]string
- textNodeEdits map[*parse.TextNode][]byte
-}
-
-// makeEscaper creates a blank escaper for the given set.
-func makeEscaper(n *nameSpace) escaper {
- return escaper{
- n,
- map[string]context{},
- map[string]*template.Template{},
- map[string]bool{},
- map[*parse.ActionNode][]string{},
- map[*parse.TemplateNode]string{},
- map[*parse.TextNode][]byte{},
- }
-}
-
-// escape escapes a template node.
-func (e *escaper) escape(c context, n parse.Node) context {
- switch n := n.(type) {
- case *parse.ActionNode:
- return e.escapeAction(c, n)
- case *parse.IfNode:
- return e.escapeBranch(c, &n.BranchNode, "if")
- case *parse.ListNode:
- return e.escapeList(c, n)
- case *parse.RangeNode:
- return e.escapeBranch(c, &n.BranchNode, "range")
- case *parse.TemplateNode:
- return e.escapeTemplate(c, n)
- case *parse.TextNode:
- return e.escapeText(c, n)
- case *parse.WithNode:
- return e.escapeBranch(c, &n.BranchNode, "with")
- }
- panic("escaping " + n.String() + " is unimplemented")
-}
-
-// escapeAction escapes an action template node.
-func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
- if len(n.Pipe.Decl) != 0 {
- // A local variable assignment, not an interpolation.
- return c
- }
- c = nudge(c)
- // Check for disallowed use of predefined escapers in the pipeline.
- for pos, idNode := range n.Pipe.Cmds {
- node, ok := idNode.Args[0].(*parse.IdentifierNode)
- if !ok {
- // A predefined escaper "esc" will never be found as an identifier in a
- // Chain or Field node, since:
- // - "esc.x ..." is invalid, since predefined escapers return strings, and
- // strings do not have methods, keys or fields.
- // - "... .esc" is invalid, since predefined escapers are global functions,
- // not methods or fields of any types.
- // Therefore, it is safe to ignore these two node types.
- continue
- }
- ident := node.Ident
- if _, ok := predefinedEscapers[ident]; ok {
- if pos < len(n.Pipe.Cmds)-1 ||
- c.state == stateAttr && c.delim == delimSpaceOrTagEnd && ident == "html" {
- return context{
- state: stateError,
- err: errorf(ErrPredefinedEscaper, n, n.Line, "predefined escaper %q disallowed in template", ident),
- }
- }
- }
- }
- switch c.state {
- case stateError:
- return c
- case stateAttrName, stateTag:
- c.state = stateAttrName
- }
- // TODO: integrate sanitizerForContext into escapeAction.
- s, err := sanitizerForContext(c)
- if err != nil {
- return context{
- state: stateError,
- // TODO: return sanitization-specific errors.
- err: errorf(ErrEscapeAction, n, n.Line, "cannot escape action %v: %s", n, err),
- }
- }
- e.editActionNode(n, s)
- return c
-}
-
-// ensurePipelineContains ensures that the pipeline ends with the commands with
-// the identifiers in s in order. If the pipeline ends with a predefined escaper
-// (i.e. "html" or "urlquery"), merge it with the identifiers in s.c
-func ensurePipelineContains(p *parse.PipeNode, s []string) {
- if len(s) == 0 {
- // Do not rewrite pipeline if we have no escapers to insert.
- return
- }
- // Precondition: p.Cmds contains at most one predefined escaper and the
- // escaper will be present at p.Cmds[len(p.Cmds)-1]. This precondition is
- // always true because of the checks in escapeAction.
- pipelineLen := len(p.Cmds)
- if pipelineLen > 0 {
- lastCmd := p.Cmds[pipelineLen-1]
- if idNode, ok := lastCmd.Args[0].(*parse.IdentifierNode); ok {
- if esc := idNode.Ident; predefinedEscapers[esc] {
- // Pipeline ends with a predefined escaper.
- if len(p.Cmds) == 1 && len(lastCmd.Args) > 1 {
- // Special case: pipeline is of the form {{ esc arg1 arg2 ... argN }},
- // where esc is the predefined escaper, and arg1...argN are its arguments.
- // Convert this into the equivalent form
- // {{ _eval_args_ arg1 arg2 ... argN | esc }}, so that esc can be easily
- // merged with the escapers in s.
- lastCmd.Args[0] = parse.NewIdentifier(evalArgsFuncName).SetTree(nil).SetPos(lastCmd.Args[0].Position())
- p.Cmds = append(p.Cmds, newIdentCmd(esc, p.Position()))
- pipelineLen++
- }
- // If any of the commands in s that we are about to insert is equivalent
- // to the predefined escaper, use the predefined escaper instead.
- dup := false
- for i, escaper := range s {
- if escFnsEq(esc, escaper) {
- s[i] = idNode.Ident
- dup = true
- }
- }
- if dup {
- // The predefined escaper will already be inserted along with the
- // escapers in s, so do not copy it to the rewritten pipeline.
- pipelineLen--
- }
- }
- }
- }
- // Rewrite the pipeline, creating the escapers in s at the end of the pipeline.
- newCmds := make([]*parse.CommandNode, pipelineLen, pipelineLen+len(s))
- copy(newCmds, p.Cmds)
- for _, name := range s {
- newCmds = append(newCmds, newIdentCmd(name, p.Position()))
- }
- p.Cmds = newCmds
-}
-
-// predefinedEscapers contains template predefined escapers that are equivalent
-// to some contextual escapers. Keep in sync with equivEscapers.
-var predefinedEscapers = map[string]bool{
- "html": true,
- "urlquery": true,
-}
-
-// equivEscapers matches contextual escapers to equivalent predefined
-// template escapers.
-var equivEscapers = map[string]string{
- // The following pairs of HTML escapers provide equivalent security
- // guarantees, since they all escape '\000', '\'', '"', '&', '<', and '>'.
- sanitizeHTMLFuncName: "html",
- sanitizeRCDATAFuncName: "html",
- // These two URL escapers produce URLs safe for embedding in a URL query by
- // percent-encoding all the reserved characters specified in RFC 3986 Section
- // 2.2
- queryEscapeURLFuncName: "urlquery",
- // The normalizer function is not actually equivalent to urlquery; urlquery is
- // stricter as it escapes reserved characters (e.g. '#'), while the normalizer
- // function does not. It is therefore only safe to replace the normalizer with
- // with urlquery (this happens in ensurePipelineContains), but not the other
- // way around. We keep this entry around to preserve the behavior of templates
- // written before Go 1.9, which might depend on this substitution taking place.
- normalizeURLFuncName: "urlquery",
-}
-
-// escFnsEq reports whether the two escaping functions are equivalent.
-func escFnsEq(a, b string) bool {
- return normalizeEscFn(a) == normalizeEscFn(b)
-}
-
-// normalizeEscFn(a) is equal to normalizeEscFn(b) for any pair of names of
-// escaper functions a and b that are equivalent.
-func normalizeEscFn(e string) string {
- if norm := equivEscapers[e]; norm != "" {
- return norm
- }
- return e
-}
-
-// newIdentCmd produces a command containing a single identifier node.
-func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode {
- return &parse.CommandNode{
- NodeType: parse.NodeCommand,
- Args: []parse.Node{parse.NewIdentifier(identifier).SetTree(nil).SetPos(pos)}, // TODO: SetTree.
- Pos: pos,
- }
-}
-
-// nudge returns the context that would result from following empty string
-// transitions from the input context.
-// For example, parsing:
-// `<a href=`
-// will end in context{stateBeforeValue, AttrURL}, but parsing one extra rune:
-// `<a href=x`
-// will end in context{stateURL, delimSpaceOrTagEnd, ...}.
-// There are two transitions that happen when the 'x' is seen:
-// (1) Transition from a before-value state to a start-of-value state without
-// consuming any character.
-// (2) Consume 'x' and transition past the first value character.
-// In this case, nudging produces the context after (1) happens.
-func nudge(c context) context {
- switch c.state {
- case stateTag:
- // In `<foo {{.}}`, the action should emit an attribute.
- c.state = stateAttrName
- case stateBeforeValue:
- // In `<foo bar={{.}}`, the action is an undelimited value.
- c.state, c.delim = stateAttr, delimSpaceOrTagEnd
- case stateAfterName:
- // In `<foo bar {{.}}`, the action is an attribute name.
- c.state = stateAttrName
- }
- return c
-}
-
-// join joins the two contexts of a branch template node. The result is an
-// error context if either of the input contexts are error contexts, or if the
-// input contexts differ.
-func join(a, b context, node parse.Node, nodeName string) context {
- if a.state == stateError {
- return a
- }
- if b.state == stateError {
- return b
- }
-
- // Accumulate the result of context-joining elements and attributes in a, since the
- // contents of a are always returned.
- a.element.names = joinNames(a.element.name, b.element.name, a.element.names, b.element.names)
- a.attr.names = joinNames(a.attr.name, b.attr.name, a.attr.names, b.attr.names)
- if a.attr.value != b.attr.value {
- a.attr.ambiguousValue = true
- }
-
- if a.eq(b) {
- return a
- }
-
- c := a
- c.element.name = b.element.name
- if c.eq(b) {
- // The contexts differ only by their element names. The element names from the conditional
- // branches that are accumulated in c.element.names will be checked during action sanitization
- // to ensure that they do not lead to different sanitization contexts.
- return c
- }
-
- c = a
- c.attr.name = b.attr.name
- if c.eq(b) {
- // The contexts differ only by their attribute name. The attribute names from the conditional
- // branches that are accumulated in c.attr.names will be checked during action sanitization
- // to ensure that they do not lead to different sanitization contexts.
- return c
- }
-
- // Allow a nudged context to join with an unnudged one.
- // This means that
- // <p title={{if .C}}{{.}}{{end}}
- // ends in an unquoted value state even though the else branch
- // ends in stateBeforeValue.
- if c, d := nudge(a), nudge(b); !(c.eq(a) && d.eq(b)) {
- if e := join(c, d, node, nodeName); e.state != stateError {
- return e
- }
- }
-
- return context{
- state: stateError,
- err: errorf(ErrBranchEnd, node, 0, "{{%s}} branches end in different contexts: %v, %v", nodeName, a, b),
- }
-}
-
-// joinNames returns the slice of all possible names that an element or attr could
-// assume after context joining the element or attr containing aName and aNames with the
-// element or attr containing bName and bNames.
-func joinNames(aName, bName string, aNames, bNames []string) []string {
- var ret []string
- if aName != bName {
- ret = append(ret, aName, bName)
- }
- aNamesSet := make(map[string]bool)
- for _, name := range aNames {
- aNamesSet[name] = true
- }
- for _, name := range bNames {
- if !aNamesSet[name] {
- ret = append(ret, name)
- }
- }
- return ret
-}
-
-// escapeBranch escapes a branch template node: "if", "range" and "with".
-func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context {
- c0 := e.escapeList(c, n.List)
- if nodeName == "range" && c0.state != stateError {
- // The "true" branch of a "range" node can execute multiple times.
- // We check that executing n.List once results in the same context
- // as executing n.List twice.
- c1, _ := e.escapeListConditionally(c0, n.List, nil)
- c0 = join(c0, c1, n, nodeName)
- if c0.state == stateError {
- // Make clear that this is a problem on loop re-entry
- // since developers tend to overlook that branch when
- // debugging templates.
- c0.err.Line = n.Line
- c0.err.Description = "on range loop re-entry: " + c0.err.Description
- return c0
- }
- }
- c1 := e.escapeList(c, n.ElseList)
- return join(c0, c1, n, nodeName)
-}
-
-// escapeList escapes a list template node.
-func (e *escaper) escapeList(c context, n *parse.ListNode) context {
- if n == nil {
- return c
- }
- for _, m := range n.Nodes {
- c = e.escape(c, m)
- }
- return c
-}
-
-// escapeListConditionally escapes a list node but only preserves edits and
-// inferences in e if the inferences and output context satisfy filter.
-// It returns the best guess at an output context, and the result of the filter
-// which is the same as whether e was updated.
-func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) {
- e1 := makeEscaper(e.ns)
- // Make type inferences available to f.
- for k, v := range e.output {
- e1.output[k] = v
- }
- c = e1.escapeList(c, n)
- ok := filter != nil && filter(&e1, c)
- if ok {
- // Copy inferences and edits from e1 back into e.
- for k, v := range e1.output {
- e.output[k] = v
- }
- for k, v := range e1.derived {
- e.derived[k] = v
- }
- for k, v := range e1.called {
- e.called[k] = v
- }
- for k, v := range e1.actionNodeEdits {
- e.editActionNode(k, v)
- }
- for k, v := range e1.templateNodeEdits {
- e.editTemplateNode(k, v)
- }
- for k, v := range e1.textNodeEdits {
- e.editTextNode(k, v)
- }
- }
- return c, ok
-}
-
-// escapeTemplate escapes a {{template}} call node.
-func (e *escaper) escapeTemplate(c context, n *parse.TemplateNode) context {
- c, name := e.escapeTree(c, n, n.Name, n.Line)
- if name != n.Name {
- e.editTemplateNode(n, name)
- }
- return c
-}
-
-// mangle produces an identifier that includes a suffix that distinguishes it
-// from template names mangled with different contexts.
-func mangle(c context, templateName string) string {
- // The mangled name for the default context is the input templateName.
- if c.state == stateText {
- return templateName
- }
- s := templateName + "$htmltemplate_" + c.state.String()
- if c.delim != 0 {
- s += "_" + c.delim.String()
- }
- if c.attr.name != "" {
- s += "_" + c.attr.String()
- }
- if c.element.name != "" {
- s += "_" + c.element.String()
- }
- return s
-}
-
-// escapeTree escapes the named template starting in the given context as
-// necessary and returns its output context.
-func (e *escaper) escapeTree(c context, node parse.Node, name string, line int) (context, string) {
- // Mangle the template name with the input context to produce a reliable
- // identifier.
- dname := mangle(c, name)
- e.called[dname] = true
- if out, ok := e.output[dname]; ok {
- // Already escaped.
- return out, dname
- }
- t := e.template(name)
- if t == nil {
- // Two cases: The template exists but is empty, or has never been mentioned at
- // all. Distinguish the cases in the error messages.
- if e.ns.set[name] != nil {
- return context{
- state: stateError,
- err: errorf(ErrNoSuchTemplate, node, line, "%q is an incomplete or empty template", name),
- }, dname
- }
- return context{
- state: stateError,
- err: errorf(ErrNoSuchTemplate, node, line, "no such template %q", name),
- }, dname
- }
- if dname != name {
- // Use any template derived during an earlier call to escapeTemplate
- // with different top level templates, or clone if necessary.
- dt := e.template(dname)
- if dt == nil {
- dt = template.New(dname)
- dt.Tree = t.Tree.Copy()
- dt.Tree.Name = dname
- e.derived[dname] = dt
- }
- t = dt
- }
- return e.computeOutCtx(c, t), dname
-}
-
-// computeOutCtx takes a template and its start context and computes the output
-// context while storing any inferences in e.
-func (e *escaper) computeOutCtx(c context, t *template.Template) context {
- // Propagate context over the body.
- c1, ok := e.escapeTemplateBody(c, t)
- if !ok {
- // Look for a fixed point by assuming c1 as the output context.
- if c2, ok2 := e.escapeTemplateBody(c1, t); ok2 {
- c1, ok = c2, true
- }
- // Use c1 as the error context if neither assumption worked.
- }
- if !ok && c1.state != stateError {
- return context{
- state: stateError,
- err: errorf(ErrOutputContext, t.Tree.Root, 0, "cannot compute output context for template %s", t.Name()),
- }
- }
- return c1
-}
-
-// escapeTemplateBody escapes the given template assuming the given output
-// context, and returns the best guess at the output context and whether the
-// assumption was correct.
-func (e *escaper) escapeTemplateBody(c context, t *template.Template) (context, bool) {
- filter := func(e1 *escaper, c1 context) bool {
- if c1.state == stateError {
- // Do not update the input escaper, e.
- return false
- }
- if !e1.called[t.Name()] {
- // If t is not recursively called, then c1 is an
- // accurate output context.
- return true
- }
- // c1 is accurate if it matches our assumed output context.
- return c.eq(c1)
- }
- // We need to assume an output context so that recursive template calls
- // take the fast path out of escapeTree instead of infinitely recursing.
- // Naively assuming that the input context is the same as the output
- // works >90% of the time.
- e.output[t.Name()] = c
- return e.escapeListConditionally(c, t.Tree.Root, filter)
-}
-
-// delimEnds maps each delim to a string of characters that terminate it.
-var delimEnds = [...]string{
- delimDoubleQuote: `"`,
- delimSingleQuote: "'",
- // Determined empirically by running the below in various browsers.
- // var div = document.createElement("DIV");
- // for (var i = 0; i < 0x10000; ++i) {
- // div.innerHTML = "<span title=x" + String.fromCharCode(i) + "-bar>";
- // if (div.getElementsByTagName("SPAN")[0].title.indexOf("bar") < 0)
- // document.write("<p>U+" + i.toString(16));
- // }
- delimSpaceOrTagEnd: " \t\n\f\r>",
-}
-
-var doctypeBytes = []byte("<!DOCTYPE")
-
-// escapeText escapes a text template node.
-func (e *escaper) escapeText(c context, n *parse.TextNode) context {
- s, written, i, b := n.Text, 0, 0, new(bytes.Buffer)
- if e.ns.cspCompatible && bytes.Contains(s, []byte("javascript:")) {
- // This substring search is not perfect, but it is unlikely that this substring will
- // exist in template text for any other reason than to specify a javascript URI.
- return context{
- state: stateError,
- err: errorf(ErrCSPCompatibility, n, 0, `"javascript:" URI disallowed for CSP compatibility`),
- }
- }
- for i != len(s) {
- if e.ns.cspCompatible && strings.HasPrefix(c.attr.name, "on") {
- return context{
- state: stateError,
- err: errorf(ErrCSPCompatibility, n, 0, "inline event handler %q is disallowed for CSP compatibility", c.attr.name),
- }
- }
- c1, nread := contextAfterText(c, s[i:])
- i1 := i + nread
- sc, err := sanitizationContextForElementContent(c.element.name)
- if c.state == stateText || err == nil && sc == sanitizationContextRCDATA {
- end := i1
- if c1.state != c.state {
- for j := end - 1; j >= i; j-- {
- if s[j] == '<' {
- end = j
- break
- }
- }
- }
- for j := i; j < end; j++ {
- if s[j] == '<' && !bytes.HasPrefix(bytes.ToUpper(s[j:]), doctypeBytes) {
- b.Write(s[written:j])
- b.WriteString("&lt;")
- written = j + 1
- }
- }
- } else if isComment(c.state) && c.delim == delimNone {
- written = i1
- }
- if c.state == stateSpecialElementBody && c.element.name == "script" {
- if err := isJsTemplateBalanced(bytes.NewBuffer(s)); err != nil {
- return context{
- state: stateError,
- err: errorf(ErrUnbalancedJsTemplate, n, 0, "Mixing template systems can cause security vulnerabilites. Therefore, there can be no safehtml/template insertion points or actions inside an ES6 template, and all ES6 templates must be closed: %v", err.Error()),
- }
- }
- }
-
- if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone {
- // Preserve the portion between written and the comment start.
- cs := i1 - 2
- if c1.state == stateHTMLCmt {
- // "<!--" instead of "/*" or "//"
- cs -= 2
- }
- b.Write(s[written:cs])
- written = i1
- }
- if i == i1 && c.state == c1.state {
- panic(fmt.Sprintf("infinite loop from %v to %v on %q..%q", c, c1, s[:i], s[i:]))
- }
- c, i = c1, i1
- }
-
- if written != 0 && c.state != stateError {
- if !isComment(c.state) || c.delim != delimNone {
- b.Write(n.Text[written:])
- }
- e.editTextNode(n, b.Bytes())
- }
- return c
-}
-
-// contextAfterText starts in context c, consumes some tokens from the front of
-// s, then returns the context after those tokens and the unprocessed suffix.
-func contextAfterText(c context, s []byte) (context, int) {
- if c.delim == delimNone {
- c1, i := tSpecialTagEnd(c, s)
- if i == 0 {
- // A special end tag (`</script>`) has been seen and
- // all content preceding it has been consumed.
- return c1, 0
- }
- // Consider all content up to any end tag.
- return transitionFunc[c.state](c, s[:i])
- }
-
- // We are at the beginning of an attribute value.
-
- i := bytes.IndexAny(s, delimEnds[c.delim])
- if i == -1 {
- i = len(s)
- }
- if c.delim == delimSpaceOrTagEnd {
- // http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state
- // lists the runes below as error characters.
- // Error out because HTML parsers may differ on whether
- // "<a id= onclick=f(" ends inside id's or onclick's value,
- // "<a class=`foo " ends inside a value,
- // "<a style=font:'Arial'" needs open-quote fixup.
- // IE treats '`' as a quotation character.
- if j := bytes.IndexAny(s[:i], "\"'<=`"); j >= 0 {
- return context{
- state: stateError,
- err: errorf(ErrBadHTML, nil, 0, "%q in unquoted attr: %q", s[j:j+1], s[:i]),
- }, len(s)
- }
- }
- if i == len(s) {
- c.attr.value += string(s)
- // Remain inside the attribute.
- // Decode the value so non-HTML rules can easily handle
- // <button onclick="alert(&quot;Hi!&quot;)">
- // without having to entity decode token boundaries.
- for u := []byte(html.UnescapeString(string(s))); len(u) != 0; {
- c1, i1 := transitionFunc[c.state](c, u)
- c, u = c1, u[i1:]
- }
- return c, len(s)
- }
-
- // On exiting an attribute, we discard all state information
- // except the state, element, scriptType, and linkRel.
- ret := context{
- state: stateTag,
- element: c.element,
- scriptType: c.scriptType,
- linkRel: c.linkRel,
- }
- // Save the script element's type attribute value if we are parsing it for the first time.
- if c.state == stateAttr && c.element.name == "script" && c.attr.name == "type" {
- ret.scriptType = strings.ToLower(string(s[:i]))
- }
- // Save the link element's rel attribute value if we are parsing it for the first time.
- if c.state == stateAttr && c.element.name == "link" && c.attr.name == "rel" {
- ret.linkRel = " " + strings.Join(strings.Fields(strings.TrimSpace(strings.ToLower(string(s[:i])))), " ") + " "
- }
- if c.delim != delimSpaceOrTagEnd {
- // Consume any quote.
- i++
- }
- return ret, i
-}
-
-// editActionNode records a change to an action pipeline for later commit.
-func (e *escaper) editActionNode(n *parse.ActionNode, cmds []string) {
- if _, ok := e.actionNodeEdits[n]; ok {
- panic(fmt.Sprintf("node %s shared between templates", n))
- }
- e.actionNodeEdits[n] = cmds
-}
-
-// editTemplateNode records a change to a {{template}} callee for later commit.
-func (e *escaper) editTemplateNode(n *parse.TemplateNode, callee string) {
- if _, ok := e.templateNodeEdits[n]; ok {
- panic(fmt.Sprintf("node %s shared between templates", n))
- }
- e.templateNodeEdits[n] = callee
-}
-
-// editTextNode records a change to a text node for later commit.
-func (e *escaper) editTextNode(n *parse.TextNode, text []byte) {
- if _, ok := e.textNodeEdits[n]; ok {
- panic(fmt.Sprintf("node %s shared between templates", n))
- }
- e.textNodeEdits[n] = text
-}
-
-// commit applies changes to actions and template calls needed to contextually
-// autoescape content and adds any derived templates to the set.
-func (e *escaper) commit() {
- for name := range e.output {
- e.template(name).Funcs(funcs)
- }
- // Any template from the name space associated with this escaper can be used
- // to add derived templates to the underlying text/template name space.
- tmpl := e.arbitraryTemplate()
- for _, t := range e.derived {
- if _, err := tmpl.text.AddParseTree(t.Name(), t.Tree); err != nil {
- panic("error adding derived template")
- }
- }
- for n, s := range e.actionNodeEdits {
- ensurePipelineContains(n.Pipe, s)
- }
- for n, name := range e.templateNodeEdits {
- n.Name = name
- }
- for n, s := range e.textNodeEdits {
- n.Text = s
- }
- // Reset state that is specific to this commit so that the same changes are
- // not re-applied to the template on subsequent calls to commit.
- e.called = make(map[string]bool)
- e.actionNodeEdits = make(map[*parse.ActionNode][]string)
- e.templateNodeEdits = make(map[*parse.TemplateNode]string)
- e.textNodeEdits = make(map[*parse.TextNode][]byte)
-}
-
-// template returns the named template given a mangled template name.
-func (e *escaper) template(name string) *template.Template {
- // Any template from the name space associated with this escaper can be used
- // to look up templates in the underlying text/template name space.
- t := e.arbitraryTemplate().text.Lookup(name)
- if t == nil {
- t = e.derived[name]
- }
- return t
-}
-
-// arbitraryTemplate returns an arbitrary template from the name space
-// associated with e and panics if no templates are found.
-func (e *escaper) arbitraryTemplate() *Template {
- for _, t := range e.ns.set {
- return t
- }
- panic("no templates in name space")
-}
-
-var (
- errorType = reflect.TypeOf((*error)(nil)).Elem()
- fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
-)
-
-// indirectToStringerOrError returns the value, after dereferencing as many times
-// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
-// or error,
-func indirectToStringerOrError(a interface{}) interface{} {
- if a == nil {
- return nil
- }
- v := reflect.ValueOf(a)
- for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
- v = v.Elem()
- }
- return v.Interface()
-}
-
-var (
- jsTemplateSeparator = []byte("`")
- jsTemplateExprStart = []byte("${")
- jsTemplateExprEnd = []byte("}")
-)
-
-// Determine if a string has unbalanced (open) JS templates.
-func isJsTemplateBalanced(s *bytes.Buffer) error {
- for {
- index := bytes.Index(s.Bytes(), jsTemplateSeparator)
- if index == -1 {
- return nil
- }
- s.Next(index + 1)
- err := consumeJsTemplate(s)
- if err != nil {
- return err
- }
- }
-}
-
-// s is a JS template string (without the opening `); this function consumes s up to
-// the matching closing `.
-func consumeJsTemplate(s *bytes.Buffer) error {
- for {
- templateEnd := bytes.Index(s.Bytes(), jsTemplateSeparator)
- exprStart := bytes.Index(s.Bytes(), jsTemplateExprStart)
- if templateEnd == -1 {
- return fmt.Errorf("Missing closing ` in JS template")
- }
- if exprStart != -1 && exprStart < templateEnd {
- err := consumeJsTemplateExpr(s)
- if err != nil {
- return err
- }
- return consumeJsTemplate(s)
- } else {
- // The template expression occurs after this template, e.g. "`foo``bar${test}`".
- s.Next(templateEnd + 1)
- return nil
- }
- }
-}
-
-// s is a Js Template expression (starting with "${"). This function consumes up to and including the matching closing "}".
-func consumeJsTemplateExpr(s *bytes.Buffer) error {
- for {
- exprEnd := bytes.Index(s.Bytes(), jsTemplateExprEnd)
- if exprEnd == -1 {
- // Template expression isn't closed
- return fmt.Errorf("Missing closing } in JS template")
- }
- nestedTemplateStart := bytes.Index(s.Bytes(), jsTemplateSeparator)
- if nestedTemplateStart != -1 && nestedTemplateStart < exprEnd {
- s.Next(nestedTemplateStart + 1)
- err := consumeJsTemplate(s)
- if err != nil {
- return err
- }
- return consumeJsTemplateExpr(s)
- } else {
- s.Next(exprEnd + 1)
- return nil
- }
- }
-}
diff --git a/vendor/github.com/google/safehtml/template/init.go b/vendor/github.com/google/safehtml/template/init.go
deleted file mode 100644
index c9fa2de1d..000000000
--- a/vendor/github.com/google/safehtml/template/init.go
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (c) 2017 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 or at
-// https://developers.google.com/open-source/licenses/bsd
-
-package template
-
-import (
- "github.com/google/safehtml/internal/template/raw"
-)
-
-// The following functions are used by package uncheckedconversions
-// (via package raw) to create TrustedSource and TrustedTemplate values
-// from plain strings.
-
-func trustedSourceRaw(s string) TrustedSource {
- return TrustedSource{s}
-}
-
-func trustedTemplateRaw(s string) TrustedTemplate {
- return TrustedTemplate{s}
-}
-
-func init() {
- raw.TrustedSource = trustedSourceRaw
- raw.TrustedTemplate = trustedTemplateRaw
-}
diff --git a/vendor/github.com/google/safehtml/template/sanitize.go b/vendor/github.com/google/safehtml/template/sanitize.go
deleted file mode 100644
index c75e345e1..000000000
--- a/vendor/github.com/google/safehtml/template/sanitize.go
+++ /dev/null
@@ -1,258 +0,0 @@
-// 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 (
- "fmt"
- "regexp"
- "strings"
-)
-
-// sanitizerForContext returns an ordered list of function names that will be called to
-// sanitize data values found in the HTML context defined by c.
-func sanitizerForContext(c context) ([]string, error) {
- switch c.state {
- case stateTag, stateAttrName, stateAfterName:
- return nil, fmt.Errorf("actions must not affect element or attribute names")
- case stateHTMLCmt:
- return []string{sanitizeHTMLCommentFuncName}, nil
- }
- if len(c.element.names) == 0 && c.element.name == "" && c.state == stateText {
- // Not in an HTML element.
- return []string{sanitizeHTMLFuncName}, nil
- }
- if c.attr.name != "" || len(c.attr.names) > 0 {
- // We are in an attribute value context.
- if c.delim != delimDoubleQuote && c.delim != delimSingleQuote {
- // TODO: consider disallowing single-quoted or unquoted attribute values completely, even in hardcoded template text.
- return nil, fmt.Errorf("unquoted attribute values disallowed")
- }
- return sanitizersForAttributeValue(c)
- }
- // Otherwise, we are in an element content context.
- elementContentSanitizer, err := sanitizerForElementContent(c)
- return appendIfNotEmpty([]string{}, elementContentSanitizer), err
-}
-
-// appendIfNotEmpty appends the given strings that are non-empty to the given slice.
-func appendIfNotEmpty(slice []string, strings ...string) []string {
- for _, s := range strings {
- if s != "" {
- slice = append(slice, s)
- }
- }
- return slice
-}
-
-// sanitizersForAttributeValue returns a list of names of functions that will be
-// called in order to sanitize data values found the HTML attribtue value context c.
-func sanitizersForAttributeValue(c context) ([]string, error) {
- // Ensure that all combinations of element and attribute names for this context results
- // in the same attribute value sanitization context.
- var elems, attrs []string
- if len(c.element.names) == 0 {
- elems = []string{c.element.name}
- } else {
- elems = c.element.names
- }
- if len(c.attr.names) == 0 {
- attrs = []string{c.attr.name}
- } else {
- attrs = c.attr.names
- }
- var sc0 sanitizationContext
- var elem0, attr0 string
- for i, elem := range elems {
- for j, attr := range attrs {
- sc, err := sanitizationContextForAttrVal(elem, attr, c.linkRel)
- if err != nil {
- if len(elems) == 1 && len(attrs) == 1 {
- return nil, err
- }
- return nil, fmt.Errorf(`conditional branch with {element=%q, attribute=%q} results in sanitization error: %s`, elem, attr, err)
- }
- if i == 0 && j == 0 {
- sc0, elem0, attr0 = sc, elem, attr
- continue
- }
- if sc != sc0 {
- return nil, fmt.Errorf(
- `conditional branches end in different attribute value sanitization contexts: {element=%q, attribute=%q} has sanitization context %q, {element=%q, attribute=%q} has sanitization context %q`,
- elem0, attr0, sc0, elem, attr, sc)
- }
- }
- }
- if sc0.isEnum() && c.attr.value != "" {
- return nil, fmt.Errorf("partial substitutions are disallowed in the %q attribute value context of a %q element", c.attr.name, c.element.name)
- }
- if sc0 == sanitizationContextStyle && c.attr.value != "" {
- if err := validateDoesNotEndsWithCharRefPrefix(c.attr.value); err != nil {
- return nil, fmt.Errorf("action cannot be interpolated into the %q attribute value of this %q element: %s", c.attr.name, c.element.name, err)
- }
- }
- // ret is a stack of sanitizer names that will be built in reverse.
- var ret []string
- // All attribute values must be HTML-escaped at run time by sanitizeHTML to eliminate
- // any HTML markup that can cause the HTML parser to transition out of the attribute value state.
- // These attribute values will later be HTML-unescaped by the HTML parser in the browser.
- ret = append(ret, sanitizeHTMLFuncName)
- sanitizer := sc0.sanitizerName()
- if !sc0.isURLorTrustedResourceURL() {
- return reverse(appendIfNotEmpty(ret, sanitizer)), nil
- }
- urlAttrValPrefix := c.attr.value
- if urlAttrValPrefix == "" {
- // Attribute value prefixes in URL or TrustedResourceURL sanitization contexts
- // must sanitized and normalized.
- return reverse(appendIfNotEmpty(ret, normalizeURLFuncName, sanitizer)), nil
- }
- // Action occurs after a URL or TrustedResourceURL prefix.
- if c.attr.ambiguousValue {
- return nil, fmt.Errorf("actions must not occur after an ambiguous URL prefix in the %q attribute value context of a %q element", c.attr.name, c.element.name)
- }
- validator, ok := urlPrefixValidators[sc0]
- if !ok {
- return nil, fmt.Errorf("cannot validate attribute value prefix %q in the %q sanitization context", c.attr.value, sc0)
- }
- if err := validator(c.attr.value); err != nil {
- return nil, fmt.Errorf("action cannot be interpolated into the %q URL attribute value of this %q element: %s", c.attr.name, c.element.name, err)
- }
- switch {
- case sc0 == sanitizationContextTrustedResourceURL:
- // Untrusted data that occurs anywhere after TrustedResourceURL prefix must be query-escaped
- // to prevent the injection of any new path segments or URL components. Moreover, they must
- // not contain any ".." dot-segments.
- ret = append(ret, queryEscapeURLFuncName, validateTrustedResourceURLSubstitutionFuncName)
- case strings.ContainsAny(urlAttrValPrefix, "#?"):
- // For URLs, we only escape in the query or fragment part to prevent the injection of new query
- // parameters or fragments.
- ret = append(ret, queryEscapeURLFuncName)
- default:
- ret = append(ret, normalizeURLFuncName)
- }
- return reverse(ret), nil
-}
-
-// reverse reverses s and returns it.
-func reverse(s []string) []string {
- for head, tail := 0, len(s)-1; head < tail; head, tail = head+1, tail-1 {
- s[head], s[tail] = s[tail], s[head]
- }
- return s
-}
-
-// sanitizationContextForAttrVal returns the sanitization context for attr when it
-// appears within element.
-func sanitizationContextForAttrVal(element, attr, linkRel string) (sanitizationContext, error) {
- if element == "link" && attr == "href" {
- // Special case: safehtml.URL values are allowed in a link element's href attribute if that element's
- // rel attribute possesses certain values.
- relVals := strings.Fields(linkRel)
- for _, val := range relVals {
- if urlLinkRelVals[val] {
- return sanitizationContextTrustedResourceURLOrURL, nil
- }
- }
- }
- if dataAttributeNamePattern.MatchString(attr) {
- // Special case: data-* attributes are specified by HTML5 to hold custom data private to
- // the page or application; they should not be interpreted by browsers. Therefore, no
- // sanitization is required for these attribute values.
- return sanitizationContextNone, nil
- }
- if sc, ok := elementSpecificAttrValSanitizationContext[attr][element]; ok {
- return sc, nil
- }
- sc, isAllowedAttr := globalAttrValSanitizationContext[attr]
- _, isAllowedElement := elementContentSanitizationContext[element]
- if isAllowedAttr && (isAllowedElement || allowedVoidElements[element]) {
- // Only sanitize attributes that appear in elements whose semantics are known.
- // Thes attributes might have different semantics in other standard or custom
- // elements that our sanitization policy does not handle correctly.
- return sc, nil
- }
- return 0, fmt.Errorf("actions must not occur in the %q attribute value context of a %q element", attr, element)
-}
-
-// dataAttributeNamePattern matches valid data attribute names.
-// This pattern is conservative and matches only a subset of the valid names defined in
-// https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
-var dataAttributeNamePattern = regexp.MustCompile(`^data-[a-z_][-a-z0-9_]*$`)
-
-// endsWithCharRefPrefixPattern matches strings that end in an incomplete
-// HTML character reference.
-//
-// See https://html.spec.whatwg.org/multipage/syntax.html#character-references.
-var endsWithCharRefPrefixPattern = regexp.MustCompile(
- `&(?:[[:alpha:]][[:alnum:]]*|#(?:[xX][[:xdigit:]]*|[[:digit:]]*))?$`)
-
-// validateDoesNotEndsWithCharRefPrefix returns an error only if the given prefix ends
-// with an incomplete HTML character reference.
-func validateDoesNotEndsWithCharRefPrefix(prefix string) error {
- if endsWithCharRefPrefixPattern.MatchString(prefix) {
- return fmt.Errorf(`prefix %q ends with an incomplete HTML character reference; did you mean "&amp;" instead of "&"?`, prefix)
- }
- return nil
-}
-
-// sanitizerForElementContent returns the name of the function that will be called
-// to sanitize data values found in the HTML element content context c.
-func sanitizerForElementContent(c context) (string, error) {
- // Ensure that all other possible element names for this context result in the same
- // element content sanitization context.
- var elems []string
- if len(c.element.names) == 0 {
- elems = []string{c.element.name}
- } else {
- elems = c.element.names
- }
- var sc0 sanitizationContext
- var elem0 string
- for i, elem := range elems {
- var sc sanitizationContext
- var err error
- if elem == "" {
- // Special case: an empty element name represents a context outside of a HTML element.
- sc = sanitizationContextHTML
- } else {
- sc, err = sanitizationContextForElementContent(elem)
- }
- if err != nil {
- if len(elems) == 1 {
- return "", err
- }
- return "", fmt.Errorf(`conditional branch with element %q results in sanitization error: %s`, elem, err)
- }
- if i == 0 {
- sc0, elem0 = sc, elem
- continue
- }
- if sc != sc0 {
- return "",
- fmt.Errorf(`conditional branches end in different element content sanitization contexts: element %q has sanitization context %q, element %q has sanitization context %q`,
- elem0, sc0, elem, sc)
- }
- }
- return sc0.sanitizerName(), nil
-}
-
-// sanitizationContextForElementContent returns the element content sanitization context for the given element.
-func sanitizationContextForElementContent(element string) (sanitizationContext, error) {
- sc, ok := elementContentSanitizationContext[element]
- if !ok {
- return 0, fmt.Errorf("actions must not occur in the element content context of a %q element", element)
- }
- return sc, nil
-}
-
-// sanitizeHTMLComment returns the empty string regardless of input.
-// Comment content does not correspond to any parsed structure or
-// human-readable content, so the simplest and most secure policy is to drop
-// content interpolated into comments.
-// This approach is equally valid whether or not static comment content is
-// removed from the template.
-func sanitizeHTMLComment(_ ...interface{}) string {
- return ""
-}
diff --git a/vendor/github.com/google/safehtml/template/sanitizers.go b/vendor/github.com/google/safehtml/template/sanitizers.go
deleted file mode 100644
index 782e931b8..000000000
--- a/vendor/github.com/google/safehtml/template/sanitizers.go
+++ /dev/null
@@ -1,599 +0,0 @@
-// Copyright (c) 2017 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 or at
-// https://developers.google.com/open-source/licenses/bsd
-
-package template
-
-import (
- "fmt"
- "text/template"
-
- "github.com/google/safehtml/internal/safehtmlutil"
- "github.com/google/safehtml"
-)
-
-// sanitizationContext determines what type of sanitization to perform
-// on a template action.
-type sanitizationContext uint8
-
-const (
- _ = iota
- sanitizationContextAsyncEnum
- sanitizationContextDirEnum
- sanitizationContextHTML
- sanitizationContextHTMLValOnly
- sanitizationContextIdentifier
- sanitizationContextLoadingEnum
- sanitizationContextNone
- sanitizationContextRCDATA
- sanitizationContextScript
- sanitizationContextStyle
- sanitizationContextStyleSheet
- sanitizationContextTargetEnum
- sanitizationContextTrustedResourceURL
- sanitizationContextTrustedResourceURLOrURL
- sanitizationContextURL
- sanitizationContextURLSet
-)
-
-// String returns the string representation of sanitizationContext s.
-func (s sanitizationContext) String() string {
- if int(s) >= len(sanitizationContextInfo) {
- return fmt.Sprintf("invalid sanitization context %d", s)
- }
- return sanitizationContextInfo[s].name
-}
-
-// sanitizerName returns the name of the sanitizer to call in sanitizationContext s.
-// It returns an empty string if no sanitization is required in s.
-func (s sanitizationContext) sanitizerName() string {
- if int(s) >= len(sanitizationContextInfo) {
- return fmt.Sprintf("invalid sanitization context %d", s)
- }
- return sanitizationContextInfo[s].sanitizerName
-}
-
-// isEnum reports reports whether s is a sanitization context for enumerated values.
-func (s sanitizationContext) isEnum() bool {
- return s == sanitizationContextAsyncEnum || s == sanitizationContextDirEnum || s == sanitizationContextLoadingEnum || s == sanitizationContextTargetEnum
-}
-
-// isURLorTrustedResourceURL reports reports whether s is a sanitization context for URL or TrustedResourceURL values.
-func (s sanitizationContext) isURLorTrustedResourceURL() bool {
- return s == sanitizationContextTrustedResourceURL || s == sanitizationContextTrustedResourceURLOrURL || s == sanitizationContextURL
-}
-
-// sanitizationContextInfo[x] contains the name for sanitization context x and the
-// name of the sanitizer to call in that context.
-// If sanitizationContextInfo[x].sanitizerName is empty, then no sanitizer needs
-// to be called in x.
-var sanitizationContextInfo = [...]struct {
- name, sanitizerName string
-}{
- sanitizationContextAsyncEnum: {"AsyncEnum", sanitizeAsyncEnumFuncName},
- sanitizationContextDirEnum: {"DirEnum", sanitizeDirEnumFuncName},
- sanitizationContextHTML: {"HTML", sanitizeHTMLFuncName},
- sanitizationContextHTMLValOnly: {"HTMLValOnly", sanitizeHTMLValOnlyFuncName},
- sanitizationContextIdentifier: {"Identifier", sanitizeIdentifierFuncName},
- sanitizationContextLoadingEnum: {"LoadingEnum", sanitizeLoadingEnumFuncName},
- sanitizationContextNone: {"None", ""},
- sanitizationContextRCDATA: {"RCDATA", sanitizeRCDATAFuncName},
- sanitizationContextScript: {"Script", sanitizeScriptFuncName},
- sanitizationContextStyle: {"Style", sanitizeStyleFuncName},
- sanitizationContextStyleSheet: {"StyleSheet", sanitizeStyleSheetFuncName},
- sanitizationContextTargetEnum: {"TargetEnum", sanitizeTargetEnumFuncName},
- sanitizationContextTrustedResourceURL: {"TrustedResourceURL", sanitizeTrustedResourceURLFuncName},
- sanitizationContextTrustedResourceURLOrURL: {"TrustedResourceURLOrURL", sanitizeTrustedResourceURLOrURLFuncName},
- sanitizationContextURL: {"URL", sanitizeURLFuncName},
- sanitizationContextURLSet: {"URLSet", sanitizeURLSetFuncName},
-}
-
-var funcs = template.FuncMap{
- queryEscapeURLFuncName: safehtmlutil.QueryEscapeURL,
- normalizeURLFuncName: safehtmlutil.NormalizeURL,
- validateTrustedResourceURLSubstitutionFuncName: validateTrustedResourceURLSubstitution,
- evalArgsFuncName: evalArgs,
- sanitizeHTMLCommentFuncName: sanitizeHTMLComment,
- sanitizeAsyncEnumFuncName: sanitizeAsyncEnum,
- sanitizeDirEnumFuncName: sanitizeDirEnum,
- sanitizeHTMLFuncName: sanitizeHTML,
- sanitizeHTMLValOnlyFuncName: sanitizeHTMLValOnly,
- sanitizeIdentifierFuncName: sanitizeIdentifier,
- sanitizeLoadingEnumFuncName: sanitizeLoadingEnum,
- sanitizeRCDATAFuncName: sanitizeRCDATA,
- sanitizeScriptFuncName: sanitizeScript,
- sanitizeStyleFuncName: sanitizeStyle,
- sanitizeStyleSheetFuncName: sanitizeStyleSheet,
- sanitizeTargetEnumFuncName: sanitizeTargetEnum,
- sanitizeTrustedResourceURLFuncName: sanitizeTrustedResourceURL,
- sanitizeTrustedResourceURLOrURLFuncName: sanitizeTrustedResourceURLOrURL,
- sanitizeURLFuncName: sanitizeURL,
- sanitizeURLSetFuncName: sanitizeURLSet,
-}
-
-const (
- queryEscapeURLFuncName = "_queryEscapeURL"
- normalizeURLFuncName = "_normalizeURL"
- validateTrustedResourceURLSubstitutionFuncName = "_validateTrustedResourceURLSubstitution"
- evalArgsFuncName = "_evalArgs"
- sanitizeHTMLCommentFuncName = "_sanitizeHTMLComment"
- sanitizeAsyncEnumFuncName = "_sanitizeAsyncEnum"
- sanitizeDirEnumFuncName = "_sanitizeDirEnum"
- sanitizeHTMLFuncName = "_sanitizeHTML"
- sanitizeHTMLValOnlyFuncName = "_sanitizeHTMLValOnly"
- sanitizeIdentifierFuncName = "_sanitizeIdentifier"
- sanitizeLoadingEnumFuncName = "_sanitizeLoadingEnum"
- sanitizeRCDATAFuncName = "_sanitizeRCDATA"
- sanitizeScriptFuncName = "_sanitizeScript"
- sanitizeStyleFuncName = "_sanitizeStyle"
- sanitizeStyleSheetFuncName = "_sanitizeStyleSheet"
- sanitizeTargetEnumFuncName = "_sanitizeTargetEnum"
- sanitizeTrustedResourceURLFuncName = "_sanitizeTrustedResourceURL"
- sanitizeTrustedResourceURLOrURLFuncName = "_sanitizeTrustedResourceURLOrURL"
- sanitizeURLFuncName = "_sanitizeURL"
- sanitizeURLSetFuncName = "_sanitizeURLSet"
-)
-
-// urlLinkRelVals contains values for a link element's rel attribute that indicate that the same link
-// element's href attribute may contain a safehtml.URL value.
-var urlLinkRelVals = map[string]bool{
- "alternate": true,
- "author": true,
- "bookmark": true,
- "canonical": true,
- "cite": true,
- "dns-prefetch": true,
- "help": true,
- "icon": true,
- "license": true,
- "next": true,
- "preconnect": true,
- "prefetch": true,
- "preload": true,
- "prerender": true,
- "prev": true,
- "search": true,
- "subresource": true,
-}
-
-// elementSpecificAttrValSanitizationContext[x][y] is the sanitization context for
-// attribute x when it appears within element y.
-var elementSpecificAttrValSanitizationContext = map[string]map[string]sanitizationContext{
- "accept": {
- "input": sanitizationContextNone,
- },
- "action": {
- "form": sanitizationContextURL,
- },
- "defer": {
- "script": sanitizationContextNone,
- },
- "formaction": {
- "button": sanitizationContextURL,
- "input": sanitizationContextURL,
- },
- "formmethod": {
- "button": sanitizationContextNone,
- "input": sanitizationContextNone,
- },
- "href": {
- "a": sanitizationContextTrustedResourceURLOrURL,
- "area": sanitizationContextTrustedResourceURLOrURL,
- },
- "method": {
- "form": sanitizationContextNone,
- },
- "pattern": {
- "input": sanitizationContextNone,
- },
- "readonly": {
- "input": sanitizationContextNone,
- "textarea": sanitizationContextNone,
- },
- "src": {
- "audio": sanitizationContextTrustedResourceURLOrURL,
- "img": sanitizationContextTrustedResourceURLOrURL,
- "input": sanitizationContextTrustedResourceURLOrURL,
- "source": sanitizationContextTrustedResourceURLOrURL,
- "video": sanitizationContextTrustedResourceURLOrURL,
- },
- "srcdoc": {
- "iframe": sanitizationContextHTMLValOnly,
- },
-}
-
-// globalAttrValSanitizationContext[x] is the sanitization context for attribute x when
-// it appears within any element not in the key set of elementSpecificAttrValSanitizationContext[x].
-var globalAttrValSanitizationContext = map[string]sanitizationContext{
- "align": sanitizationContextNone,
- "alt": sanitizationContextNone,
- "aria-activedescendant": sanitizationContextIdentifier,
- "aria-atomic": sanitizationContextNone,
- "aria-autocomplete": sanitizationContextNone,
- "aria-busy": sanitizationContextNone,
- "aria-checked": sanitizationContextNone,
- "aria-controls": sanitizationContextIdentifier,
- "aria-current": sanitizationContextNone,
- "aria-disabled": sanitizationContextNone,
- "aria-dropeffect": sanitizationContextNone,
- "aria-expanded": sanitizationContextNone,
- "aria-haspopup": sanitizationContextNone,
- "aria-hidden": sanitizationContextNone,
- "aria-invalid": sanitizationContextNone,
- "aria-label": sanitizationContextNone,
- "aria-labelledby": sanitizationContextIdentifier,
- "aria-level": sanitizationContextNone,
- "aria-live": sanitizationContextNone,
- "aria-multiline": sanitizationContextNone,
- "aria-multiselectable": sanitizationContextNone,
- "aria-orientation": sanitizationContextNone,
- "aria-owns": sanitizationContextIdentifier,
- "aria-posinset": sanitizationContextNone,
- "aria-pressed": sanitizationContextNone,
- "aria-readonly": sanitizationContextNone,
- "aria-relevant": sanitizationContextNone,
- "aria-required": sanitizationContextNone,
- "aria-selected": sanitizationContextNone,
- "aria-setsize": sanitizationContextNone,
- "aria-sort": sanitizationContextNone,
- "aria-valuemax": sanitizationContextNone,
- "aria-valuemin": sanitizationContextNone,
- "aria-valuenow": sanitizationContextNone,
- "aria-valuetext": sanitizationContextNone,
- "async": sanitizationContextAsyncEnum,
- "autocapitalize": sanitizationContextNone,
- "autocomplete": sanitizationContextNone,
- "autocorrect": sanitizationContextNone,
- "autofocus": sanitizationContextNone,
- "autoplay": sanitizationContextNone,
- "bgcolor": sanitizationContextNone,
- "border": sanitizationContextNone,
- "cellpadding": sanitizationContextNone,
- "cellspacing": sanitizationContextNone,
- "checked": sanitizationContextNone,
- "cite": sanitizationContextURL,
- "class": sanitizationContextNone,
- "color": sanitizationContextNone,
- "cols": sanitizationContextNone,
- "colspan": sanitizationContextNone,
- "contenteditable": sanitizationContextNone,
- "controls": sanitizationContextNone,
- "datetime": sanitizationContextNone,
- "dir": sanitizationContextDirEnum,
- "disabled": sanitizationContextNone,
- "download": sanitizationContextNone,
- "draggable": sanitizationContextNone,
- "enctype": sanitizationContextNone,
- "face": sanitizationContextNone,
- "for": sanitizationContextIdentifier,
- "formenctype": sanitizationContextNone,
- "frameborder": sanitizationContextNone,
- "height": sanitizationContextNone,
- "hidden": sanitizationContextNone,
- "href": sanitizationContextTrustedResourceURL,
- "hreflang": sanitizationContextNone,
- "id": sanitizationContextIdentifier,
- "ismap": sanitizationContextNone,
- "itemid": sanitizationContextNone,
- "itemprop": sanitizationContextNone,
- "itemref": sanitizationContextNone,
- "itemscope": sanitizationContextNone,
- "itemtype": sanitizationContextNone,
- "label": sanitizationContextNone,
- "lang": sanitizationContextNone,
- "list": sanitizationContextIdentifier,
- "loading": sanitizationContextLoadingEnum,
- "loop": sanitizationContextNone,
- "max": sanitizationContextNone,
- "maxlength": sanitizationContextNone,
- "media": sanitizationContextNone,
- "min": sanitizationContextNone,
- "minlength": sanitizationContextNone,
- "multiple": sanitizationContextNone,
- "muted": sanitizationContextNone,
- "name": sanitizationContextIdentifier,
- "nonce": sanitizationContextNone,
- "open": sanitizationContextNone,
- "placeholder": sanitizationContextNone,
- "poster": sanitizationContextURL,
- "preload": sanitizationContextNone,
- "rel": sanitizationContextNone,
- "required": sanitizationContextNone,
- "reversed": sanitizationContextNone,
- "role": sanitizationContextNone,
- "rows": sanitizationContextNone,
- "rowspan": sanitizationContextNone,
- "selected": sanitizationContextNone,
- "shape": sanitizationContextNone,
- "size": sanitizationContextNone,
- "sizes": sanitizationContextNone,
- "slot": sanitizationContextNone,
- "span": sanitizationContextNone,
- "spellcheck": sanitizationContextNone,
- "src": sanitizationContextTrustedResourceURL,
- "srcset": sanitizationContextURLSet,
- "start": sanitizationContextNone,
- "step": sanitizationContextNone,
- "style": sanitizationContextStyle,
- "summary": sanitizationContextNone,
- "tabindex": sanitizationContextNone,
- "target": sanitizationContextTargetEnum,
- "title": sanitizationContextNone,
- "translate": sanitizationContextNone,
- "type": sanitizationContextNone,
- "valign": sanitizationContextNone,
- "value": sanitizationContextNone,
- "width": sanitizationContextNone,
- "wrap": sanitizationContextNone,
-}
-
-// elementContentSanitizationContext maps element names to element content sanitization contexts.
-var elementContentSanitizationContext = map[string]sanitizationContext{
- "a": sanitizationContextHTML,
- "abbr": sanitizationContextHTML,
- "address": sanitizationContextHTML,
- "article": sanitizationContextHTML,
- "aside": sanitizationContextHTML,
- "audio": sanitizationContextHTML,
- "b": sanitizationContextHTML,
- "bdi": sanitizationContextHTML,
- "bdo": sanitizationContextHTML,
- "blockquote": sanitizationContextHTML,
- "body": sanitizationContextHTML,
- "button": sanitizationContextHTML,
- "canvas": sanitizationContextHTML,
- "caption": sanitizationContextHTML,
- "center": sanitizationContextHTML,
- "cite": sanitizationContextHTML,
- "code": sanitizationContextHTML,
- "colgroup": sanitizationContextHTML,
- "command": sanitizationContextHTML,
- "data": sanitizationContextHTML,
- "datalist": sanitizationContextHTML,
- "dd": sanitizationContextHTML,
- "del": sanitizationContextHTML,
- "details": sanitizationContextHTML,
- "dfn": sanitizationContextHTML,
- "dialog": sanitizationContextHTML,
- "div": sanitizationContextHTML,
- "dl": sanitizationContextHTML,
- "dt": sanitizationContextHTML,
- "em": sanitizationContextHTML,
- "fieldset": sanitizationContextHTML,
- "figcaption": sanitizationContextHTML,
- "figure": sanitizationContextHTML,
- "font": sanitizationContextHTML,
- "footer": sanitizationContextHTML,
- "form": sanitizationContextHTML,
- "frame": sanitizationContextHTML,
- "frameset": sanitizationContextHTML,
- "h1": sanitizationContextHTML,
- "h2": sanitizationContextHTML,
- "h3": sanitizationContextHTML,
- "h4": sanitizationContextHTML,
- "h5": sanitizationContextHTML,
- "h6": sanitizationContextHTML,
- "head": sanitizationContextHTML,
- "header": sanitizationContextHTML,
- "html": sanitizationContextHTML,
- "i": sanitizationContextHTML,
- "iframe": sanitizationContextHTML,
- "ins": sanitizationContextHTML,
- "kbd": sanitizationContextHTML,
- "label": sanitizationContextHTML,
- "legend": sanitizationContextHTML,
- "lh": sanitizationContextHTML,
- "li": sanitizationContextHTML,
- "main": sanitizationContextHTML,
- "map": sanitizationContextHTML,
- "mark": sanitizationContextHTML,
- "menu": sanitizationContextHTML,
- "meter": sanitizationContextHTML,
- "nav": sanitizationContextHTML,
- "noscript": sanitizationContextHTML,
- "ol": sanitizationContextHTML,
- "optgroup": sanitizationContextHTML,
- "option": sanitizationContextHTML,
- "output": sanitizationContextHTML,
- "p": sanitizationContextHTML,
- "picture": sanitizationContextHTML,
- "pre": sanitizationContextHTML,
- "progress": sanitizationContextHTML,
- "q": sanitizationContextHTML,
- "rb": sanitizationContextHTML,
- "rp": sanitizationContextHTML,
- "rt": sanitizationContextHTML,
- "rtc": sanitizationContextHTML,
- "ruby": sanitizationContextHTML,
- "s": sanitizationContextHTML,
- "samp": sanitizationContextHTML,
- "script": sanitizationContextScript,
- "section": sanitizationContextHTML,
- "select": sanitizationContextHTML,
- "slot": sanitizationContextHTML,
- "small": sanitizationContextHTML,
- "span": sanitizationContextHTML,
- "strong": sanitizationContextHTML,
- "style": sanitizationContextStyleSheet,
- "sub": sanitizationContextHTML,
- "summary": sanitizationContextHTML,
- "sup": sanitizationContextHTML,
- "table": sanitizationContextHTML,
- "tbody": sanitizationContextHTML,
- "td": sanitizationContextHTML,
- "textarea": sanitizationContextRCDATA,
- "tfoot": sanitizationContextHTML,
- "th": sanitizationContextHTML,
- "thead": sanitizationContextHTML,
- "time": sanitizationContextHTML,
- "title": sanitizationContextRCDATA,
- "tr": sanitizationContextHTML,
- "u": sanitizationContextHTML,
- "ul": sanitizationContextHTML,
- "var": sanitizationContextHTML,
- "video": sanitizationContextHTML,
-}
-
-// allowedVoidElements is a set of names of void elements actions may appear in.
-var allowedVoidElements = map[string]bool{
- "area": true,
- "br": true,
- "col": true,
- "hr": true,
- "img": true,
- "input": true,
- "link": true,
- "param": true,
- "source": true,
- "track": true,
- "wbr": true,
-}
-
-var sanitizeAsyncEnumValues = map[string]bool{
- "async": true,
-}
-
-func sanitizeAsyncEnum(args ...interface{}) (string, error) {
- input := safehtmlutil.Stringify(args...)
- if sanitizeAsyncEnumValues[input] {
- return input, nil
- }
- return "", fmt.Errorf(`expected one of the following strings: ["async"]`)
-}
-
-var sanitizeDirEnumValues = map[string]bool{
- "auto": true,
- "ltr": true,
- "rtl": true,
-}
-
-func sanitizeDirEnum(args ...interface{}) (string, error) {
- input := safehtmlutil.Stringify(args...)
- if sanitizeDirEnumValues[input] {
- return input, nil
- }
- return "", fmt.Errorf(`expected one of the following strings: ["auto" "ltr" "rtl"]`)
-}
-
-func sanitizeHTML(args ...interface{}) (string, error) {
- if len(args) > 0 {
- if safeTypeValue, ok := safehtmlutil.Indirect(args[0]).(safehtml.HTML); ok {
- return safeTypeValue.String(), nil
- }
- }
- input := safehtmlutil.Stringify(args...)
- return safehtml.HTMLEscaped(input).String(), nil
-}
-
-func sanitizeHTMLValOnly(args ...interface{}) (string, error) {
- if len(args) > 0 {
- if safeTypeValue, ok := safehtmlutil.Indirect(args[0]).(safehtml.HTML); ok {
- return safeTypeValue.String(), nil
- }
- }
- return "", fmt.Errorf(`expected a safehtml.HTML value`)
-}
-
-func sanitizeIdentifier(args ...interface{}) (string, error) {
- if len(args) > 0 {
- if safeTypeValue, ok := safehtmlutil.Indirect(args[0]).(safehtml.Identifier); ok {
- return safeTypeValue.String(), nil
- }
- }
- return "", fmt.Errorf(`expected a safehtml.Identifier value`)
-}
-
-var sanitizeLoadingEnumValues = map[string]bool{
- "eager": true,
- "lazy": true,
-}
-
-func sanitizeLoadingEnum(args ...interface{}) (string, error) {
- input := safehtmlutil.Stringify(args...)
- if sanitizeLoadingEnumValues[input] {
- return input, nil
- }
- return "", fmt.Errorf(`expected one of the following strings: ["eager" "lazy"]`)
-}
-
-func sanitizeRCDATA(args ...interface{}) (string, error) {
- input := safehtmlutil.Stringify(args...)
- return safehtml.HTMLEscaped(input).String(), nil
-}
-
-func sanitizeScript(args ...interface{}) (string, error) {
- if len(args) > 0 {
- if safeTypeValue, ok := safehtmlutil.Indirect(args[0]).(safehtml.Script); ok {
- return safeTypeValue.String(), nil
- }
- }
- return "", fmt.Errorf(`expected a safehtml.Script value`)
-}
-
-func sanitizeStyle(args ...interface{}) (string, error) {
- if len(args) > 0 {
- if safeTypeValue, ok := safehtmlutil.Indirect(args[0]).(safehtml.Style); ok {
- return safeTypeValue.String(), nil
- }
- }
- return "", fmt.Errorf(`expected a safehtml.Style value`)
-}
-
-func sanitizeStyleSheet(args ...interface{}) (string, error) {
- if len(args) > 0 {
- if safeTypeValue, ok := safehtmlutil.Indirect(args[0]).(safehtml.StyleSheet); ok {
- return safeTypeValue.String(), nil
- }
- }
- return "", fmt.Errorf(`expected a safehtml.StyleSheet value`)
-}
-
-var sanitizeTargetEnumValues = map[string]bool{
- "_blank": true,
- "_self": true,
-}
-
-func sanitizeTargetEnum(args ...interface{}) (string, error) {
- input := safehtmlutil.Stringify(args...)
- if sanitizeTargetEnumValues[input] {
- return input, nil
- }
- return "", fmt.Errorf(`expected one of the following strings: ["_blank" "_self"]`)
-}
-
-func sanitizeTrustedResourceURL(args ...interface{}) (string, error) {
- if len(args) > 0 {
- if safeTypeValue, ok := safehtmlutil.Indirect(args[0]).(safehtml.TrustedResourceURL); ok {
- return safeTypeValue.String(), nil
- }
- }
- return "", fmt.Errorf(`expected a safehtml.TrustedResourceURL value`)
-}
-
-func sanitizeTrustedResourceURLOrURL(args ...interface{}) (string, error) {
- if len(args) > 0 {
- switch v := safehtmlutil.Indirect(args[0]).(type) {
- case safehtml.TrustedResourceURL, safehtml.URL:
- return safehtmlutil.Stringify(v), nil
- }
- }
- input := safehtmlutil.Stringify(args...)
- return safehtml.URLSanitized(input).String(), nil
-}
-
-func sanitizeURL(args ...interface{}) (string, error) {
- if len(args) > 0 {
- if safeTypeValue, ok := safehtmlutil.Indirect(args[0]).(safehtml.URL); ok {
- return safeTypeValue.String(), nil
- }
- }
- input := safehtmlutil.Stringify(args...)
- return safehtml.URLSanitized(input).String(), nil
-}
-
-func sanitizeURLSet(args ...interface{}) (string, error) {
- input := safehtmlutil.Stringify(args...)
- return safehtml.URLSetSanitized(input).String(), nil
-}
diff --git a/vendor/github.com/google/safehtml/template/state_string.go b/vendor/github.com/google/safehtml/template/state_string.go
deleted file mode 100644
index afbbf5a0d..000000000
--- a/vendor/github.com/google/safehtml/template/state_string.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Code generated by "stringer -type State"; DO NOT EDIT
-
-package template
-
-import "fmt"
-
-const _State_name = "StateTextStateSpecialElementBodyStateTagStateAttrNameStateAfterNameStateBeforeValueStateHTMLCmtStateAttrStateError"
-
-var _State_index = [...]uint16{0, 9, 32, 40, 53, 67, 83, 95, 104, 114}
-
-func (i state) String() string {
- if i >= state(len(_State_index)-1) {
- return fmt.Sprintf("state(%d)", i)
- }
- return _State_name[_State_index[i]:_State_index[i+1]]
-}
diff --git a/vendor/github.com/google/safehtml/template/template.go b/vendor/github.com/google/safehtml/template/template.go
deleted file mode 100644
index efd0ef610..000000000
--- a/vendor/github.com/google/safehtml/template/template.go
+++ /dev/null
@@ -1,651 +0,0 @@
-// 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 (
- "bytes"
- "fmt"
- "io"
- "io/ioutil"
- "path/filepath"
- "sync"
- "text/template"
- "text/template/parse"
-
- "log"
- "github.com/google/safehtml"
- "github.com/google/safehtml/uncheckedconversions"
-)
-
-// Template is a specialized Template from "text/template" that produces a safe
-// HTML document fragment.
-type Template struct {
- // Sticky error if escaping fails, or errEscapeOK if succeeded.
- escapeErr error
- // We could embed the text/template field, but it's safer not to because
- // we need to keep our version of the name space and the underlying
- // template's in sync.
- text *template.Template
- // The underlying template's parse tree, updated to be HTML-safe.
- Tree *parse.Tree
- *nameSpace // common to all associated templates
-}
-
-// errEscapeOK is a sentinel value used to indicate valid escaping.
-var errEscapeOK = fmt.Errorf("template escaped correctly")
-
-// nameSpace is the data structure shared by all templates in an association.
-type nameSpace struct {
- mu sync.Mutex
- set map[string]*Template
- escaped bool
- // cspCompatible indicates whether inline event handlers and
- // javascript: URIs are disallowed in templates in this namespace.
- cspCompatible bool
- esc escaper
-}
-
-// Templates returns a slice of the templates associated with t, including t
-// itself.
-func (t *Template) Templates() []*Template {
- ns := t.nameSpace
- ns.mu.Lock()
- defer ns.mu.Unlock()
- // Return a slice so we don't expose the map.
- m := make([]*Template, 0, len(ns.set))
- for _, v := range ns.set {
- m = append(m, v)
- }
- return m
-}
-
-// Option sets options for the template. Options are described by
-// strings, either a simple string or "key=value". There can be at
-// most one equals sign in an option string. If the option string
-// is unrecognized or otherwise invalid, Option panics.
-//
-// Known options:
-//
-// missingkey: Control the behavior during execution if a map is
-// indexed with a key that is not present in the map.
-// "missingkey=default" or "missingkey=invalid"
-// The default behavior: Do nothing and continue execution.
-// If printed, the result of the index operation is the string
-// "<no value>".
-// "missingkey=zero"
-// The operation returns the zero value for the map type's element.
-// "missingkey=error"
-// Execution stops immediately with an error.
-//
-func (t *Template) Option(opt ...string) *Template {
- t.text.Option(opt...)
- return t
-}
-
-// checkCanParse checks whether it is OK to parse templates.
-// If not, it returns an error.
-func (t *Template) checkCanParse() error {
- if t == nil {
- return nil
- }
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- if t.nameSpace.escaped {
- return fmt.Errorf("html/template: cannot Parse after Execute")
- }
- return nil
-}
-
-// escape escapes all associated templates.
-func (t *Template) escape() error {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- t.nameSpace.escaped = true
- if t.escapeErr == nil {
- if t.Tree == nil {
- return fmt.Errorf("template: %q is an incomplete or empty template", t.Name())
- }
- if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {
- return err
- }
- } else if t.escapeErr != errEscapeOK {
- return t.escapeErr
- }
- return nil
-}
-
-// Execute applies a parsed template to the specified data object,
-// writing the output to wr.
-// If an error occurs executing the template or writing its output,
-// execution stops, but partial results may already have been written to
-// the output writer.
-// A template may be executed safely in parallel, although if parallel
-// executions share a Writer the output may be interleaved.
-func (t *Template) Execute(wr io.Writer, data interface{}) error {
- if err := t.escape(); err != nil {
- return err
- }
- return t.text.Execute(wr, data)
-}
-
-// ExecuteToHTML applies a parsed template to the specified data object,
-// returning the output as a safehtml.HTML value.
-// A template may be executed safely in parallel.
-func (t *Template) ExecuteToHTML(data interface{}) (safehtml.HTML, error) {
- var buf bytes.Buffer
- if err := t.Execute(&buf, data); err != nil {
- return safehtml.HTML{}, err
- }
- return uncheckedconversions.HTMLFromStringKnownToSatisfyTypeContract(buf.String()), nil
-}
-
-// MustParseAndExecuteToHTML is a helper that returns the safehtml.HTML value produced
-// by parsing text as a template body and executing it with no data. Any errors
-// encountered parsing or executing the template are fatal. This function is intended
-// to produce safehtml.HTML values from static HTML snippets such as
-//
-// html := MustParseAndExecuteToHTML("<b>Important</b>")
-//
-// To guarantee that the template body is never controlled by an attacker, text
-// must be an untyped string constant, which is always under programmer control.
-func MustParseAndExecuteToHTML(text stringConstant) safehtml.HTML {
- t, err := New("").Parse(text)
- if err != nil {
- log.Fatal(err)
- }
- html, err := t.ExecuteToHTML(nil)
- if err != nil {
- log.Fatal(err)
- }
- return html
-}
-
-// ExecuteTemplate applies the template associated with t that has the given
-// name to the specified data object and writes the output to wr.
-// If an error occurs executing the template or writing its output,
-// execution stops, but partial results may already have been written to
-// the output writer.
-// A template may be executed safely in parallel, although if parallel
-// executions share a Writer the output may be interleaved.
-func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
- tmpl, err := t.lookupAndEscapeTemplate(name)
- if err != nil {
- return err
- }
- return tmpl.text.Execute(wr, data)
-}
-
-// ExecuteTemplateToHTML applies the template associated with t that has
-// the given name to the specified data object and returns the output as
-// a safehtml.HTML value.
-// A template may be executed safely in parallel.
-func (t *Template) ExecuteTemplateToHTML(name string, data interface{}) (safehtml.HTML, error) {
- var buf bytes.Buffer
- if err := t.ExecuteTemplate(&buf, name, data); err != nil {
- return safehtml.HTML{}, err
- }
- return uncheckedconversions.HTMLFromStringKnownToSatisfyTypeContract(buf.String()), nil
-}
-
-// lookupAndEscapeTemplate guarantees that the template with the given name
-// is escaped, or returns an error if it cannot be. It returns the named
-// template.
-func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- t.nameSpace.escaped = true
- tmpl = t.set[name]
- if tmpl == nil {
- return nil, fmt.Errorf("html/template: %q is undefined", name)
- }
- if tmpl.escapeErr != nil && tmpl.escapeErr != errEscapeOK {
- return nil, tmpl.escapeErr
- }
- if tmpl.text.Tree == nil || tmpl.text.Root == nil {
- return nil, fmt.Errorf("html/template: %q is an incomplete template", name)
- }
- if t.text.Lookup(name) == nil {
- panic("html/template internal error: template escaping out of sync")
- }
- if tmpl.escapeErr == nil {
- err = escapeTemplate(tmpl, tmpl.text.Root, name)
- }
- return tmpl, err
-}
-
-// DefinedTemplates returns a string listing the defined templates,
-// prefixed by the string "; defined templates are: ". If there are none,
-// it returns the empty string. Used to generate an error message.
-func (t *Template) DefinedTemplates() string {
- return t.text.DefinedTemplates()
-}
-
-// Parse parses text as a template body for t.
-// Named template definitions ({{define ...}} or {{block ...}} statements) in text
-// define additional templates associated with t and are removed from the
-// definition of t itself.
-//
-// Templates can be redefined in successive calls to Parse,
-// before the first use of Execute on t or any associated template.
-// A template definition with a body containing only white space and comments
-// is considered empty and will not replace an existing template's body.
-// This allows using Parse to add new named template definitions without
-// overwriting the main template body.
-//
-// To guarantee that the template body is never controlled by an attacker, text
-// must be an untyped string constant, which is always under programmer control.
-func (t *Template) Parse(text stringConstant) (*Template, error) {
- if err := t.checkCanParse(); err != nil {
- return nil, err
- }
-
- ret, err := t.text.Parse(string(text))
- if err != nil {
- return nil, err
- }
-
- // In general, all the named templates might have changed underfoot.
- // Regardless, some new ones may have been defined.
- // The template.Template set has been updated; update ours.
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- for _, v := range ret.Templates() {
- name := v.Name()
- tmpl := t.set[name]
- if tmpl == nil {
- tmpl = t.new(name)
- }
- tmpl.text = v
- tmpl.Tree = v.Tree
- }
- return t, nil
-}
-
-// ParseFromTrustedTemplate parses tmpl as a template body for t.
-// Named template definitions ({{define ...}} or {{block ...}} statements) in text
-// define additional templates associated with t and are removed from the
-// definition of t itself.
-//
-// Templates can be redefined in successive calls to ParseFromTrustedTemplate,
-// before the first use of Execute on t or any associated template.
-// A template definition with a body containing only white space and comments
-// is considered empty and will not replace an existing template's body.
-// This allows using ParseFromTrustedTemplate to add new named template definitions without
-// overwriting the main template body.
-//
-// To guarantee that the template body is never controlled by an attacker, tmpl
-// is a TrustedTemplate, which is always under programmer control.
-func (t *Template) ParseFromTrustedTemplate(tmpl TrustedTemplate) (*Template, error) {
- return t.Parse(stringConstant(tmpl.String()))
-}
-
-// Clone returns a duplicate of the template, including all associated
-// templates. The actual representation is not copied, but the name space of
-// associated templates is, so further calls to Parse in the copy will add
-// templates to the copy but not to the original. Clone can be used to prepare
-// common templates and use them with variant definitions for other templates
-// by adding the variants after the clone is made.
-//
-// It returns an error if t has already been executed.
-func (t *Template) Clone() (*Template, error) {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- if t.escapeErr != nil {
- return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
- }
- textClone, err := t.text.Clone()
- if err != nil {
- return nil, err
- }
- ns := &nameSpace{set: make(map[string]*Template)}
- ns.esc = makeEscaper(ns)
- ret := &Template{
- nil,
- textClone,
- textClone.Tree,
- ns,
- }
- ret.set[ret.Name()] = ret
- for _, x := range textClone.Templates() {
- name := x.Name()
- src := t.set[name]
- if src == nil || src.escapeErr != nil {
- return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
- }
- x.Tree = x.Tree.Copy()
- ret.set[name] = &Template{
- nil,
- x,
- x.Tree,
- ret.nameSpace,
- }
- }
- // Return the template associated with the name of this template.
- return ret.set[ret.Name()], nil
-}
-
-// New allocates a new HTML template with the given name.
-func New(name string) *Template {
- ns := &nameSpace{set: make(map[string]*Template)}
- ns.esc = makeEscaper(ns)
- tmpl := &Template{
- nil,
- template.New(name),
- nil,
- ns,
- }
- tmpl.set[name] = tmpl
- return tmpl
-}
-
-// New allocates a new HTML template associated with the given one
-// and with the same delimiters. The association, which is transitive,
-// allows one template to invoke another with a {{template}} action.
-//
-// If a template with the given name already exists, the new HTML template
-// will replace it. The existing template will be reset and disassociated with
-// t.
-func (t *Template) New(name string) *Template {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- return t.new(name)
-}
-
-// new is the implementation of New, without the lock.
-func (t *Template) new(name string) *Template {
- tmpl := &Template{
- nil,
- t.text.New(name),
- nil,
- t.nameSpace,
- }
- if existing, ok := tmpl.set[name]; ok {
- emptyTmpl := New(existing.Name())
- *existing = *emptyTmpl
- }
- tmpl.set[name] = tmpl
- return tmpl
-}
-
-// Name returns the name of the template.
-func (t *Template) Name() string {
- return t.text.Name()
-}
-
-// FuncMap is the type of the map defining the mapping from names to
-// functions. Each function must have either a single return value, or two
-// return values of which the second has type error. In that case, if the
-// second (error) argument evaluates to non-nil during execution, execution
-// terminates and Execute returns that error. FuncMap has the same base type
-// as FuncMap in "text/template", copied here so clients need not import
-// "text/template".
-type FuncMap map[string]interface{}
-
-// Funcs adds the elements of the argument map to the template's function map.
-// It must be called before the template is parsed.
-// It panics if a value in the map is not a function with appropriate return
-// type. However, it is legal to overwrite elements of the map. The return
-// value is the template, so calls can be chained.
-func (t *Template) Funcs(funcMap FuncMap) *Template {
- t.text.Funcs(template.FuncMap(funcMap))
- return t
-}
-
-// CSPCompatible causes this template to check template text for
-// Content Security Policy (CSP) compatibility. The template will return errors
-// at execution time if inline event handler attribute names or javascript:
-// URIs are found in template text.
-//
-// For example, the following templates will cause errors:
-// <span onclick="doThings();">A thing.</span> // inline event handler "onclick"
-// <a href="javascript:linkClicked()">foo</a> // javascript: URI present
-func (t *Template) CSPCompatible() *Template {
- t.nameSpace.mu.Lock()
- t.nameSpace.cspCompatible = true
- t.nameSpace.mu.Unlock()
- return t
-}
-
-// Delims sets the action delimiters to the specified strings, to be used in
-// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
-// definitions will inherit the settings. An empty delimiter stands for the
-// corresponding default: {{ or }}.
-// The return value is the template, so calls can be chained.
-func (t *Template) Delims(left, right string) *Template {
- t.text.Delims(left, right)
- return t
-}
-
-// Lookup returns the template with the given name that is associated with t,
-// or nil if there is no such template.
-func (t *Template) Lookup(name string) *Template {
- t.nameSpace.mu.Lock()
- defer t.nameSpace.mu.Unlock()
- return t.set[name]
-}
-
-// Must is a helper that wraps a call to a function returning (*Template, error)
-// and panics if the error is non-nil. It is intended for use in variable initializations
-// such as
-// var t = template.Must(template.New("name").Parse("html"))
-func Must(t *Template, err error) *Template {
- if err != nil {
- panic(err)
- }
- return t
-}
-
-// stringConstant is an unexported string type. Users of this package cannot
-// create values of this type except by passing an untyped string constant to
-// functions which expect a stringConstant. This type must be used only in
-// function and method parameters.
-type stringConstant string
-
-func stringConstantsToStrings(strs []stringConstant) []string {
- ret := make([]string, 0, len(strs))
- for _, s := range strs {
- ret = append(ret, string(s))
- }
- return ret
-}
-
-// ParseFiles creates a new Template and parses the template definitions from
-// the named files. The returned template's name will have the (base) name and
-// (parsed) contents of the first file. There must be at least one file.
-// If an error occurs, parsing stops and the returned *Template is nil.
-//
-// When parsing multiple files with the same name in different directories,
-// the last one mentioned will be the one that results.
-// For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
-// named "foo", while "a/foo" is unavailable.
-//
-// To guarantee that filepaths, and thus template bodies, are never controlled by
-// an attacker, filenames must be untyped string constants, which are always under
-// programmer control.
-func ParseFiles(filenames ...stringConstant) (*Template, error) {
- return parseFiles(nil, readFileOS, stringConstantsToStrings(filenames)...)
-}
-
-// ParseFilesFromTrustedSources creates a new Template and parses the template definitions from
-// the named files. The returned template's name will have the (base) name and
-// (parsed) contents of the first file. There must be at least one file.
-// If an error occurs, parsing stops and the returned *Template is nil.
-//
-// When parsing multiple files with the same name in different directories,
-// the last one mentioned will be the one that results.
-// For instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
-// named "foo", while "a/foo" is unavailable.
-//
-// To guarantee that filepaths, and thus template bodies, are never controlled by
-// an attacker, filenames must be trusted sources, which are always under programmer
-// or application control.
-func ParseFilesFromTrustedSources(filenames ...TrustedSource) (*Template, error) {
- return parseFiles(nil, readFileOS, trustedSourcesToStrings(filenames)...)
-}
-
-// ParseFiles parses the named files and associates the resulting templates with
-// t. If an error occurs, parsing stops and the returned template is nil;
-// otherwise it is t. There must be at least one file.
-//
-// When parsing multiple files with the same name in different directories,
-// the last one mentioned will be the one that results.
-//
-// ParseFiles returns an error if t or any associated template has already been executed.
-//
-// To guarantee that filepaths, and thus template bodies, are never controlled by
-// an attacker, filenames must be untyped string constants, which are always under
-// programmer control.
-func (t *Template) ParseFiles(filenames ...stringConstant) (*Template, error) {
- return parseFiles(t, readFileOS, stringConstantsToStrings(filenames)...)
-}
-
-// ParseFilesFromTrustedSources parses the named files and associates the resulting templates with
-// t. If an error occurs, parsing stops and the returned template is nil;
-// otherwise it is t. There must be at least one file.
-//
-// When parsing multiple files with the same name in different directories,
-// the last one mentioned will be the one that results.
-//
-// ParseFilesFromTrustedSources returns an error if t or any associated template has already been executed.
-//
-// To guarantee that filepaths, and thus template bodies, are never controlled by
-// an attacker, filenames must be trusted sources, which are always under programmer
-// or application control.
-func (t *Template) ParseFilesFromTrustedSources(filenames ...TrustedSource) (*Template, error) {
- return parseFiles(t, readFileOS, trustedSourcesToStrings(filenames)...)
-}
-
-// parseFiles is the helper for the method and function. If the argument
-// template is nil, it is created from the first file.
-// readFile takes a filename and returns the file's basename and contents.
-func parseFiles(t *Template, readFile func(string) (string, []byte, error), filenames ...string) (*Template, error) {
- if err := t.checkCanParse(); err != nil {
- return nil, err
- }
-
- if len(filenames) == 0 {
- // Not really a problem, but be consistent.
- return nil, fmt.Errorf("html/template: no files named in call to ParseFiles")
- }
- for _, filename := range filenames {
- name, b, err := readFile(filename)
- if err != nil {
- return nil, err
- }
- s := stringConstant(b)
- // First template becomes return value if not already defined,
- // and we use that one for subsequent New calls to associate
- // all the templates together. Also, if this file has the same name
- // as t, this file becomes the contents of t, so
- // t, err := New(name).Funcs(xxx).ParseFiles(name)
- // works. Otherwise we create a new template associated with t.
- var tmpl *Template
- if t == nil {
- t = New(name)
- }
- if name == t.Name() {
- tmpl = t
- } else {
- tmpl = t.New(name)
- }
- _, err = tmpl.Parse(s)
- if err != nil {
- return nil, err
- }
- }
- return t, nil
-}
-
-// Copied with minor changes from
-// https://go.googlesource.com/go/+/refs/tags/go1.17.1/src/text/template/helper.go.
-func readFileOS(file string) (string, []byte, error) {
- name := filepath.Base(file)
- b, err := ioutil.ReadFile(file)
- return name, b, err
-}
-
-// ParseGlob creates a new Template and parses the template definitions from the
-// files identified by the pattern, which must match at least one file. The
-// returned template will have the (base) name and (parsed) contents of the
-// first file matched by the pattern. ParseGlob is equivalent to calling
-// ParseFiles with the list of files matched by the pattern.
-//
-// To guarantee that the pattern, and thus the template bodies, is never controlled by
-// an attacker, pattern must be an untyped string constant, which is always under
-// programmer control.
-func ParseGlob(pattern stringConstant) (*Template, error) {
- return parseGlob(nil, string(pattern))
-}
-
-// ParseGlobFromTrustedSource creates a new Template and parses the template definitions from the
-// files identified by the pattern, which must match at least one file. The
-// returned template will have the (base) name and (parsed) contents of the
-// first file matched by the pattern. ParseGlobFromTrustedSource is equivalent to calling
-// ParseFilesFromTrustedSources with the list of files matched by the pattern.
-//
-// To guarantee that the pattern, and thus the template bodies, is never controlled by
-// an attacker, pattern must be a trusted source, which is always under programmer or
-// application control.
-func ParseGlobFromTrustedSource(pattern TrustedSource) (*Template, error) {
- return parseGlob(nil, pattern.String())
-}
-
-// ParseGlob parses the template definitions in the files identified by the
-// pattern and associates the resulting templates with t. The pattern is
-// processed by filepath.Glob and must match at least one file. ParseGlob is
-// equivalent to calling t.ParseFiles with the list of files matched by the
-// pattern.
-//
-// When parsing multiple files with the same name in different directories,
-// the last one mentioned will be the one that results.
-//
-// ParseGlob returns an error if t or any associated template has already been executed.
-//
-// To guarantee that the pattern, and thus the template bodies, is never controlled by
-// an attacker, pattern must be an untyped string constant, which is always under
-// programmer control.
-func (t *Template) ParseGlob(pattern stringConstant) (*Template, error) {
- return parseGlob(t, string(pattern))
-}
-
-// ParseGlobFromTrustedSource parses the template definitions in the files identified by the
-// pattern and associates the resulting templates with t. The pattern is
-// processed by filepath.Glob and must match at least one file. ParseGlob is
-// equivalent to calling t.ParseFiles with the list of files matched by the
-// pattern.
-//
-// When parsing multiple files with the same name in different directories,
-// the last one mentioned will be the one that results.
-//
-// ParseGlobFromTrustedSource returns an error if t or any associated template has already been executed.
-//
-// To guarantee that the pattern, and thus the template bodies, is never controlled by
-// an attacker, pattern must be a trusted source, which is always under programmer or
-// application control.
-func (t *Template) ParseGlobFromTrustedSource(pattern TrustedSource) (*Template, error) {
- return parseGlob(t, pattern.String())
-}
-
-// parseGlob is the implementation of the function and method ParseGlob.
-func parseGlob(t *Template, pattern string) (*Template, error) {
- if err := t.checkCanParse(); err != nil {
- return nil, err
- }
- filenames, err := filepath.Glob(pattern)
- if err != nil {
- return nil, err
- }
- if len(filenames) == 0 {
- return nil, fmt.Errorf("html/template: pattern matches no files: %#q", pattern)
- }
- return parseFiles(t, readFileOS, filenames...)
-}
-
-// IsTrue reports whether the value is 'true', in the sense of not the zero of its type,
-// and whether the value has a meaningful truth value. This is the definition of
-// truth used by if and other such actions.
-func IsTrue(val interface{}) (truth, ok bool) {
- return template.IsTrue(val)
-}
diff --git a/vendor/github.com/google/safehtml/template/transition.go b/vendor/github.com/google/safehtml/template/transition.go
deleted file mode 100644
index e0882e489..000000000
--- a/vendor/github.com/google/safehtml/template/transition.go
+++ /dev/null
@@ -1,312 +0,0 @@
-// 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 (
- "bytes"
- "strings"
-)
-
-// transitionFunc is the array of context transition functions for text nodes.
-// A transition function takes a context and template text input, and returns
-// the updated context and the number of bytes consumed from the front of the
-// input.
-var transitionFunc = [...]func(context, []byte) (context, int){
- stateText: tText,
- stateSpecialElementBody: tSpecialTagEnd,
- stateTag: tTag,
- stateAttrName: tAttrName,
- stateAfterName: tAfterName,
- stateBeforeValue: tBeforeValue,
- stateHTMLCmt: tHTMLCmt,
- stateAttr: tAttr,
- stateError: tError,
-}
-
-var commentStart = []byte("<!--")
-var commentEnd = []byte("-->")
-
-// tText is the context transition function for the text state.
-func tText(c context, s []byte) (context, int) {
- k := 0
- for {
- i := k + bytes.IndexByte(s[k:], '<')
- if i < k || i+1 == len(s) {
- return c, len(s)
- } else if i+4 <= len(s) && bytes.Equal(commentStart, s[i:i+4]) {
- return context{state: stateHTMLCmt}, i + 4
- }
- i++
- end := false
- if s[i] == '/' {
- if i+1 == len(s) {
- return c, len(s)
- }
- end, i = true, i+1
- }
- j, e := eatTagName(s, i)
- if j != i {
- // We've found an HTML tag.
- ret := context{state: stateTag}
- // Element name not needed if we are at the end of the element.
- if !end {
- ret.element = e
- }
- return ret, j
- }
- k = j
- }
-}
-
-// specialElements contains the names of elements whose bodies are treated
-// differently by the parser and escaper from stateText.
-var specialElements = map[string]bool{
- "script": true,
- "style": true,
- "textarea": true,
- "title": true,
-}
-
-// voidElements contains the names of all void elements.
-// https://www.w3.org/TR/html5/syntax.html#void-elements
-var voidElements = map[string]bool{
- "area": true,
- "base": true,
- "br": true,
- "col": true,
- "embed": true,
- "hr": true,
- "img": true,
- "input": true,
- "keygen": true,
- "link": true,
- "meta": true,
- "param": true,
- "source": true,
- "track": true,
- "wbr": true,
-}
-
-// tTag is the context transition function for the tag state.
-func tTag(c context, s []byte) (context, int) {
- // Find the attribute name.
- i := eatWhiteSpace(s, 0)
- if i == len(s) {
- return c, len(s)
- }
- if s[i] == '>' {
- ret := context{
- state: stateText,
- element: c.element,
- scriptType: c.scriptType,
- linkRel: c.linkRel,
- }
- if specialElements[c.element.name] {
- ret.state = stateSpecialElementBody
- }
- if c.element.name != "" && voidElements[c.element.name] {
- // Special case: end of start tag of a void element.
- // Discard unnecessary state, since this element have no content.
- ret.element = element{}
- ret.scriptType = ""
- ret.linkRel = ""
- }
- return ret, i + 1
- }
- j, err := eatAttrName(s, i)
- if err != nil {
- return context{state: stateError, err: err}, len(s)
- }
- state := stateTag
- if i == j {
- return context{
- state: stateError,
- err: errorf(ErrBadHTML, nil, 0, "expected space, attr name, or end of tag, but got %q", s[i:]),
- }, len(s)
- }
-
- if j == len(s) {
- state = stateAttrName
- } else {
- state = stateAfterName
- }
- return context{
- state: state,
- element: c.element,
- attr: attr{name: strings.ToLower(string(s[i:j]))},
- linkRel: c.linkRel,
- }, j
-}
-
-// tAttrName is the context transition function for stateAttrName.
-func tAttrName(c context, s []byte) (context, int) {
- i, err := eatAttrName(s, 0)
- if err != nil {
- return context{state: stateError, err: err}, len(s)
- } else if i != len(s) {
- c.state = stateAfterName
- }
- return c, i
-}
-
-// tAfterName is the context transition function for stateAfterName.
-func tAfterName(c context, s []byte) (context, int) {
- // Look for the start of the value.
- i := eatWhiteSpace(s, 0)
- if i == len(s) {
- return c, len(s)
- } else if s[i] != '=' {
- // Occurs due to tag ending '>', and valueless attribute.
- c.state = stateTag
- return c, i
- }
- c.state = stateBeforeValue
- // Consume the "=".
- return c, i + 1
-}
-
-// tBeforeValue is the context transition function for stateBeforeValue.
-func tBeforeValue(c context, s []byte) (context, int) {
- i := eatWhiteSpace(s, 0)
- if i == len(s) {
- return c, len(s)
- }
- // Find the attribute delimiter.
- // TODO: consider disallowing single-quoted or unquoted attribute values completely, even in hardcoded template text.
- delim := delimSpaceOrTagEnd
- switch s[i] {
- case '\'':
- delim, i = delimSingleQuote, i+1
- case '"':
- delim, i = delimDoubleQuote, i+1
- }
- c.state, c.delim = stateAttr, delim
- return c, i
-}
-
-// tHTMLCmt is the context transition function for stateHTMLCmt.
-func tHTMLCmt(c context, s []byte) (context, int) {
- if i := bytes.Index(s, commentEnd); i != -1 {
- return context{}, i + 3
- }
- return c, len(s)
-}
-
-var (
- specialTagEndPrefix = []byte("</")
- tagEndSeparators = []byte("> \t\n\f/")
-)
-
-// tSpecialTagEnd is the context transition function for raw text, RCDATA
-// script data, and stylesheet element states.
-func tSpecialTagEnd(c context, s []byte) (context, int) {
- if specialElements[c.element.name] {
- if i := indexTagEnd(s, []byte(c.element.name)); i != -1 {
- return context{}, i
- }
- }
- return c, len(s)
-}
-
-// indexTagEnd finds the index of a special tag end in a case insensitive way, or returns -1
-func indexTagEnd(s []byte, tag []byte) int {
- res := 0
- plen := len(specialTagEndPrefix)
- for len(s) > 0 {
- // Try to find the tag end prefix first
- i := bytes.Index(s, specialTagEndPrefix)
- if i == -1 {
- return i
- }
- s = s[i+plen:]
- // Try to match the actual tag if there is still space for it
- if len(tag) <= len(s) && bytes.EqualFold(tag, s[:len(tag)]) {
- s = s[len(tag):]
- // Check the tag is followed by a proper separator
- if len(s) > 0 && bytes.IndexByte(tagEndSeparators, s[0]) != -1 {
- return res + i
- }
- res += len(tag)
- }
- res += i + plen
- }
- return -1
-}
-
-// tAttr is the context transition function for the attribute state.
-func tAttr(c context, s []byte) (context, int) {
- return c, len(s)
-}
-
-// tError is the context transition function for the error state.
-func tError(c context, s []byte) (context, int) {
- return c, len(s)
-}
-
-// eatAttrName returns the largest j such that s[i:j] is an attribute name.
-// It returns an error if s[i:] does not look like it begins with an
-// attribute name, such as encountering a quote mark without a preceding
-// equals sign.
-func eatAttrName(s []byte, i int) (int, *Error) {
- for j := i; j < len(s); j++ {
- switch s[j] {
- case ' ', '\t', '\n', '\f', '\r', '=', '>':
- return j, nil
- case '\'', '"', '<':
- // These result in a parse warning in HTML5 and are
- // indicative of serious problems if seen in an attr
- // name in a template.
- return -1, errorf(ErrBadHTML, nil, 0, "%q in attribute name: %.32q", s[j:j+1], s)
- default:
- // No-op.
- }
- }
- return len(s), nil
-}
-
-// asciiAlpha reports whether c is an ASCII letter.
-func asciiAlpha(c byte) bool {
- return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z'
-}
-
-// asciiAlphaNum reports whether c is an ASCII letter or digit.
-func asciiAlphaNum(c byte) bool {
- return asciiAlpha(c) || '0' <= c && c <= '9'
-}
-
-// eatTagName returns the largest j such that s[i:j] is a tag name and the tag name.
-func eatTagName(s []byte, i int) (int, element) {
- if i == len(s) || !asciiAlpha(s[i]) {
- return i, element{}
- }
- j := i + 1
- for j < len(s) {
- x := s[j]
- if asciiAlphaNum(x) {
- j++
- continue
- }
- // Allow "x-y" or "x:y" but not "x-", "-y", or "x--y".
- if (x == ':' || x == '-') && j+1 < len(s) && asciiAlphaNum(s[j+1]) {
- j += 2
- continue
- }
- break
- }
- return j, element{name: strings.ToLower(string(s[i:j]))}
-}
-
-// eatWhiteSpace returns the largest j such that s[i:j] is white space.
-func eatWhiteSpace(s []byte, i int) int {
- for j := i; j < len(s); j++ {
- switch s[j] {
- case ' ', '\t', '\n', '\f', '\r':
- // No-op.
- default:
- return j
- }
- }
- return len(s)
-}
diff --git a/vendor/github.com/google/safehtml/template/trustedfs.go b/vendor/github.com/google/safehtml/template/trustedfs.go
deleted file mode 100644
index 80db11824..000000000
--- a/vendor/github.com/google/safehtml/template/trustedfs.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright (c) 2021 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 or at
-// https://developers.google.com/open-source/licenses/bsd
-
-//go:build go1.16
-// +build go1.16
-
-package template
-
-import (
- "embed"
- "fmt"
- "io/fs"
- "os"
- "path"
-)
-
-// A TrustedFS is an immutable type referencing a filesystem (fs.FS)
-// under application control.
-//
-// In order to ensure that an attacker cannot influence the TrustedFS value, a
-// TrustedFS can be instantiated in only two ways. One way is from an embed.FS
-// with TrustedFSFromEmbed. It is assumed that embedded filesystems are under
-// the programmer's control. The other way is from a TrustedSource using
-// TrustedFSFromTrustedSource, in which case the guarantees and caveats of
-// TrustedSource apply.
-type TrustedFS struct {
- fsys fs.FS
-}
-
-// TrustedFSFromEmbed constructs a TrustedFS from an embed.FS.
-func TrustedFSFromEmbed(fsys embed.FS) TrustedFS {
- return TrustedFS{fsys: fsys}
-}
-
-// TrustedFSFromTrustedSource constructs a TrustedFS from the string in the
-// TrustedSource, which should refer to a directory.
-func TrustedFSFromTrustedSource(ts TrustedSource) TrustedFS {
- return TrustedFS{fsys: os.DirFS(ts.src)}
-}
-
-// Sub returns a TrustedFS at a subdirectory of the receiver.
-// It works by calling fs.Sub on the receiver's fs.FS.
-func (tf TrustedFS) Sub(dir TrustedSource) (TrustedFS, error) {
- subfs, err := fs.Sub(tf.fsys, dir.String())
- return TrustedFS{fsys: subfs}, err
-}
-
-// ParseFS is like ParseFiles or ParseGlob but reads from the TrustedFS
-// instead of the host operating system's file system.
-// It accepts a list of glob patterns.
-// (Note that most file names serve as glob patterns matching only themselves.)
-//
-// The same behaviors listed for ParseFiles() apply to ParseFS too (e.g. using the base name
-// of the file as the template name).
-func ParseFS(tfs TrustedFS, patterns ...string) (*Template, error) {
- return parseFS(nil, tfs.fsys, patterns)
-}
-
-// ParseFS is like ParseFiles or ParseGlob but reads from the TrustedFS
-// instead of the host operating system's file system.
-// It accepts a list of glob patterns.
-// (Note that most file names serve as glob patterns matching only themselves.)
-//
-// The same behaviors listed for ParseFiles() apply to ParseFS too (e.g. using the base name
-// of the file as the template name).
-func (t *Template) ParseFS(tfs TrustedFS, patterns ...string) (*Template, error) {
- return parseFS(t, tfs.fsys, patterns)
-}
-
-// Copied from
-// https://go.googlesource.com/go/+/refs/tags/go1.17.1/src/text/template/helper.go.
-func parseFS(t *Template, fsys fs.FS, patterns []string) (*Template, error) {
- var filenames []string
- for _, pattern := range patterns {
- list, err := fs.Glob(fsys, pattern)
- if err != nil {
- return nil, err
- }
- if len(list) == 0 {
- return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
- }
- filenames = append(filenames, list...)
- }
- return parseFiles(t, readFileFS(fsys), filenames...)
-}
-
-// Copied with minor changes from
-// https://go.googlesource.com/go/+/refs/tags/go1.17.1/src/text/template/helper.go.
-func readFileFS(fsys fs.FS) func(string) (string, []byte, error) {
- return func(file string) (string, []byte, error) {
- name := path.Base(file)
- b, err := fs.ReadFile(fsys, file)
- return name, b, err
- }
-}
diff --git a/vendor/github.com/google/safehtml/template/trustedsource.go b/vendor/github.com/google/safehtml/template/trustedsource.go
deleted file mode 100644
index f64263948..000000000
--- a/vendor/github.com/google/safehtml/template/trustedsource.go
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (c) 2017 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 or at
-// https://developers.google.com/open-source/licenses/bsd
-
-package template
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "strings"
-
- "flag"
-)
-
-// A TrustedSource is an immutable string-like type referencing
-// trusted template files under application control. It can be passed to
-// template-parsing functions and methods to safely load templates
-// without the risk of untrusted template execution.
-//
-// In order to ensure that an attacker cannot influence the TrustedSource
-// value, a TrustedSource can be instantiated only from untyped string
-// constants, command-line flags, and other application-controlled strings, but
-// never from arbitrary string values potentially representing untrusted user input.
-//
-// Note that TrustedSource's constructors cannot truly guarantee that the
-// templates it references are not attacker-controlled; it can guarantee only that
-// the path to the template itself is under application control. Users of these
-// constructors must ensure themselves that TrustedSource never references
-// attacker-controlled files or directories that contain such files.
-type TrustedSource struct {
- // We declare a TrustedSource not as a string but as a struct wrapping a string
- // to prevent construction of TrustedSource values through string conversion.
- src string
-}
-
-// TrustedSourceFromConstant constructs a TrustedSource with its underlying
-// src set to the given src, which must be an untyped string constant.
-//
-// No runtime validation or sanitization is performed on src; being under
-// application control, it is simply assumed to comply with the TrustedSource type
-// contract.
-func TrustedSourceFromConstant(src stringConstant) TrustedSource {
- return TrustedSource{string(src)}
-}
-
-// TrustedSourceFromConstantDir constructs a TrustedSource calling path/filepath.Join on
-// an application-controlled directory path, which must be an untyped string constant,
-// a TrustedSource, and a dynamic filename. It returns an error if filename contains
-// filepath or list separators, since this might cause the resulting path to reference a
-// file outside of the given directory.
-//
-// dir or src may be empty if either of these path segments are not required.
-func TrustedSourceFromConstantDir(dir stringConstant, src TrustedSource, filename string) (TrustedSource, error) {
- if i := strings.IndexAny(filename, string([]rune{filepath.Separator, filepath.ListSeparator})); i != -1 {
- return TrustedSource{}, fmt.Errorf("filename %q must not contain the separator %q", filename, filename[i])
- }
- if filename == ".." {
- return TrustedSource{}, fmt.Errorf("filename must not be the special name %q", filename)
- }
- return TrustedSource{filepath.Join(string(dir), src.String(), filename)}, nil
-}
-
-// TrustedSourceJoin is a wrapper around path/filepath.Join that returns a
-// TrustedSource formed by joining the given path elements into a single path,
-// adding an OS-specific path separator if necessary.
-func TrustedSourceJoin(elem ...TrustedSource) TrustedSource {
- return TrustedSource{filepath.Join(trustedSourcesToStrings(elem)...)}
-}
-
-// TrustedSourceFromFlag returns a TrustedSource containing the string
-// representation of the retrieved value of the flag.
-//
-// In a server setting, flags are part of the application's deployment
-// configuration and are hence considered application-controlled.
-func TrustedSourceFromFlag(value flag.Value) TrustedSource {
- return TrustedSource{fmt.Sprint(value.String())}
-}
-
-// TrustedSourceFromEnvVar is a wrapper around os.Getenv that
-// returns a TrustedSource containing the value of the environment variable
-// named by the key. It returns the value, which will be empty if the variable
-// is not present. To distinguish between an empty value and an unset value,
-// use os.LookupEnv.
-//
-// In a server setting, environment variables are part of the application's
-// deployment configuration and are hence considered application-controlled.
-func TrustedSourceFromEnvVar(key stringConstant) TrustedSource {
- return TrustedSource{os.Getenv(string(key))}
-}
-
-// String returns the string form of the TrustedSource.
-func (t TrustedSource) String() string {
- return t.src
-}
-
-func trustedSourcesToStrings(paths []TrustedSource) []string {
- ret := make([]string, 0, len(paths))
- for _, p := range paths {
- ret = append(ret, p.String())
- }
- return ret
-}
diff --git a/vendor/github.com/google/safehtml/template/trustedtemplate.go b/vendor/github.com/google/safehtml/template/trustedtemplate.go
deleted file mode 100644
index bd3b1b46a..000000000
--- a/vendor/github.com/google/safehtml/template/trustedtemplate.go
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 2017 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 or at
-// https://developers.google.com/open-source/licenses/bsd
-
-package template
-
-// A TrustedTemplate is an immutable string-like type containing a
-// safehtml/template template body. It can be safely loaded as template
-// text without the risk of untrusted template execution.
-//
-// In order to ensure that an attacker cannot influence the TrustedTemplate
-// value, a TrustedTemplate can be instantiated only from untyped string constants,
-// and never from arbitrary string values potentially representing untrusted user input.
-//
-type TrustedTemplate struct {
- // We declare a TrustedTemplate not as a string but as a struct wrapping a string
- // to prevent construction of TrustedTemplate values through string conversion.
- tmpl string
-}
-
-// MakeTrustedTemplate constructs a TrustedTemplate with its underlying
-// tmpl set to the given tmpl, which must be an untyped string constant.
-//
-// No runtime validation or sanitization is performed on tmpl; being under
-// application control, it is simply assumed to comply with the TrustedTemplate type
-// contract.
-func MakeTrustedTemplate(tmpl stringConstant) TrustedTemplate {
- return TrustedTemplate{string(tmpl)}
-}
-
-// String returns the string form of the TrustedTemplate.
-func (t TrustedTemplate) String() string {
- return t.tmpl
-}
diff --git a/vendor/github.com/google/safehtml/template/url.go b/vendor/github.com/google/safehtml/template/url.go
deleted file mode 100644
index f63475fcf..000000000
--- a/vendor/github.com/google/safehtml/template/url.go
+++ /dev/null
@@ -1,122 +0,0 @@
-// 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 (
- "fmt"
- "html"
- "regexp"
- "strings"
-
- "github.com/google/safehtml/internal/safehtmlutil"
- "github.com/google/safehtml"
-)
-
-// urlPrefixValidators maps URL and TrustedResourceURL sanitization contexts to functions return an error
-// if the given string is unsafe to use as a URL prefix in that sanitization context.
-var urlPrefixValidators = map[sanitizationContext]func(string) error{
- sanitizationContextURL: validateURLPrefix,
- sanitizationContextTrustedResourceURLOrURL: validateURLPrefix,
- sanitizationContextTrustedResourceURL: validateTrustedResourceURLPrefix,
-}
-
-// startsWithFullySpecifiedSchemePattern matches strings that have a fully-specified scheme component.
-// See RFC 3986 Section 3.
-var startsWithFullySpecifiedSchemePattern = regexp.MustCompile(
- `^[[:alpha:]](?:[[:alnum:]]|[+.-])*:`)
-
-// validateURLPrefix validates if the given non-empty prefix is a safe safehtml.URL prefix.
-//
-// Prefixes are considered unsafe if they end in an incomplete HTML character reference
-// or percent-encoding character triplet.
-//
-// If the prefix contains a fully-specified scheme component, it is considered safe only if
-// it starts with an allowed scheme. See safehtml.URLSanitized for more details.
-//
-// Otherwise, the prefix is safe only if it contains '/', '?', or '#', since the presence of any
-// of these runes ensures that this prefix, when combined with some arbitrary suffix, cannot be
-// interpreted as a part of a scheme.
-func validateURLPrefix(prefix string) error {
- decoded, err := decodeURLPrefix(prefix)
- if err != nil {
- return err
- }
- switch {
- case startsWithFullySpecifiedSchemePattern.MatchString(decoded):
- if safehtml.URLSanitized(decoded).String() != decoded {
- return fmt.Errorf("URL prefix %q contains an unsafe scheme", prefix)
- }
- case !strings.ContainsAny(decoded, "/?#"):
- // If the URL prefix does not already have a ':' scheme delimiter, and does not contain
- // '/', '?', or '#', any ':' following this prefix will be intepreted as a scheme
- // delimiter, causing this URL prefix to be interpreted as being part of a scheme.
- // e.g. `<a href="java{{ "script:" }}alert(1)>`
- return fmt.Errorf("URL prefix %q is unsafe; it might be interpreted as part of a scheme", prefix)
- }
- return nil
-}
-
-// validateTrustedResourceURLPrefix validates if the given non-empty prefix is a safe
-// safehtml.TrustedResourceURL prefix.
-//
-// Prefixes are considered unsafe if they end in an incomplete HTML character reference
-// or percent-encoding character triplet.
-//
-// See safehtmlutil.IsSafeTrustedResourceURLPrefix for details on how the prefix is validated.
-func validateTrustedResourceURLPrefix(prefix string) error {
- decoded, err := decodeURLPrefix(prefix)
- if err != nil {
- return err
- }
- if !safehtmlutil.IsSafeTrustedResourceURLPrefix(decoded) {
- return fmt.Errorf("%q is a disallowed TrustedResourceURL prefix", prefix)
- }
- return nil
-}
-
-// endsWithPercentEncodingPrefixPattern matches strings that end in an incomplete
-// URL percent encoding triplet.
-//
-// See https://tools.ietf.org/html/rfc3986#section-2.1.
-var endsWithPercentEncodingPrefixPattern = regexp.MustCompile(
- `%[[:xdigit:]]?$`)
-
-// containsWhitespaceOrControlPattern matches strings that contain ASCII whitespace
-// or control characters.
-var containsWhitespaceOrControlPattern = regexp.MustCompile(`[[:space:]]|[[:cntrl:]]`)
-
-// decodeURLPrefix returns the given prefix after it has been HTML-unescaped.
-// It returns an error if the prefix:
-// * ends in an incomplete HTML character reference before HTML-unescaping,
-// * ends in an incomplete percent-encoding character triplet after HTML-unescaping, or
-// * contains whitespace before or after HTML-unescaping.
-func decodeURLPrefix(prefix string) (string, error) {
- if containsWhitespaceOrControlPattern.MatchString(prefix) {
- return "", fmt.Errorf("URL prefix %q contains whitespace or control characters", prefix)
- }
- if err := validateDoesNotEndsWithCharRefPrefix(prefix); err != nil {
- return "", fmt.Errorf("URL %s", err)
- }
- decoded := html.UnescapeString(prefix)
- // Check again for whitespace that might have previously been masked by a HTML reference,
- // such as in "javascript&NewLine;".
- if containsWhitespaceOrControlPattern.MatchString(decoded) {
- return "", fmt.Errorf("URL prefix %q contains whitespace or control characters", prefix)
- }
- if endsWithPercentEncodingPrefixPattern.MatchString(decoded) {
- return "", fmt.Errorf("URL prefix %q ends with an incomplete percent-encoding character triplet", prefix)
- }
- return decoded, nil
-}
-
-func validateTrustedResourceURLSubstitution(args ...interface{}) (string, error) {
- input := safehtmlutil.Stringify(args...)
- if safehtmlutil.URLContainsDoubleDotSegment(input) {
- // Reject substitutions containing the ".." dot-segment to prevent the final TrustedResourceURL from referencing
- // a resource higher up in the path name hierarchy than the path specified in the prefix.
- return "", fmt.Errorf(`cannot substitute %q after TrustedResourceURL prefix: ".." is disallowed`, input)
- }
- return input, nil
-}