error.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package template
  5. import (
  6. "fmt"
  7. "text/template/parse"
  8. )
  9. // Error describes a problem encountered during template Escaping.
  10. type Error struct {
  11. // ErrorCode describes the kind of error.
  12. ErrorCode ErrorCode
  13. // Node is the node that caused the problem, if known.
  14. // If not nil, it overrides Name and Line.
  15. Node parse.Node
  16. // Name is the name of the template in which the error was encountered.
  17. Name string
  18. // Line is the line number of the error in the template source or 0.
  19. Line int
  20. // Description is a human-readable description of the problem.
  21. Description string
  22. }
  23. // ErrorCode is a code for a kind of error.
  24. type ErrorCode int
  25. // We define codes for each error that manifests while escaping templates, but
  26. // escaped templates may also fail at runtime.
  27. //
  28. // Output: "ZgotmplZ"
  29. // Example:
  30. // <img src="{{.X}}">
  31. // where {{.X}} evaluates to `javascript:...`
  32. // Discussion:
  33. // "ZgotmplZ" is a special value that indicates that unsafe content reached a
  34. // CSS or URL context at runtime. The output of the example will be
  35. // <img src="#ZgotmplZ">
  36. // If the data comes from a trusted source, use content types to exempt it
  37. // from filtering: URL(`javascript:...`).
  38. const (
  39. // OK indicates the lack of an error.
  40. OK ErrorCode = iota
  41. // ErrAmbigContext: "... appears in an ambiguous context within a URL"
  42. // Example:
  43. // <a href="
  44. // {{if .C}}
  45. // /path/
  46. // {{else}}
  47. // /search?q=
  48. // {{end}}
  49. // {{.X}}
  50. // ">
  51. // Discussion:
  52. // {{.X}} is in an ambiguous URL context since, depending on {{.C}},
  53. // it may be either a URL suffix or a query parameter.
  54. // Moving {{.X}} into the condition removes the ambiguity:
  55. // <a href="{{if .C}}/path/{{.X}}{{else}}/search?q={{.X}}">
  56. ErrAmbigContext
  57. // ErrBadHTML: "expected space, attr name, or end of tag, but got ...",
  58. // "... in unquoted attr", "... in attribute name"
  59. // Example:
  60. // <a href = /search?q=foo>
  61. // <href=foo>
  62. // <form na<e=...>
  63. // <option selected<
  64. // Discussion:
  65. // This is often due to a typo in an HTML element, but some runes
  66. // are banned in tag names, attribute names, and unquoted attribute
  67. // values because they can tickle parser ambiguities.
  68. // Quoting all attributes is the best policy.
  69. ErrBadHTML
  70. // ErrBranchEnd: "{{if}} branches end in different contexts"
  71. // Example:
  72. // {{if .C}}<a href="{{end}}{{.X}}
  73. // Discussion:
  74. // Package html/template statically examines each path through an
  75. // {{if}}, {{range}}, or {{with}} to escape any following pipelines.
  76. // The example is ambiguous since {{.X}} might be an HTML text node,
  77. // or a URL prefix in an HTML attribute. The context of {{.X}} is
  78. // used to figure out how to escape it, but that context depends on
  79. // the run-time value of {{.C}} which is not statically known.
  80. //
  81. // The problem is usually something like missing quotes or angle
  82. // brackets, or can be avoided by refactoring to put the two contexts
  83. // into different branches of an if, range or with. If the problem
  84. // is in a {{range}} over a collection that should never be empty,
  85. // adding a dummy {{else}} can help.
  86. ErrBranchEnd
  87. // ErrEndContext: "... ends in a non-text context: ..."
  88. // Examples:
  89. // <div
  90. // <div title="no close quote>
  91. // <script>f()
  92. // Discussion:
  93. // Executed templates should produce a DocumentFragment of HTML.
  94. // Templates that end without closing tags will trigger this error.
  95. // Templates that should not be used in an HTML context or that
  96. // produce incomplete Fragments should not be executed directly.
  97. //
  98. // {{define "main"}} <script>{{template "helper"}}</script> {{end}}
  99. // {{define "helper"}} document.write(' <div title=" ') {{end}}
  100. //
  101. // "helper" does not produce a valid document fragment, so should
  102. // not be Executed directly.
  103. ErrEndContext
  104. // ErrNoSuchTemplate: "no such template ..."
  105. // Examples:
  106. // {{define "main"}}<div {{template "attrs"}}>{{end}}
  107. // {{define "attrs"}}href="{{.URL}}"{{end}}
  108. // Discussion:
  109. // Package html/template looks through template calls to compute the
  110. // context.
  111. // Here the {{.URL}} in "attrs" must be treated as a URL when called
  112. // from "main", but you will get this error if "attrs" is not defined
  113. // when "main" is parsed.
  114. ErrNoSuchTemplate
  115. // ErrOutputContext: "cannot compute output context for template ..."
  116. // Examples:
  117. // {{define "t"}}{{if .T}}{{template "t" .T}}{{end}}{{.H}}",{{end}}
  118. // Discussion:
  119. // A recursive template does not end in the same context in which it
  120. // starts, and a reliable output context cannot be computed.
  121. // Look for typos in the named template.
  122. // If the template should not be called in the named start context,
  123. // look for calls to that template in unexpected contexts.
  124. // Maybe refactor recursive templates to not be recursive.
  125. ErrOutputContext
  126. // ErrPartialCharset: "unfinished JS regexp charset in ..."
  127. // Example:
  128. // <script>var pattern = /foo[{{.Chars}}]/</script>
  129. // Discussion:
  130. // Package html/template does not support interpolation into regular
  131. // expression literal character sets.
  132. ErrPartialCharset
  133. // ErrPartialEscape: "unfinished escape sequence in ..."
  134. // Example:
  135. // <script>alert("\{{.X}}")</script>
  136. // Discussion:
  137. // Package html/template does not support actions following a
  138. // backslash.
  139. // This is usually an error and there are better solutions; for
  140. // example
  141. // <script>alert("{{.X}}")</script>
  142. // should work, and if {{.X}} is a partial escape sequence such as
  143. // "xA0", mark the whole sequence as safe content: JSStr(`\xA0`)
  144. ErrPartialEscape
  145. // ErrRangeLoopReentry: "on range loop re-entry: ..."
  146. // Example:
  147. // <script>var x = [{{range .}}'{{.}},{{end}}]</script>
  148. // Discussion:
  149. // If an iteration through a range would cause it to end in a
  150. // different context than an earlier pass, there is no single context.
  151. // In the example, there is missing a quote, so it is not clear
  152. // whether {{.}} is meant to be inside a JS string or in a JS value
  153. // context. The second iteration would produce something like
  154. //
  155. // <script>var x = ['firstValue,'secondValue]</script>
  156. ErrRangeLoopReentry
  157. // ErrSlashAmbig: '/' could start a division or regexp.
  158. // Example:
  159. // <script>
  160. // {{if .C}}var x = 1{{end}}
  161. // /-{{.N}}/i.test(x) ? doThis : doThat();
  162. // </script>
  163. // Discussion:
  164. // The example above could produce `var x = 1/-2/i.test(s)...`
  165. // in which the first '/' is a mathematical division operator or it
  166. // could produce `/-2/i.test(s)` in which the first '/' starts a
  167. // regexp literal.
  168. // Look for missing semicolons inside branches, and maybe add
  169. // parentheses to make it clear which interpretation you intend.
  170. ErrSlashAmbig
  171. // ErrPredefinedEscaper: "predefined escaper ... disallowed in template"
  172. // Example:
  173. // <div class={{. | html}}>Hello<div>
  174. // Discussion:
  175. // Package html/template already contextually escapes all pipelines to
  176. // produce HTML output safe against code injection. Manually escaping
  177. // pipeline output using the predefined escapers "html" or "urlquery" is
  178. // unnecessary, and may affect the correctness or safety of the escaped
  179. // pipeline output in Go 1.8 and earlier.
  180. //
  181. // In most cases, such as the given example, this error can be resolved by
  182. // simply removing the predefined escaper from the pipeline and letting the
  183. // contextual autoescaper handle the escaping of the pipeline. In other
  184. // instances, where the predefined escaper occurs in the middle of a
  185. // pipeline where subsequent commands expect escaped input, e.g.
  186. // {{.X | html | makeALink}}
  187. // where makeALink does
  188. // return `<a href="`+input+`">link</a>`
  189. // consider refactoring the surrounding template to make use of the
  190. // contextual autoescaper, i.e.
  191. // <a href="{{.X}}">link</a>
  192. //
  193. // To ease migration to Go 1.9 and beyond, "html" and "urlquery" will
  194. // continue to be allowed as the last command in a pipeline. However, if the
  195. // pipeline occurs in an unquoted attribute value context, "html" is
  196. // disallowed. Avoid using "html" and "urlquery" entirely in new templates.
  197. ErrPredefinedEscaper
  198. )
  199. func (e *Error) Error() string {
  200. switch {
  201. case e.Node != nil:
  202. loc, _ := (*parse.Tree)(nil).ErrorContext(e.Node)
  203. return fmt.Sprintf("html/template:%s: %s", loc, e.Description)
  204. case e.Line != 0:
  205. return fmt.Sprintf("html/template:%s:%d: %s", e.Name, e.Line, e.Description)
  206. case e.Name != "":
  207. return fmt.Sprintf("html/template:%s: %s", e.Name, e.Description)
  208. }
  209. return "html/template: " + e.Description
  210. }
  211. // errorf creates an error given a format string f and args.
  212. // The template Name still needs to be supplied.
  213. func errorf(k ErrorCode, node parse.Node, line int, f string, args ...interface{}) *Error {
  214. return &Error{k, node, "", line, fmt.Sprintf(f, args...)}
  215. }