diff --git a/docs/content/administration/mail-templates.en-us.md b/docs/content/administration/mail-templates.en-us.md index 05c41a6a02f0..b642ff4aa7f6 100644 --- a/docs/content/administration/mail-templates.en-us.md +++ b/docs/content/administration/mail-templates.en-us.md @@ -266,7 +266,7 @@ the messages. Here's a list of some of them: | `AppDomain` | - | Any | Gitea's host name | | `EllipsisString` | string, int | Any | Truncates a string to the specified length; adds ellipsis as needed | | `Str2html` | string | Body only | Sanitizes text by removing any HTML tags from it. | -| `Safe` | string | Body only | Takes the input as HTML; can be used for `.ReviewComments.RenderedContent`. | +| `SafeHTML` | string | Body only | Takes the input as HTML; can be used for `.ReviewComments.RenderedContent`. | These are _functions_, not metadata, so they have to be used: diff --git a/docs/content/administration/mail-templates.zh-cn.md b/docs/content/administration/mail-templates.zh-cn.md index 4846f6f398b2..fd455ef3a860 100644 --- a/docs/content/administration/mail-templates.zh-cn.md +++ b/docs/content/administration/mail-templates.zh-cn.md @@ -242,14 +242,14 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/ 模板系统包含一些函数,可用于进一步处理和格式化消息。以下是其中一些函数的列表: -| 函数名 | 参数 | 可用于 | 用法 | -| ----------------- | ----------- | ------------ | --------------------------------------------------------------------------------- | -| `AppUrl` | - | 任何地方 | Gitea 的 URL | -| `AppName` | - | 任何地方 | 从 `app.ini` 中设置,通常为 "Gitea" | -| `AppDomain` | - | 任何地方 | Gitea 的主机名 | -| `EllipsisString` | string, int | 任何地方 | 将字符串截断为指定长度;根据需要添加省略号 | -| `Str2html` | string | 仅正文部分 | 通过删除其中的 HTML 标签对文本进行清理 | -| `Safe` | string | 仅正文部分 | 将输入作为 HTML 处理;可用于 `.ReviewComments.RenderedContent` 等字段 | +| 函数名 | 参数 | 可用于 | 用法 | +|------------------| ----------- | ------------ | --------------------------------------------------------------------------------- | +| `AppUrl` | - | 任何地方 | Gitea 的 URL | +| `AppName` | - | 任何地方 | 从 `app.ini` 中设置,通常为 "Gitea" | +| `AppDomain` | - | 任何地方 | Gitea 的主机名 | +| `EllipsisString` | string, int | 任何地方 | 将字符串截断为指定长度;根据需要添加省略号 | +| `Str2html` | string | 仅正文部分 | 通过删除其中的 HTML 标签对文本进行清理 | +| `SafeHTML` | string | 仅正文部分 | 将输入作为 HTML 处理;可用于 `.ReviewComments.RenderedContent` 等字段 | 这些都是 _函数_,而不是元数据,因此必须按以下方式使用: diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 691f7547488d..567948749811 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, @@ -177,8 +179,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 739a92f34f93..8f5d633d4f80 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 04f76748d081..cf860dab2ab1 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 c7a6ec7e4e9f..e11247aed499 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 aa5e810cd780..42944615c38b 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 e80bd2fc31c1..5720319ee845 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 dd4ece14335a..adaf83ae155e 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 440fa11dc950..9311a46e38f7 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 b518d7d9d738..53c909ee9c81 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 fbfaa19411bf..115ee9295510 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -88,7 +88,7 @@ {{.CsrfTokenHtml}}
@@ -113,7 +113,7 @@
diff --git a/templates/repo/editor/cherry_pick.tmpl b/templates/repo/editor/cherry_pick.tmpl index b65c3a30337f..f9c9eef5aab6 100644 --- a/templates/repo/editor/cherry_pick.tmpl +++ b/templates/repo/editor/cherry_pick.tmpl @@ -11,11 +11,11 @@
@@ -595,19 +595,19 @@ {{$oldProjectDisplayHtml := "Unknown Project"}} {{if .OldProject}} {{$trKey := printf "projects.type-%d.display_name" .OldProject.Type}} - {{$oldProjectDisplayHtml = printf `%s` (ctx.Locale.Tr $trKey | Escape) (.OldProject.Title | Escape)}} + {{$oldProjectDisplayHtml = HTMLFormat `%s` (ctx.Locale.Tr $trKey) .OldProject.Title}} {{end}} {{$newProjectDisplayHtml := "Unknown Project"}} {{if .Project}} {{$trKey := printf "projects.type-%d.display_name" .Project.Type}} - {{$newProjectDisplayHtml = printf `%s` (ctx.Locale.Tr $trKey | Escape) (.Project.Title | Escape)}} + {{$newProjectDisplayHtml = HTMLFormat `%s` (ctx.Locale.Tr $trKey) .Project.Title}} {{end}} {{if and (gt .OldProjectID 0) (gt .ProjectID 0)}} - {{ctx.Locale.Tr "repo.issues.change_project_at" ($oldProjectDisplayHtml|Safe) ($newProjectDisplayHtml|Safe) $createdStr}} + {{ctx.Locale.Tr "repo.issues.change_project_at" $oldProjectDisplayHtml $newProjectDisplayHtml $createdStr}} {{else if gt .OldProjectID 0}} - {{ctx.Locale.Tr "repo.issues.remove_project_at" ($oldProjectDisplayHtml|Safe) $createdStr}} + {{ctx.Locale.Tr "repo.issues.remove_project_at" $oldProjectDisplayHtml $createdStr}} {{else if gt .ProjectID 0}} - {{ctx.Locale.Tr "repo.issues.add_project_at" ($newProjectDisplayHtml|Safe) $createdStr}} + {{ctx.Locale.Tr "repo.issues.add_project_at" $newProjectDisplayHtml $createdStr}} {{end}}
diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index 13d49b61b76c..371c9db6f052 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"}}
diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index 9b4657b63475..37cad26c9b39 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -43,31 +43,31 @@ {{end}}
{{if .Issue.IsPull}} - {{$headHref := .HeadTarget|Escape}} + {{$headHref := .HeadTarget}} {{if .HeadBranchLink}} - {{$headHref = printf `%s` (.HeadBranchLink | Escape) $headHref}} + {{$headHref = HTMLFormat `%s` .HeadBranchLink $headHref}} {{end}} - {{$headHref = printf `%s ` $headHref (ctx.Locale.Tr "copy_branch") (.HeadTarget | Escape) (svg "octicon-copy" 14)}} - {{$baseHref := .BaseTarget|Escape}} + {{$headHref = HTMLFormat `%s ` $headHref (ctx.Locale.Tr "copy_branch") .HeadTarget (svg "octicon-copy" 14)}} + {{$baseHref := .BaseTarget}} {{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}} diff --git a/templates/repo/migrate/migrate.tmpl b/templates/repo/migrate/migrate.tmpl index c686f0b832b1..d1abb153748a 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 0aeb2af178b2..7f1d07e46fc3 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 f636108b379c..3ef8894444a4 100644 --- a/templates/repo/settings/webhook/settings.tmpl +++ b/templates/repo/settings/webhook/settings.tmpl @@ -263,7 +263,7 @@ {{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 5b296dc2af4e..f3b6be97cf2f 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 8cf76d80a577..7ce9a4b70fb2 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"`)}} + {{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" .}}