hugo

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

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

content_test.go (13958B)

    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 //go:build go1.13 && !windows
    6 // +build go1.13,!windows
    7 
    8 package template
    9 
   10 import (
   11 	"bytes"
   12 	"fmt"
   13 	htmltemplate "html/template"
   14 	"strings"
   15 	"testing"
   16 )
   17 
   18 func TestTypedContent(t *testing.T) {
   19 	data := []any{
   20 		`<b> "foo%" O'Reilly &bar;`,
   21 		htmltemplate.CSS(`a[href =~ "//example.com"]#foo`),
   22 		htmltemplate.HTML(`Hello, <b>World</b> &amp;tc!`),
   23 		htmltemplate.HTMLAttr(` dir="ltr"`),
   24 		htmltemplate.JS(`c && alert("Hello, World!");`),
   25 		htmltemplate.JSStr(`Hello, World & O'Reilly\u0021`),
   26 		htmltemplate.URL(`greeting=H%69,&addressee=(World)`),
   27 		htmltemplate.Srcset(`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`),
   28 		htmltemplate.URL(`,foo/,`),
   29 	}
   30 
   31 	// For each content sensitive escaper, see how it does on
   32 	// each of the typed strings above.
   33 	tests := []struct {
   34 		// A template containing a single {{.}}.
   35 		input string
   36 		want  []string
   37 	}{
   38 		{
   39 			`<style>{{.}} { color: blue }</style>`,
   40 			[]string{
   41 				`ZgotmplZ`,
   42 				// Allowed but not escaped.
   43 				`a[href =~ "//example.com"]#foo`,
   44 				`ZgotmplZ`,
   45 				`ZgotmplZ`,
   46 				`ZgotmplZ`,
   47 				`ZgotmplZ`,
   48 				`ZgotmplZ`,
   49 				`ZgotmplZ`,
   50 				`ZgotmplZ`,
   51 			},
   52 		},
   53 		{
   54 			`<div style="{{.}}">`,
   55 			[]string{
   56 				`ZgotmplZ`,
   57 				// Allowed and HTML escaped.
   58 				`a[href =~ &#34;//example.com&#34;]#foo`,
   59 				`ZgotmplZ`,
   60 				`ZgotmplZ`,
   61 				`ZgotmplZ`,
   62 				`ZgotmplZ`,
   63 				`ZgotmplZ`,
   64 				`ZgotmplZ`,
   65 				`ZgotmplZ`,
   66 			},
   67 		},
   68 		{
   69 			`{{.}}`,
   70 			[]string{
   71 				`&lt;b&gt; &#34;foo%&#34; O&#39;Reilly &amp;bar;`,
   72 				`a[href =~ &#34;//example.com&#34;]#foo`,
   73 				// Not escaped.
   74 				`Hello, <b>World</b> &amp;tc!`,
   75 				` dir=&#34;ltr&#34;`,
   76 				`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
   77 				`Hello, World &amp; O&#39;Reilly\u0021`,
   78 				`greeting=H%69,&amp;addressee=(World)`,
   79 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
   80 				`,foo/,`,
   81 			},
   82 		},
   83 		{
   84 			`<a{{.}}>`,
   85 			[]string{
   86 				`ZgotmplZ`,
   87 				`ZgotmplZ`,
   88 				`ZgotmplZ`,
   89 				// Allowed and HTML escaped.
   90 				` dir="ltr"`,
   91 				`ZgotmplZ`,
   92 				`ZgotmplZ`,
   93 				`ZgotmplZ`,
   94 				`ZgotmplZ`,
   95 				`ZgotmplZ`,
   96 			},
   97 		},
   98 		{
   99 			`<a title={{.}}>`,
  100 			[]string{
  101 				`&lt;b&gt;&#32;&#34;foo%&#34;&#32;O&#39;Reilly&#32;&amp;bar;`,
  102 				`a[href&#32;&#61;~&#32;&#34;//example.com&#34;]#foo`,
  103 				// Tags stripped, spaces escaped, entity not re-escaped.
  104 				`Hello,&#32;World&#32;&amp;tc!`,
  105 				`&#32;dir&#61;&#34;ltr&#34;`,
  106 				`c&#32;&amp;&amp;&#32;alert(&#34;Hello,&#32;World!&#34;);`,
  107 				`Hello,&#32;World&#32;&amp;&#32;O&#39;Reilly\u0021`,
  108 				`greeting&#61;H%69,&amp;addressee&#61;(World)`,
  109 				`greeting&#61;H%69,&amp;addressee&#61;(World)&#32;2x,&#32;https://golang.org/favicon.ico&#32;500.5w`,
  110 				`,foo/,`,
  111 			},
  112 		},
  113 		{
  114 			`<a title='{{.}}'>`,
  115 			[]string{
  116 				`&lt;b&gt; &#34;foo%&#34; O&#39;Reilly &amp;bar;`,
  117 				`a[href =~ &#34;//example.com&#34;]#foo`,
  118 				// Tags stripped, entity not re-escaped.
  119 				`Hello, World &amp;tc!`,
  120 				` dir=&#34;ltr&#34;`,
  121 				`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
  122 				`Hello, World &amp; O&#39;Reilly\u0021`,
  123 				`greeting=H%69,&amp;addressee=(World)`,
  124 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
  125 				`,foo/,`,
  126 			},
  127 		},
  128 		{
  129 			`<textarea>{{.}}</textarea>`,
  130 			[]string{
  131 				`&lt;b&gt; &#34;foo%&#34; O&#39;Reilly &amp;bar;`,
  132 				`a[href =~ &#34;//example.com&#34;]#foo`,
  133 				// Angle brackets escaped to prevent injection of close tags, entity not re-escaped.
  134 				`Hello, &lt;b&gt;World&lt;/b&gt; &amp;tc!`,
  135 				` dir=&#34;ltr&#34;`,
  136 				`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
  137 				`Hello, World &amp; O&#39;Reilly\u0021`,
  138 				`greeting=H%69,&amp;addressee=(World)`,
  139 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
  140 				`,foo/,`,
  141 			},
  142 		},
  143 		{
  144 			`<script>alert({{.}})</script>`,
  145 			[]string{
  146 				`"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
  147 				`"a[href =~ \"//example.com\"]#foo"`,
  148 				`"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
  149 				`" dir=\"ltr\""`,
  150 				// Not escaped.
  151 				`c && alert("Hello, World!");`,
  152 				// Escape sequence not over-escaped.
  153 				`"Hello, World & O'Reilly\u0021"`,
  154 				`"greeting=H%69,\u0026addressee=(World)"`,
  155 				`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
  156 				`",foo/,"`,
  157 			},
  158 		},
  159 		{
  160 			`<button onclick="alert({{.}})">`,
  161 			[]string{
  162 				`&#34;\u003cb\u003e \&#34;foo%\&#34; O&#39;Reilly \u0026bar;&#34;`,
  163 				`&#34;a[href =~ \&#34;//example.com\&#34;]#foo&#34;`,
  164 				`&#34;Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!&#34;`,
  165 				`&#34; dir=\&#34;ltr\&#34;&#34;`,
  166 				// Not JS escaped but HTML escaped.
  167 				`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
  168 				// Escape sequence not over-escaped.
  169 				`&#34;Hello, World &amp; O&#39;Reilly\u0021&#34;`,
  170 				`&#34;greeting=H%69,\u0026addressee=(World)&#34;`,
  171 				`&#34;greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w&#34;`,
  172 				`&#34;,foo/,&#34;`,
  173 			},
  174 		},
  175 		{
  176 			`<script>alert("{{.}}")</script>`,
  177 			[]string{
  178 				`\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
  179 				`a[href =~ \u0022\/\/example.com\u0022]#foo`,
  180 				`Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
  181 				` dir=\u0022ltr\u0022`,
  182 				`c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
  183 				// Escape sequence not over-escaped.
  184 				`Hello, World \u0026 O\u0027Reilly\u0021`,
  185 				`greeting=H%69,\u0026addressee=(World)`,
  186 				`greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
  187 				`,foo\/,`,
  188 			},
  189 		},
  190 		{
  191 			`<script type="text/javascript">alert("{{.}}")</script>`,
  192 			[]string{
  193 				`\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
  194 				`a[href =~ \u0022\/\/example.com\u0022]#foo`,
  195 				`Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
  196 				` dir=\u0022ltr\u0022`,
  197 				`c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
  198 				// Escape sequence not over-escaped.
  199 				`Hello, World \u0026 O\u0027Reilly\u0021`,
  200 				`greeting=H%69,\u0026addressee=(World)`,
  201 				`greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
  202 				`,foo\/,`,
  203 			},
  204 		},
  205 		{
  206 			`<script type="text/javascript">alert({{.}})</script>`,
  207 			[]string{
  208 				`"\u003cb\u003e \"foo%\" O'Reilly \u0026bar;"`,
  209 				`"a[href =~ \"//example.com\"]#foo"`,
  210 				`"Hello, \u003cb\u003eWorld\u003c/b\u003e \u0026amp;tc!"`,
  211 				`" dir=\"ltr\""`,
  212 				// Not escaped.
  213 				`c && alert("Hello, World!");`,
  214 				// Escape sequence not over-escaped.
  215 				`"Hello, World & O'Reilly\u0021"`,
  216 				`"greeting=H%69,\u0026addressee=(World)"`,
  217 				`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
  218 				`",foo/,"`,
  219 			},
  220 		},
  221 		{
  222 			// Not treated as JS. The output is same as for <div>{{.}}</div>
  223 			`<script type="text/template">{{.}}</script>`,
  224 			[]string{
  225 				`&lt;b&gt; &#34;foo%&#34; O&#39;Reilly &amp;bar;`,
  226 				`a[href =~ &#34;//example.com&#34;]#foo`,
  227 				// Not escaped.
  228 				`Hello, <b>World</b> &amp;tc!`,
  229 				` dir=&#34;ltr&#34;`,
  230 				`c &amp;&amp; alert(&#34;Hello, World!&#34;);`,
  231 				`Hello, World &amp; O&#39;Reilly\u0021`,
  232 				`greeting=H%69,&amp;addressee=(World)`,
  233 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
  234 				`,foo/,`,
  235 			},
  236 		},
  237 		{
  238 			`<button onclick='alert("{{.}}")'>`,
  239 			[]string{
  240 				`\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
  241 				`a[href =~ \u0022\/\/example.com\u0022]#foo`,
  242 				`Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
  243 				` dir=\u0022ltr\u0022`,
  244 				`c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
  245 				// Escape sequence not over-escaped.
  246 				`Hello, World \u0026 O\u0027Reilly\u0021`,
  247 				`greeting=H%69,\u0026addressee=(World)`,
  248 				`greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
  249 				`,foo\/,`,
  250 			},
  251 		},
  252 		{
  253 			`<a href="?q={{.}}">`,
  254 			[]string{
  255 				`%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`,
  256 				`a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`,
  257 				`Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
  258 				`%20dir%3d%22ltr%22`,
  259 				`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
  260 				`Hello%2c%20World%20%26%20O%27Reilly%5cu0021`,
  261 				// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done.
  262 				`greeting=H%69,&amp;addressee=%28World%29`,
  263 				`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
  264 				`,foo/,`,
  265 			},
  266 		},
  267 		{
  268 			`<style>body { background: url('?img={{.}}') }</style>`,
  269 			[]string{
  270 				`%3cb%3e%20%22foo%25%22%20O%27Reilly%20%26bar%3b`,
  271 				`a%5bhref%20%3d~%20%22%2f%2fexample.com%22%5d%23foo`,
  272 				`Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
  273 				`%20dir%3d%22ltr%22`,
  274 				`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
  275 				`Hello%2c%20World%20%26%20O%27Reilly%5cu0021`,
  276 				// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done.
  277 				`greeting=H%69,&addressee=%28World%29`,
  278 				`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
  279 				`,foo/,`,
  280 			},
  281 		},
  282 		{
  283 			`<img srcset="{{.}}">`,
  284 			[]string{
  285 				`#ZgotmplZ`,
  286 				`#ZgotmplZ`,
  287 				// Commas are not esacped
  288 				`Hello,#ZgotmplZ`,
  289 				// Leading spaces are not percent escapes.
  290 				` dir=%22ltr%22`,
  291 				// Spaces after commas are not percent escaped.
  292 				`#ZgotmplZ, World!%22%29;`,
  293 				`Hello,#ZgotmplZ`,
  294 				`greeting=H%69%2c&amp;addressee=%28World%29`,
  295 				// Metadata is not escaped.
  296 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
  297 				`%2cfoo/%2c`,
  298 			},
  299 		},
  300 		{
  301 			`<img srcset={{.}}>`,
  302 			[]string{
  303 				`#ZgotmplZ`,
  304 				`#ZgotmplZ`,
  305 				`Hello,#ZgotmplZ`,
  306 				// Spaces are HTML escaped not %-escaped
  307 				`&#32;dir&#61;%22ltr%22`,
  308 				`#ZgotmplZ,&#32;World!%22%29;`,
  309 				`Hello,#ZgotmplZ`,
  310 				`greeting&#61;H%69%2c&amp;addressee&#61;%28World%29`,
  311 				`greeting&#61;H%69,&amp;addressee&#61;(World)&#32;2x,&#32;https://golang.org/favicon.ico&#32;500.5w`,
  312 				// Commas are escaped.
  313 				`%2cfoo/%2c`,
  314 			},
  315 		},
  316 		{
  317 			`<img srcset="{{.}} 2x, https://golang.org/ 500.5w">`,
  318 			[]string{
  319 				`#ZgotmplZ`,
  320 				`#ZgotmplZ`,
  321 				`Hello,#ZgotmplZ`,
  322 				` dir=%22ltr%22`,
  323 				`#ZgotmplZ, World!%22%29;`,
  324 				`Hello,#ZgotmplZ`,
  325 				`greeting=H%69%2c&amp;addressee=%28World%29`,
  326 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
  327 				`%2cfoo/%2c`,
  328 			},
  329 		},
  330 		{
  331 			`<img srcset="http://godoc.org/ {{.}}, https://golang.org/ 500.5w">`,
  332 			[]string{
  333 				`#ZgotmplZ`,
  334 				`#ZgotmplZ`,
  335 				`Hello,#ZgotmplZ`,
  336 				` dir=%22ltr%22`,
  337 				`#ZgotmplZ, World!%22%29;`,
  338 				`Hello,#ZgotmplZ`,
  339 				`greeting=H%69%2c&amp;addressee=%28World%29`,
  340 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
  341 				`%2cfoo/%2c`,
  342 			},
  343 		},
  344 		{
  345 			`<img srcset="http://godoc.org/?q={{.}} 2x, https://golang.org/ 500.5w">`,
  346 			[]string{
  347 				`#ZgotmplZ`,
  348 				`#ZgotmplZ`,
  349 				`Hello,#ZgotmplZ`,
  350 				` dir=%22ltr%22`,
  351 				`#ZgotmplZ, World!%22%29;`,
  352 				`Hello,#ZgotmplZ`,
  353 				`greeting=H%69%2c&amp;addressee=%28World%29`,
  354 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
  355 				`%2cfoo/%2c`,
  356 			},
  357 		},
  358 		{
  359 			`<img srcset="http://godoc.org/ 2x, {{.}} 500.5w">`,
  360 			[]string{
  361 				`#ZgotmplZ`,
  362 				`#ZgotmplZ`,
  363 				`Hello,#ZgotmplZ`,
  364 				` dir=%22ltr%22`,
  365 				`#ZgotmplZ, World!%22%29;`,
  366 				`Hello,#ZgotmplZ`,
  367 				`greeting=H%69%2c&amp;addressee=%28World%29`,
  368 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
  369 				`%2cfoo/%2c`,
  370 			},
  371 		},
  372 		{
  373 			`<img srcset="http://godoc.org/ 2x, https://golang.org/ {{.}}">`,
  374 			[]string{
  375 				`#ZgotmplZ`,
  376 				`#ZgotmplZ`,
  377 				`Hello,#ZgotmplZ`,
  378 				` dir=%22ltr%22`,
  379 				`#ZgotmplZ, World!%22%29;`,
  380 				`Hello,#ZgotmplZ`,
  381 				`greeting=H%69%2c&amp;addressee=%28World%29`,
  382 				`greeting=H%69,&amp;addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
  383 				`%2cfoo/%2c`,
  384 			},
  385 		},
  386 	}
  387 
  388 	for _, test := range tests {
  389 		tmpl := Must(New("x").Parse(test.input))
  390 		pre := strings.Index(test.input, "{{.}}")
  391 		post := len(test.input) - (pre + 5)
  392 		var b bytes.Buffer
  393 		for i, x := range data {
  394 			b.Reset()
  395 			if err := tmpl.Execute(&b, x); err != nil {
  396 				t.Errorf("%q with %v: %s", test.input, x, err)
  397 				continue
  398 			}
  399 			if want, got := test.want[i], b.String()[pre:b.Len()-post]; want != got {
  400 				t.Errorf("%q with %v:\nwant\n\t%q,\ngot\n\t%q\n", test.input, x, want, got)
  401 				continue
  402 			}
  403 		}
  404 	}
  405 }
  406 
  407 // Test that we print using the String method. Was issue 3073.
  408 type myStringer struct {
  409 	v int
  410 }
  411 
  412 func (s *myStringer) String() string {
  413 	return fmt.Sprintf("string=%d", s.v)
  414 }
  415 
  416 type errorer struct {
  417 	v int
  418 }
  419 
  420 func (s *errorer) Error() string {
  421 	return fmt.Sprintf("error=%d", s.v)
  422 }
  423 
  424 func TestStringer(t *testing.T) {
  425 	s := &myStringer{3}
  426 	b := new(bytes.Buffer)
  427 	tmpl := Must(New("x").Parse("{{.}}"))
  428 	if err := tmpl.Execute(b, s); err != nil {
  429 		t.Fatal(err)
  430 	}
  431 	var expect = "string=3"
  432 	if b.String() != expect {
  433 		t.Errorf("expected %q got %q", expect, b.String())
  434 	}
  435 	e := &errorer{7}
  436 	b.Reset()
  437 	if err := tmpl.Execute(b, e); err != nil {
  438 		t.Fatal(err)
  439 	}
  440 	expect = "error=7"
  441 	if b.String() != expect {
  442 		t.Errorf("expected %q got %q", expect, b.String())
  443 	}
  444 }
  445 
  446 // https://golang.org/issue/5982
  447 func TestEscapingNilNonemptyInterfaces(t *testing.T) {
  448 	tmpl := Must(New("x").Parse("{{.E}}"))
  449 
  450 	got := new(bytes.Buffer)
  451 	testData := struct{ E error }{} // any non-empty interface here will do; error is just ready at hand
  452 	tmpl.Execute(got, testData)
  453 
  454 	// A non-empty interface should print like an empty interface.
  455 	want := new(bytes.Buffer)
  456 	data := struct{ E any }{}
  457 	tmpl.Execute(want, data)
  458 
  459 	if !bytes.Equal(want.Bytes(), got.Bytes()) {
  460 		t.Errorf("expected %q got %q", string(want.Bytes()), string(got.Bytes()))
  461 	}
  462 }