Rename Str2html to SanitizeHTML and clarify its behavior (#29516)

Str2html was abused a lot. So use a proper name for it: SanitizeHTML

And add some tests to show its behavior.

(cherry picked from commit fb42972c057364a1dc99dfb528554e7a94415be7)

Conflicts:
	docs/content/administration/mail-templates.en-us.md
	docs/content/administration/mail-templates.zh-cn.md
	prefer their version always
This commit is contained in:
wxiaoguang 2024-03-01 18:16:19 +08:00 committed by Earl Warren
parent 58bc3af08d
commit 2892aaab02
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
14 changed files with 52 additions and 47 deletions

View file

@ -222,9 +222,9 @@ Please check [Gitea's logs](administration/logging-config.md) for error messages
<a href="{{.Link}}">{{.Repo}}#{{.Issue.Index}}</a>. <a href="{{.Link}}">{{.Repo}}#{{.Issue.Index}}</a>.
</p> </p>
{{if not (eq .Body "")}} {{if not (eq .Body "")}}
<h3>Message content:</h3> <h3>Message content</h3>
<hr> <hr>
{{.Body | Str2html}} {{.Body | SanitizeHTML}}
{{end}} {{end}}
</p> </p>
<hr> <hr>
@ -245,7 +245,7 @@ This template produces something along these lines:
> [@rhonda](#) (Rhonda Myers) updated [mike/stuff#38](#). > [@rhonda](#) (Rhonda Myers) updated [mike/stuff#38](#).
> >
> #### Message content: > #### Message content
> >
> \_********************************\_******************************** > \_********************************\_********************************
> >
@ -260,19 +260,19 @@ The template system contains several functions that can be used to further proce
the messages. Here's a list of some of them: the messages. Here's a list of some of them:
| Name | Parameters | Available | Usage | | Name | Parameters | Available | Usage |
| ---------------- | ----------- | --------- | --------------------------------------------------------------------------- | | ---------------- | ----------- | --------- |-----------------------------------------------------------------------------|
| `AppUrl` | - | Any | Gitea's URL | | `AppUrl` | - | Any | Gitea's URL |
| `AppName` | - | Any | Set from `app.ini`, usually "Gitea" | | `AppName` | - | Any | Set from `app.ini`, usually "Gitea" |
| `AppDomain` | - | Any | Gitea's host name | | `AppDomain` | - | Any | Gitea's host name |
| `EllipsisString` | string, int | Any | Truncates a string to the specified length; adds ellipsis as needed | | `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. | | `SanitizeHTML` | string | Body only | Sanitizes text by removing any dangerous 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: These are _functions_, not metadata, so they have to be used:
```html ```html
Like this: {{Str2html "Escape<my>text"}} Like this: {{SanitizeHTML "Escape<my>text"}}
Or this: {{"Escape<my>text" | Str2html}} Or this: {{"Escape<my>text" | SanitizeHTML}}
Or this: {{AppUrl}} Or this: {{AppUrl}}
But not like this: {{.AppUrl}} But not like this: {{.AppUrl}}
``` ```

View file

@ -207,7 +207,7 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/
{{if not (eq .Body "")}} {{if not (eq .Body "")}}
<h3>消息内容:</h3> <h3>消息内容:</h3>
<hr> <hr>
{{.Body | Str2html}} {{.Body | SanitizeHTML}}
{{end}} {{end}}
</p> </p>
<hr> <hr>
@ -228,7 +228,7 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/
> [@rhonda](#)Rhonda Myers更新了 [mike/stuff#38](#)。 > [@rhonda](#)Rhonda Myers更新了 [mike/stuff#38](#)。
> >
> #### 消息内容 > #### 消息内容
> >
> \_********************************\_******************************** > \_********************************\_********************************
> >
@ -242,20 +242,20 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://go.dev/pkg/text/
模板系统包含一些函数,可用于进一步处理和格式化消息。以下是其中一些函数的列表: 模板系统包含一些函数,可用于进一步处理和格式化消息。以下是其中一些函数的列表:
| 函数名 | 参数 | 可用于 | 用法 | | 函数名 | 参数 | 可用于 | 用法 |
| ----------------- | ----------- | ------------ | --------------------------------------------------------------------------------- | |------------------| ----------- | ------------ |---------------------------------------------------------|
| `AppUrl` | - | 任何地方 | Gitea 的 URL | | `AppUrl` | - | 任何地方 | Gitea 的 URL |
| `AppName` | - | 任何地方 | 从 `app.ini` 中设置,通常为 "Gitea" | | `AppName` | - | 任何地方 | 从 `app.ini` 中设置,通常为 "Gitea" |
| `AppDomain` | - | 任何地方 | Gitea 的主机名 | | `AppDomain` | - | 任何地方 | Gitea 的主机名 |
| `EllipsisString` | string, int | 任何地方 | 将字符串截断为指定长度;根据需要添加省略号 | | `EllipsisString` | string, int | 任何地方 | 将字符串截断为指定长度;根据需要添加省略号 |
| `Str2html` | string | 仅正文部分 | 通过删除其中的 HTML 标签对文本进行清理 | | `SanitizeHTML` | string | 仅正文部分 | 通过删除其中的危险 HTML 标签对文本进行清理 |
| `Safe` | string | 仅正文部分 | 将输入作为 HTML 处理;可用于 `.ReviewComments.RenderedContent` 等字段 | | `SafeHTML` | string | 仅正文部分 | 将输入作为 HTML 处理;可用于 `.ReviewComments.RenderedContent` 等字段 |
这些都是 _函数_,而不是元数据,因此必须按以下方式使用: 这些都是 _函数_,而不是元数据,因此必须按以下方式使用:
```html ```html
像这样使用: {{Str2html "Escape<my>text"}} 像这样使用: {{SanitizeHTML "Escape<my>text"}}
或者这样使用: {{"Escape<my>text" | Str2html}} 或者这样使用: {{"Escape<my>text" | SanitizeHTML}}
或者这样使用: {{AppUrl}} 或者这样使用: {{AppUrl}}
但不要像这样使用: {{.AppUrl}} 但不要像这样使用: {{.AppUrl}}
``` ```

View file

@ -33,16 +33,16 @@ func NewFuncMap() template.FuncMap {
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// html/template related functions // html/template related functions
"dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names. "dict": dict, // it's lowercase because this name has been widely used. Our other functions should have uppercase names.
"Eval": Eval, "Eval": Eval,
"SafeHTML": SafeHTML, "SafeHTML": SafeHTML,
"HTMLFormat": HTMLFormat, "HTMLFormat": HTMLFormat,
"HTMLEscape": HTMLEscape, "HTMLEscape": HTMLEscape,
"QueryEscape": url.QueryEscape, "QueryEscape": url.QueryEscape,
"JSEscape": JSEscapeSafe, "JSEscape": JSEscapeSafe,
"Str2html": Str2html, // TODO: rename it to SanitizeHTML "SanitizeHTML": SanitizeHTML,
"URLJoin": util.URLJoin, "URLJoin": util.URLJoin,
"DotEscape": DotEscape, "DotEscape": DotEscape,
"PathEscape": url.PathEscape, "PathEscape": url.PathEscape,
"PathEscapeSegments": util.PathEscapeSegments, "PathEscapeSegments": util.PathEscapeSegments,
@ -210,8 +210,8 @@ func SafeHTML(s any) template.HTML {
panic(fmt.Sprintf("unexpected type %T", s)) panic(fmt.Sprintf("unexpected type %T", s))
} }
// Str2html sanitizes the input by pre-defined markdown rules // SanitizeHTML sanitizes the input by pre-defined markdown rules
func Str2html(s any) template.HTML { func SanitizeHTML(s any) template.HTML {
switch v := s.(type) { switch v := s.(type) {
case string: case string:
return template.HTML(markup.Sanitize(v)) return template.HTML(markup.Sanitize(v))

View file

@ -61,3 +61,8 @@ func TestJSEscapeSafe(t *testing.T) {
func TestHTMLFormat(t *testing.T) { func TestHTMLFormat(t *testing.T) {
assert.Equal(t, template.HTML("<a>&lt; < 1</a>"), HTMLFormat("<a>%s %s %d</a>", "<", template.HTML("<"), 1)) assert.Equal(t, template.HTML("<a>&lt; < 1</a>"), HTMLFormat("<a>%s %s %d</a>", "<", template.HTML("<"), 1))
} }
func TestSanitizeHTML(t *testing.T) {
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>`))
assert.Equal(t, template.HTML(`<a href="/" rel="nofollow">link</a> xss <div>inline</div>`), SanitizeHTML(template.HTML(`<a href="/">link</a> <a href="javascript:">xss</a> <div style="dangerous">inline</div>`)))
}

