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) + }) +}