hugo

Fork of github.com/gohugoio/hugo with reverse pagination support

git clone git://git.shimmy1996.com/hugo.git

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 }