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