...

Source file src/html/template/error.go

Documentation: html/template

		 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  

View as plain text