diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index fdfb21925ab74..e262892069792 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -294,9 +294,7 @@ func timeEstimateString(timeSec any) string {
return util.TimeEstimateString(v)
}
-type QueryString string
-
-func queryBuild(a ...any) QueryString {
+func queryBuild(a ...any) template.URL {
var s string
if len(a)%2 == 1 {
if v, ok := a[0].(string); ok {
@@ -304,7 +302,7 @@ func queryBuild(a ...any) QueryString {
panic("queryBuild: invalid argument")
}
s = v
- } else if v, ok := a[0].(QueryString); ok {
+ } else if v, ok := a[0].(template.URL); ok {
s = string(v)
} else {
panic("queryBuild: invalid argument")
@@ -356,7 +354,7 @@ func queryBuild(a ...any) QueryString {
if s != "" && s != "&" && s[len(s)-1] == '&' {
s = s[:len(s)-1]
}
- return QueryString(s)
+ return template.URL(s)
}
func panicIfDevOrTesting() {
diff --git a/modules/templates/helper_test.go b/modules/templates/helper_test.go
index 3e17e86c66256..63a14e632f11d 100644
--- a/modules/templates/helper_test.go
+++ b/modules/templates/helper_test.go
@@ -8,6 +8,7 @@ import (
"strings"
"testing"
+ "code.gitea.io/gitea/modules/htmlutil"
"code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
@@ -102,3 +103,37 @@ func TestTemplateTruthy(t *testing.T) {
}
assert.True(t, truthyCount != 0 && truthyCount != len(cases))
}
+
+func TestTemplateEscape(t *testing.T) {
+ execTmpl := func(code string) string {
+ tmpl := template.New("test")
+ tmpl.Funcs(template.FuncMap{"QueryBuild": queryBuild, "HTMLFormat": htmlutil.HTMLFormat})
+ template.Must(tmpl.Parse(code))
+ w := &strings.Builder{}
+ assert.NoError(t, tmpl.Execute(w, nil))
+ return w.String()
+ }
+
+ t.Run("Golang URL Escape", func(t *testing.T) {
+ // Golang template considers "href", "*src*", "*uri*", "*url*" (and more) ... attributes as contentTypeURL and does auto-escaping
+ actual := execTmpl(``)
+ assert.Equal(t, ``, actual)
+ actual = execTmpl(``)
+ assert.Equal(t, ``, actual)
+ })
+ t.Run("Golang URL No-escape", func(t *testing.T) {
+ // non-URL content isn't auto-escaped
+ actual := execTmpl(``)
+ assert.Equal(t, ``, actual)
+ })
+ t.Run("QueryBuild", func(t *testing.T) {
+ actual := execTmpl(``)
+ assert.Equal(t, ``, actual)
+ actual = execTmpl(``)
+ assert.Equal(t, ``, actual)
+ })
+ t.Run("HTMLFormat", func(t *testing.T) {
+ actual := execTmpl("{{HTMLFormat `%s` `\"` `<>`}}")
+ assert.Equal(t, `<>`, actual)
+ })
+}