View file

@ -65,7 +65,7 @@ func renderMarkdown(ctx *context.Context, act *activities_model.Action, content
} }
markdown, err := markdown.RenderString(markdownCtx, content) markdown, err := markdown.RenderString(markdownCtx, content)
if err != nil { if err != nil {
return templates.Str2html(content) // old code did so: use Str2html to render in tmpl return templates.SanitizeHTML(content) // old code did so: use SanitizeHTML to render in tmpl
} }
return markdown return markdown
} }
@ -244,7 +244,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
} }
} }
if len(content) == 0 { if len(content) == 0 {
content = templates.Str2html(desc) content = templates.SanitizeHTML(desc)
} }
// It's a common practice for feed generators to use plain text titles. // It's a common practice for feed generators to use plain text titles.

View file

@ -105,7 +105,7 @@ func Projects(ctx *context.Context) {
} }
for _, project := range projects { for _, project := range projects {
project.RenderedContent = templates.Str2html(project.Description) // FIXME: is it right? why not render? project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render?
} }
err = shared_user.LoadHeaderCount(ctx) err = shared_user.LoadHeaderCount(ctx)
@ -396,7 +396,7 @@ func ViewProject(ctx *context.Context) {
} }
} }
project.RenderedContent = templates.Str2html(project.Description) // FIXME: is it right? why not render? project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render?
ctx.Data["LinkedPRs"] = linkedPrsMap ctx.Data["LinkedPRs"] = linkedPrsMap
ctx.Data["PageIsViewProjects"] = true ctx.Data["PageIsViewProjects"] = true
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)

