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