diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index d2fe6a28cd..8e891bdc24 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -9,6 +9,7 @@ import (
"html"
"html/template"
"net/url"
+ "slices"
"strings"
"time"
@@ -34,7 +35,8 @@ func NewFuncMap() template.FuncMap {
// html/template related functions
"dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names.
"Eval": Eval,
- "Safe": Safe,
+ "SafeHTML": SafeHTML,
+ "HTMLFormat": HTMLFormat,
"Escape": Escape,
"QueryEscape": url.QueryEscape,
"JSEscape": JSEscapeSafe,
@@ -180,8 +182,25 @@ func NewFuncMap() template.FuncMap {
}
}
-// Safe render raw as HTML
-func Safe(s any) template.HTML {
+func HTMLFormat(s string, rawArgs ...any) template.HTML {
+ args := slices.Clone(rawArgs)
+ for i, v := range args {
+ switch v := v.(type) {
+ case nil, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, template.HTML:
+ // for most basic types (including template.HTML which is safe), just do nothing and use it
+ case string:
+ args[i] = template.HTMLEscapeString(v)
+ case fmt.Stringer:
+ args[i] = template.HTMLEscapeString(v.String())
+ default:
+ args[i] = template.HTMLEscapeString(fmt.Sprint(v))
+ }
+ }
+ return template.HTML(fmt.Sprintf(s, args...))
+}
+
+// SafeHTML render raw as HTML
+func SafeHTML(s any) template.HTML {
switch v := s.(type) {
case string:
return template.HTML(v)
diff --git a/modules/templates/helper_test.go b/modules/templates/helper_test.go
index 739a92f34f..8f5d633d4f 100644
--- a/modules/templates/helper_test.go
+++ b/modules/templates/helper_test.go
@@ -4,6 +4,7 @@
package templates
import (
+ "html/template"
"testing"
"github.com/stretchr/testify/assert"
@@ -56,3 +57,7 @@ func TestSubjectBodySeparator(t *testing.T) {
func TestJSEscapeSafe(t *testing.T) {
assert.EqualValues(t, `\u0026\u003C\u003E\'\"`, JSEscapeSafe(`&<>'"`))
}
+
+func TestHTMLFormat(t *testing.T) {
+ assert.Equal(t, template.HTML("< < 1 "), HTMLFormat("%s %s %d ", "<", template.HTML("<"), 1))
+}
diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl
index 04f76748d0..cf860dab2a 100644
--- a/templates/admin/packages/list.tmpl
+++ b/templates/admin/packages/list.tmpl
@@ -88,7 +88,7 @@
{{ctx.Locale.Tr "packages.settings.delete"}}
- {{ctx.Locale.Tr "packages.settings.delete.notice" (` `|Safe) (` `|Safe)}}
+ {{ctx.Locale.Tr "packages.settings.delete.notice" (` `|SafeHTML) (` `|SafeHTML)}}
{{template "base/modal_actions_confirm" .}}
diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl
index c7a6ec7e4e..e11247aed4 100644
--- a/templates/admin/repo/list.tmpl
+++ b/templates/admin/repo/list.tmpl
@@ -101,7 +101,7 @@
{{ctx.Locale.Tr "repo.settings.delete_desc"}}
- {{ctx.Locale.Tr "repo.settings.delete_notices_2" (`
`|Safe)}}
+ {{ctx.Locale.Tr "repo.settings.delete_notices_2" (`
`|SafeHTML)}}
{{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}}
{{template "base/modal_actions_confirm" .}}
diff --git a/templates/admin/stacktrace.tmpl b/templates/admin/stacktrace.tmpl
index aa5e810cd7..42944615c3 100644
--- a/templates/admin/stacktrace.tmpl
+++ b/templates/admin/stacktrace.tmpl
@@ -39,7 +39,7 @@
{{ctx.Locale.Tr "admin.monitor.process.cancel"}}
-
{{ctx.Locale.Tr "admin.monitor.process.cancel_notices" (` `|Safe)}}
+
{{ctx.Locale.Tr "admin.monitor.process.cancel_notices" (` `|SafeHTML)}}
{{ctx.Locale.Tr "admin.monitor.process.cancel_desc"}}
{{template "base/modal_actions_confirm" .}}
diff --git a/templates/mail/issue/assigned.tmpl b/templates/mail/issue/assigned.tmpl
index e80bd2fc31..5720319ee8 100644
--- a/templates/mail/issue/assigned.tmpl
+++ b/templates/mail/issue/assigned.tmpl
@@ -8,14 +8,14 @@
{{.Subject}}
-{{$repo_url := printf "%s " (Escape .Issue.Repo.HTMLURL) (Escape .Issue.Repo.FullName)}}
-{{$link := printf "#%d " (Escape .Link) .Issue.Index}}
+{{$repo_url := HTMLFormat "%s " .Issue.Repo.HTMLURL .Issue.Repo.FullName}}
+{{$link := HTMLFormat "#%d " .Link .Issue.Index}}
{{if .IsPull}}
- {{.locale.Tr "mail.issue_assigned.pull" .Doer.Name ($link|Safe) ($repo_url|Safe)}}
+ {{.locale.Tr "mail.issue_assigned.pull" .Doer.Name $link $repo_url}}
{{else}}
- {{.locale.Tr "mail.issue_assigned.issue" .Doer.Name ($link|Safe) ($repo_url|Safe)}}
+ {{.locale.Tr "mail.issue_assigned.issue" .Doer.Name $link $repo_url}}
{{end}}
-
{{ctx.Locale.Tr "org.members.leave.detail" (` `|Safe)}}
+
{{ctx.Locale.Tr "org.members.leave.detail" (` `|SafeHTML)}}
{{template "base/modal_actions_confirm" .}}
@@ -82,7 +82,7 @@
{{ctx.Locale.Tr "org.members.remove"}}
-
{{ctx.Locale.Tr "org.members.remove.detail" (` `|Safe) (` `|Safe)}}
+
{{ctx.Locale.Tr "org.members.remove.detail" (` `|SafeHTML) (` `|SafeHTML)}}
{{template "base/modal_actions_confirm" .}}
diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl
index dd4ece1433..adaf83ae15 100644
--- a/templates/org/team/members.tmpl
+++ b/templates/org/team/members.tmpl
@@ -81,7 +81,7 @@
{{ctx.Locale.Tr "org.members.remove"}}
-
{{ctx.Locale.Tr "org.members.remove.detail" (` `|Safe) (` `|Safe)}}
+
{{ctx.Locale.Tr "org.members.remove.detail" (` `|SafeHTML) (` `|SafeHTML)}}
{{template "base/modal_actions_confirm" .}}
diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl
index 37550ab71f..509c6382d8 100644
--- a/templates/org/team/sidebar.tmpl
+++ b/templates/org/team/sidebar.tmpl
@@ -88,7 +88,7 @@
{{ctx.Locale.Tr "org.teams.leave"}}
-
{{ctx.Locale.Tr "org.teams.leave.detail" (` `|Safe)}}
+
{{ctx.Locale.Tr "org.teams.leave.detail" (` `|SafeHTML)}}
{{template "base/modal_actions_confirm" .}}
diff --git a/templates/org/team/teams.tmpl b/templates/org/team/teams.tmpl
index b518d7d9d7..53c909ee9c 100644
--- a/templates/org/team/teams.tmpl
+++ b/templates/org/team/teams.tmpl
@@ -49,7 +49,7 @@
{{ctx.Locale.Tr "org.teams.leave"}}
-
{{ctx.Locale.Tr "org.teams.leave.detail" (` `|Safe)}}
+
{{ctx.Locale.Tr "org.teams.leave.detail" (` `|SafeHTML)}}
{{template "base/modal_actions_confirm" .}}
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index fbfaa19411..115ee92955 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -88,7 +88,7 @@
{{.CsrfTokenHtml}}
- {{ctx.Locale.Tr "repo.branch.new_branch_from" (` `|Safe)}}
+ {{ctx.Locale.Tr "repo.branch.new_branch_from" (` `|SafeHTML)}}
@@ -113,7 +113,7 @@
- {{ctx.Locale.Tr "repo.tag.create_tag_from" (` `|Safe)}}
+ {{ctx.Locale.Tr "repo.tag.create_tag_from" (` `|SafeHTML)}}
diff --git a/templates/repo/editor/cherry_pick.tmpl b/templates/repo/editor/cherry_pick.tmpl
index b65c3a3033..f9c9eef5aa 100644
--- a/templates/repo/editor/cherry_pick.tmpl
+++ b/templates/repo/editor/cherry_pick.tmpl
@@ -11,11 +11,11 @@
diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl
index 13d49b61b7..371c9db6f0 100644
--- a/templates/repo/issue/view_content/pull.tmpl
+++ b/templates/repo/issue/view_content/pull.tmpl
@@ -39,7 +39,7 @@
{{ctx.Locale.Tr "repo.pulls.merged_success"}}
- {{ctx.Locale.Tr "repo.pulls.merged_info_text" (printf "%s
" (.HeadTarget | Escape) | Safe)}}
+ {{ctx.Locale.Tr "repo.pulls.merged_info_text" (HTMLFormat "%s
" .HeadTarget)}}
diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl
index c6c86124a1..d6c8146451 100644
--- a/templates/repo/issue/view_title.tmpl
+++ b/templates/repo/issue/view_title.tmpl
@@ -43,33 +43,33 @@
{{end}}
{{if .Issue.IsPull}}
- {{$headHref := .HeadTarget|Escape}}
+ {{$headHref := .HeadTarget}}
{{if .HeadBranchLink}}
- {{$headHref = printf `
%s ` (.HeadBranchLink | Escape) $headHref}}
+ {{$headHref = HTMLFormat `
%s ` .HeadBranchLink $headHref}}
{{end}}
{{if not .MadeUsingAGit}}
- {{$headHref = printf `%s
%s ` $headHref (ctx.Locale.Tr "copy_branch") (.HeadTarget | Escape) (svg "octicon-copy" 14)}}
+ {{$headHref = HTMLFormat `%s
%s ` $headHref (ctx.Locale.Tr "copy_branch") .HeadTarget (svg "octicon-copy" 14)}}
{{end}}
{{$baseHref := .BaseTarget|Escape}}
{{if .BaseBranchLink}}
- {{$baseHref = printf `
%s ` (.BaseBranchLink | Escape) $baseHref}}
+ {{$baseHref = HTMLFormat `
%s ` .BaseBranchLink $baseHref}}
{{end}}
{{if .Issue.PullRequest.HasMerged}}
{{$mergedStr:= TimeSinceUnix .Issue.PullRequest.MergedUnix ctx.Locale}}
{{if .Issue.OriginalAuthor}}
{{.Issue.OriginalAuthor}}
-
{{ctx.Locale.Tr "repo.pulls.merged_title_desc" .NumCommits ($headHref|Safe) ($baseHref|Safe) $mergedStr}}
+
{{ctx.Locale.Tr "repo.pulls.merged_title_desc" .NumCommits $headHref $baseHref $mergedStr}}
{{else}}
{{.Issue.PullRequest.Merger.GetDisplayName}}
-
{{ctx.Locale.Tr "repo.pulls.merged_title_desc" .NumCommits ($headHref|Safe) ($baseHref|Safe) $mergedStr}}
+
{{ctx.Locale.Tr "repo.pulls.merged_title_desc" .NumCommits $headHref $baseHref $mergedStr}}
{{end}}
{{else}}
{{if .Issue.OriginalAuthor}}
-
{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits ($headHref|Safe) ($baseHref|Safe)}}
+
{{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}
{{else}}
{{.Issue.Poster.GetDisplayName}}
- {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits ($headHref|Safe) ($baseHref|Safe)}}
+ {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}}
{{end}}
{{if .MadeUsingAGit}}
diff --git a/templates/repo/migrate/migrate.tmpl b/templates/repo/migrate/migrate.tmpl
index c686f0b832..d1abb15374 100644
--- a/templates/repo/migrate/migrate.tmpl
+++ b/templates/repo/migrate/migrate.tmpl
@@ -20,7 +20,7 @@
{{.Title}}
- {{(printf "repo.migrate.%s.description" .Name) | ctx.Locale.Tr}}
+ {{ctx.Locale.Tr (printf "repo.migrate.%s.description" .Name)}}
diff --git a/templates/repo/settings/lfs_file.tmpl b/templates/repo/settings/lfs_file.tmpl
index 0aeb2af178..7f1d07e46f 100644
--- a/templates/repo/settings/lfs_file.tmpl
+++ b/templates/repo/settings/lfs_file.tmpl
@@ -15,9 +15,9 @@
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
{{if .IsMarkup}}
- {{if .FileContent}}{{.FileContent | Safe}}{{end}}
+ {{if .FileContent}}{{.FileContent | SafeHTML}}{{end}}
{{else if .IsPlainText}}
-
{{if .FileContent}}{{.FileContent | Safe}}{{end}}
+
{{if .FileContent}}{{.FileContent | SafeHTML}}{{end}}
{{else if not .IsTextFile}}
{{if .IsImageFile}}
diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl
index 8e2387067e..6836e695ed 100644
--- a/templates/repo/settings/webhook/settings.tmpl
+++ b/templates/repo/settings/webhook/settings.tmpl
@@ -263,7 +263,7 @@
{{ctx.Locale.Tr "repo.settings.authorization_header"}}
{{if ne .HookType "matrix"}}{{/* Matrix doesn't make the authorization optional but it is implied by the help string, should be changed.*/}}
- {{ctx.Locale.Tr "repo.settings.authorization_header_desc" ("Bearer token123456
, Basic YWxhZGRpbjpvcGVuc2VzYW1l
" | Safe)}}
+ {{ctx.Locale.Tr "repo.settings.authorization_header_desc" ("Bearer token123456
, Basic YWxhZGRpbjpvcGVuc2VzYW1l
" | SafeHTML)}}
{{end}}
diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl
index 5b296dc2af..f3b6be97cf 100644
--- a/templates/repo/wiki/view.tmpl
+++ b/templates/repo/wiki/view.tmpl
@@ -67,13 +67,13 @@
{{if .sidebarTocContent}}
{{end}}
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
- {{.content | Safe}}
+ {{.content | SafeHTML}}
{{if .sidebarPresent}}
@@ -82,7 +82,7 @@
{{svg "octicon-pencil"}}
{{end}}
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .sidebarEscapeStatus "root" $}}
- {{.sidebarContent | Safe}}
+ {{.sidebarContent | SafeHTML}}
{{end}}
@@ -94,7 +94,7 @@
{{svg "octicon-pencil"}}
{{end}}
{{template "repo/unicode_escape_prompt" dict "footerEscapeStatus" .sidebarEscapeStatus "root" $}}
- {{.footerContent | Safe}}
+ {{.footerContent | SafeHTML}}
{{end}}
diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl
index e7eb6c8180..7ce9a4b70f 100644
--- a/templates/user/settings/applications.tmpl
+++ b/templates/user/settings/applications.tmpl
@@ -75,7 +75,7 @@
{{ctx.Locale.Tr "settings.select_permissions"}}
- {{ctx.Locale.Tr "settings.access_token_desc" (printf `href="/api/swagger" target="_blank"`) (printf `href="https://docs.gitea.com/development/oauth2-provider#scopes" target="_blank"`) | Str2html}}
+ {{ctx.Locale.Tr "settings.access_token_desc" (`href="/api/swagger" target="_blank"`|SafeHTML) (`href="https://docs.gitea.com/development/oauth2-provider#scopes" target="_blank"`|SafeHTML)}}
-
{{ctx.Locale.Tr "org.members.leave.detail" (` `|Safe)}}
+
{{ctx.Locale.Tr "org.members.leave.detail" (` `|SafeHTML)}}
{{template "base/modal_actions_confirm" .}}