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