View file

@ -1770,7 +1770,7 @@ func ViewIssue(ctx *context.Context) {
// so "|" is used as delimeter to mark the new format // so "|" is used as delimeter to mark the new format
if comment.Content[0] != '|' { if comment.Content[0] != '|' {
// handle old time comments that have formatted text stored // handle old time comments that have formatted text stored
comment.RenderedContent = templates.Str2html(comment.Content) comment.RenderedContent = templates.SanitizeHTML(comment.Content)
comment.Content = "" comment.Content = ""
} else { } else {
// else it's just a duration in seconds to pass on to the frontend // else it's just a duration in seconds to pass on to the frontend

View file

@ -1,20 +1,20 @@
{{if .Flash.ErrorMsg}} {{if .Flash.ErrorMsg}}
<div class="ui negative message flash-message flash-error"> <div class="ui negative message flash-message flash-error">
<p>{{.Flash.ErrorMsg | Str2html}}</p> <p>{{.Flash.ErrorMsg | SanitizeHTML}}</p>
</div> </div>
{{end}} {{end}}
{{if .Flash.SuccessMsg}} {{if .Flash.SuccessMsg}}
<div class="ui positive message flash-message flash-success"> <div class="ui positive message flash-message flash-success">
<p>{{.Flash.SuccessMsg | Str2html}}</p> <p>{{.Flash.SuccessMsg | SanitizeHTML}}</p>
</div> </div>
{{end}} {{end}}
{{if .Flash.InfoMsg}} {{if .Flash.InfoMsg}}
<div class="ui info message flash-message flash-info"> <div class="ui info message flash-message flash-info">
<p>{{.Flash.InfoMsg | Str2html}}</p> <p>{{.Flash.InfoMsg | SanitizeHTML}}</p>
</div> </div>
{{end}} {{end}}
{{if .Flash.WarningMsg}} {{if .Flash.WarningMsg}}
<div class="ui warning message flash-message flash-warning"> <div class="ui warning message flash-message flash-warning">
<p>{{.Flash.WarningMsg | Str2html}}</p> <p>{{.Flash.WarningMsg | SanitizeHTML}}</p>
</div> </div>
{{end}} {{end}}

View file

@ -2,6 +2,6 @@
<details> <details>
<summary>{{.Summary}}</summary> <summary>{{.Summary}}</summary>
<code> <code>
{{.Details | Str2html}} {{.Details | SanitizeHTML}}
</code> </code>
</details> </details>

View file

@ -58,7 +58,7 @@
{{.locale.Tr "mail.issue.action.new" .Doer.Name .Issue.Index}} {{.locale.Tr "mail.issue.action.new" .Doer.Name .Issue.Index}}
{{end}} {{end}}
{{else}} {{else}}
{{.Body | Str2html}} {{.Body | SanitizeHTML}}
{{end -}} {{end -}}
{{- range .ReviewComments}} {{- range .ReviewComments}}
<hr> <hr>

View file

@ -276,7 +276,7 @@
<span class="text grey" id="note-authored-time">{{TimeSince .NoteCommit.Author.When ctx.Locale}}</span> <span class="text grey" id="note-authored-time">{{TimeSince .NoteCommit.Author.When ctx.Locale}}</span>
</div> </div>
<div class="ui bottom attached info segment git-notes"> <div class="ui bottom attached info segment git-notes">
<pre class="commit-body">{{.NoteRendered | Str2html}}</pre> <pre class="commit-body">{{.NoteRendered | SanitizeHTML}}</pre>
</div> </div>
{{end}} {{end}}
{{template "repo/diff/box" .}} {{template "repo/diff/box" .}}

View file

@ -166,7 +166,7 @@
</span> </span>
<div class="detail"> <div class="detail">
{{svg "octicon-git-commit"}} {{svg "octicon-git-commit"}}
<span class="text grey muted-links">{{.Content | Str2html}}</span> <span class="text grey muted-links">{{.Content | SanitizeHTML}}</span>
</div> </div>
</div> </div>
{{else if eq .Type 7}} {{else if eq .Type 7}}

View file

@ -10,7 +10,7 @@
<div class="ui attached segment"> <div class="ui attached segment">
<div class="ui list"> <div class="ui list">
<div class="item"> <div class="item">
{{.Description | Str2html}} {{.Description | SanitizeHTML}}
</div> </div>
{{range .Webhooks}} {{range .Webhooks}}
<div class="item truncated-item-container"> <div class="item truncated-item-container">

View file

@ -1,5 +1,5 @@
{{/* This page should only depend the minimal template functions/variables, to avoid triggering new panics. {{/* This page should only depend the minimal template functions/variables, to avoid triggering new panics.
* base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, ThemeName, Str2html * base template functions: AppName, AssetUrlPrefix, AssetVersion, AppSubUrl, ThemeName, SanitizeHTML
* ctx.Locale * ctx.Locale
* .Flash * .Flash
* .ErrorMsg * .ErrorMsg