gitea源码

helper_test.go 6.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package templates
  4. import (
  5. "html/template"
  6. "strings"
  7. "testing"
  8. "code.gitea.io/gitea/modules/util"
  9. "github.com/stretchr/testify/assert"
  10. )
  11. func TestSubjectBodySeparator(t *testing.T) {
  12. test := func(input, subject, body string) {
  13. loc := mailSubjectSplit.FindStringIndex(input)
  14. if loc == nil {
  15. assert.Empty(t, subject, "no subject found, but one expected")
  16. assert.Equal(t, body, input)
  17. } else {
  18. assert.Equal(t, subject, input[0:loc[0]])
  19. assert.Equal(t, body, input[loc[1]:])
  20. }
  21. }
  22. test("Simple\n---------------\nCase",
  23. "Simple\n",
  24. "\nCase")
  25. test("Only\nBody",
  26. "",
  27. "Only\nBody")
  28. test("Minimal\n---\nseparator",
  29. "Minimal\n",
  30. "\nseparator")
  31. test("False --- separator",
  32. "",
  33. "False --- separator")
  34. test("False\n--- separator",
  35. "",
  36. "False\n--- separator")
  37. test("False ---\nseparator",
  38. "",
  39. "False ---\nseparator")
  40. test("With extra spaces\n----- \t \nBody",
  41. "With extra spaces\n",
  42. "\nBody")
  43. test("With leading spaces\n -------\nOnly body",
  44. "",
  45. "With leading spaces\n -------\nOnly body")
  46. test("Multiple\n---\n-------\n---\nSeparators",
  47. "Multiple\n",
  48. "\n-------\n---\nSeparators")
  49. test("Insufficient\n--\nSeparators",
  50. "",
  51. "Insufficient\n--\nSeparators")
  52. }
  53. func TestSanitizeHTML(t *testing.T) {
  54. assert.Equal(t, template.HTML(`<a href="/" rel="nofollow">link</a> xss <div>inline</div>`), SanitizeHTML(`<a href="/">link</a> <a href="javascript:">xss</a> <div style="dangerous">inline</div>`))
  55. }
  56. func TestTemplateIif(t *testing.T) {
  57. tmpl := template.New("test")
  58. tmpl.Funcs(template.FuncMap{"Iif": iif})
  59. template.Must(tmpl.Parse(`{{if .Value}}true{{else}}false{{end}}:{{Iif .Value "true" "false"}}`))
  60. cases := []any{nil, false, true, "", "string", 0, 1}
  61. w := &strings.Builder{}
  62. truthyCount := 0
  63. for i, v := range cases {
  64. w.Reset()
  65. assert.NoError(t, tmpl.Execute(w, struct{ Value any }{v}), "case %d (%T) %#v fails", i, v, v)
  66. out := w.String()
  67. truthyCount += util.Iif(out == "true:true", 1, 0)
  68. truthyMatches := out == "true:true" || out == "false:false"
  69. assert.True(t, truthyMatches, "case %d (%T) %#v fail: %s", i, v, v, out)
  70. }
  71. assert.True(t, truthyCount != 0 && truthyCount != len(cases))
  72. }
  73. func TestTemplateEscape(t *testing.T) {
  74. execTmpl := func(code string) string {
  75. tmpl := template.New("test")
  76. tmpl.Funcs(template.FuncMap{"QueryBuild": QueryBuild, "HTMLFormat": htmlFormat})
  77. template.Must(tmpl.Parse(code))
  78. w := &strings.Builder{}
  79. assert.NoError(t, tmpl.Execute(w, nil))
  80. return w.String()
  81. }
  82. t.Run("Golang URL Escape", func(t *testing.T) {
  83. // Golang template considers "href", "*src*", "*uri*", "*url*" (and more) ... attributes as contentTypeURL and does auto-escaping
  84. actual := execTmpl(`<a href="?a={{"%"}}"></a>`)
  85. assert.Equal(t, `<a href="?a=%25"></a>`, actual)
  86. actual = execTmpl(`<a data-xxx-url="?a={{"%"}}"></a>`)
  87. assert.Equal(t, `<a data-xxx-url="?a=%25"></a>`, actual)
  88. })
  89. t.Run("Golang URL No-escape", func(t *testing.T) {
  90. // non-URL content isn't auto-escaped
  91. actual := execTmpl(`<a data-link="?a={{"%"}}"></a>`)
  92. assert.Equal(t, `<a data-link="?a=%"></a>`, actual)
  93. })
  94. t.Run("QueryBuild", func(t *testing.T) {
  95. actual := execTmpl(`<a href="{{QueryBuild "?" "a" "%"}}"></a>`)
  96. assert.Equal(t, `<a href="?a=%25"></a>`, actual)
  97. actual = execTmpl(`<a href="?{{QueryBuild "a" "%"}}"></a>`)
  98. assert.Equal(t, `<a href="?a=%25"></a>`, actual)
  99. })
  100. t.Run("HTMLFormat", func(t *testing.T) {
  101. actual := execTmpl("{{HTMLFormat `<a k=\"%s\">%s</a>` `\"` `<>`}}")
  102. assert.Equal(t, `<a k="&#34;">&lt;&gt;</a>`, actual)
  103. })
  104. }
  105. func TestQueryBuild(t *testing.T) {
  106. t.Run("construct", func(t *testing.T) {
  107. assert.Empty(t, string(QueryBuild()))
  108. assert.Empty(t, string(QueryBuild("a", nil, "b", false, "c", 0, "d", "")))
  109. assert.Equal(t, "a=1&b=true", string(QueryBuild("a", 1, "b", "true")))
  110. // path with query parameters
  111. assert.Equal(t, "/?k=1", string(QueryBuild("/", "k", 1)))
  112. assert.Equal(t, "/", string(QueryBuild("/?k=a", "k", 0)))
  113. // no path but question mark with query parameters
  114. assert.Equal(t, "?k=1", string(QueryBuild("?", "k", 1)))
  115. assert.Equal(t, "?", string(QueryBuild("?", "k", 0)))
  116. assert.Equal(t, "path?k=1", string(QueryBuild("path?", "k", 1)))
  117. assert.Equal(t, "path", string(QueryBuild("path?", "k", 0)))
  118. // only query parameters
  119. assert.Equal(t, "&k=1", string(QueryBuild("&", "k", 1)))
  120. assert.Empty(t, string(QueryBuild("&", "k", 0)))
  121. assert.Empty(t, string(QueryBuild("&k=a", "k", 0)))
  122. assert.Empty(t, string(QueryBuild("k=a&", "k", 0)))
  123. assert.Equal(t, "a=1&b=2", string(QueryBuild("a=1", "b", 2)))
  124. assert.Equal(t, "&a=1&b=2", string(QueryBuild("&a=1", "b", 2)))
  125. assert.Equal(t, "a=1&b=2&", string(QueryBuild("a=1&", "b", 2)))
  126. })
  127. t.Run("replace", func(t *testing.T) {
  128. assert.Equal(t, "a=1&c=d&e=f", string(QueryBuild("a=b&c=d&e=f", "a", 1)))
  129. assert.Equal(t, "a=b&c=1&e=f", string(QueryBuild("a=b&c=d&e=f", "c", 1)))
  130. assert.Equal(t, "a=b&c=d&e=1", string(QueryBuild("a=b&c=d&e=f", "e", 1)))
  131. assert.Equal(t, "a=b&c=d&e=f&k=1", string(QueryBuild("a=b&c=d&e=f", "k", 1)))
  132. })
  133. t.Run("replace-&", func(t *testing.T) {
  134. assert.Equal(t, "&a=1&c=d&e=f", string(QueryBuild("&a=b&c=d&e=f", "a", 1)))
  135. assert.Equal(t, "&a=b&c=1&e=f", string(QueryBuild("&a=b&c=d&e=f", "c", 1)))
  136. assert.Equal(t, "&a=b&c=d&e=1", string(QueryBuild("&a=b&c=d&e=f", "e", 1)))
  137. assert.Equal(t, "&a=b&c=d&e=f&k=1", string(QueryBuild("&a=b&c=d&e=f", "k", 1)))
  138. })
  139. t.Run("delete", func(t *testing.T) {
  140. assert.Equal(t, "c=d&e=f", string(QueryBuild("a=b&c=d&e=f", "a", "")))
  141. assert.Equal(t, "a=b&e=f", string(QueryBuild("a=b&c=d&e=f", "c", "")))
  142. assert.Equal(t, "a=b&c=d", string(QueryBuild("a=b&c=d&e=f", "e", "")))
  143. assert.Equal(t, "a=b&c=d&e=f", string(QueryBuild("a=b&c=d&e=f", "k", "")))
  144. })
  145. t.Run("delete-&", func(t *testing.T) {
  146. assert.Equal(t, "&c=d&e=f", string(QueryBuild("&a=b&c=d&e=f", "a", "")))
  147. assert.Equal(t, "&a=b&e=f", string(QueryBuild("&a=b&c=d&e=f", "c", "")))
  148. assert.Equal(t, "&a=b&c=d", string(QueryBuild("&a=b&c=d&e=f", "e", "")))
  149. assert.Equal(t, "&a=b&c=d&e=f", string(QueryBuild("&a=b&c=d&e=f", "k", "")))
  150. })
  151. }