Skip to content

Commit 386c1ed

Browse files
authored
Refactor HTMLFormat, update chroma render, fix js error (#33136)
A small refactor to improve HTMLFormat, to help to prevent low-level mistakes. And fix #33141, fix #33139
1 parent 67aeb1f commit 386c1ed

File tree

12 files changed

+34
-17
lines changed

12 files changed

+34
-17
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ require (
2727
github.com/ProtonMail/go-crypto v1.1.4
2828
github.com/PuerkitoBio/goquery v1.10.0
2929
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3
30-
github.com/alecthomas/chroma/v2 v2.14.0
30+
github.com/alecthomas/chroma/v2 v2.15.0
3131
github.com/aws/aws-sdk-go-v2/credentials v1.17.42
3232
github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3
3333
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb

go.sum

+4-4
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv
8181
github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
8282
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 h1:BP0HiyNT3AQEYi+if3wkRcIdQFHtsw6xX3Kx0glckgA=
8383
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3/go.mod h1:hMNtySovKkn2gdDuLqnqveP+mfhUSaBdoBcr2I7Zt0E=
84-
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
85-
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
84+
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
85+
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
8686
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
87-
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
88-
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
87+
github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
88+
github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
8989
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
9090
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
9191
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=

modules/htmlutil/html.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func ParseSizeAndClass(defaultSize int, defaultClass string, others ...any) (int
3030
return size, class
3131
}
3232

33-
func HTMLFormat(s string, rawArgs ...any) template.HTML {
33+
func HTMLFormat(s template.HTML, rawArgs ...any) template.HTML {
3434
args := slices.Clone(rawArgs)
3535
for i, v := range args {
3636
switch v := v.(type) {
@@ -44,5 +44,5 @@ func HTMLFormat(s string, rawArgs ...any) template.HTML {
4444
args[i] = template.HTMLEscapeString(fmt.Sprint(v))
4545
}
4646
}
47-
return template.HTML(fmt.Sprintf(s, args...))
47+
return template.HTML(fmt.Sprintf(string(s), args...))
4848
}

modules/markup/internal/renderinternal.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (r *RenderInternal) ProtectSafeAttrs(content template.HTML) template.HTML {
7676
return template.HTML(reAttrClass().ReplaceAllString(string(content), `$1 data-attr-class="`+r.secureIDPrefix+`$2"$3`))
7777
}
7878

79-
func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt string, a ...any) error {
79+
func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt template.HTML, a ...any) error {
8080
_, err := w.Write([]byte(r.ProtectSafeAttrs(htmlutil.HTMLFormat(fmt, a...))))
8181
return err
8282
}

modules/markup/markdown/math/block_renderer.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
package math
55

66
import (
7+
"html/template"
8+
79
"code.gitea.io/gitea/modules/markup/internal"
810
giteaUtil "code.gitea.io/gitea/modules/util"
911

@@ -50,7 +52,7 @@ func (r *BlockRenderer) renderBlock(w util.BufWriter, source []byte, node gast.N
5052
n := node.(*Block)
5153
if entering {
5254
code := giteaUtil.Iif(n.Inline, "", `<pre class="code-block is-loading">`) + `<code class="language-math display">`
53-
_ = r.renderInternal.FormatWithSafeAttrs(w, code)
55+
_ = r.renderInternal.FormatWithSafeAttrs(w, template.HTML(code))
5456
r.writeLines(w, source, n)
5557
} else {
5658
_, _ = w.WriteString(`</code>` + giteaUtil.Iif(n.Inline, "", `</pre>`) + "\n")

modules/markup/orgmode/orgmode.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ func (r *orgWriter) resolveLink(kind, link string) string {
147147
func (r *orgWriter) WriteRegularLink(l org.RegularLink) {
148148
link := r.resolveLink(l.Kind(), l.URL)
149149

150-
printHTML := func(html string, a ...any) {
150+
printHTML := func(html template.HTML, a ...any) {
151151
_, _ = fmt.Fprint(r, htmlutil.HTMLFormat(html, a...))
152152
}
153153
// Inspired by https://github.com/niklasfasching/go-org/blob/6eb20dbda93cb88c3503f7508dc78cbbc639378f/org/html_writer.go#L406-L427

modules/markup/orgmode/orgmode_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ func HelloWorld() {
103103
}
104104
#+end_src
105105
`, `<div class="src src-go">
106-
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints &#34;Hello World&#34;
107-
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
106+
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints &#34;Hello World&#34;</span>
107+
<span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
108108
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Hello World&#34;</span><span class="p">)</span>
109109
<span class="p">}</span></code></pre>
110110
</div>`)

modules/templates/helper.go

+15-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func NewFuncMap() template.FuncMap {
3838
"Iif": iif,
3939
"Eval": evalTokens,
4040
"SafeHTML": safeHTML,
41-
"HTMLFormat": htmlutil.HTMLFormat,
41+
"HTMLFormat": htmlFormat,
4242
"HTMLEscape": htmlEscape,
4343
"QueryEscape": queryEscape,
4444
"QueryBuild": QueryBuild,
@@ -207,6 +207,20 @@ func htmlEscape(s any) template.HTML {
207207
panic(fmt.Sprintf("unexpected type %T", s))
208208
}
209209

210+
func htmlFormat(s any, args ...any) template.HTML {
211+
if len(args) == 0 {
212+
// to prevent developers from calling "HTMLFormat $userInput" by mistake which will lead to XSS
213+
panic("missing arguments for HTMLFormat")
214+
}
215+
switch v := s.(type) {
216+
case string:
217+
return htmlutil.HTMLFormat(template.HTML(v), args...)
218+
case template.HTML:
219+
return htmlutil.HTMLFormat(v, args...)
220+
}
221+
panic(fmt.Sprintf("unexpected type %T", s))
222+
}
223+
210224
func jsEscapeSafe(s string) template.HTML {
211225
return template.HTML(template.JSEscapeString(s))
212226
}

modules/templates/helper_test.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"strings"
99
"testing"
1010

11-
"code.gitea.io/gitea/modules/htmlutil"
1211
"code.gitea.io/gitea/modules/util"
1312

1413
"github.com/stretchr/testify/assert"
@@ -88,7 +87,7 @@ func TestTemplateIif(t *testing.T) {
8887
func TestTemplateEscape(t *testing.T) {
8988
execTmpl := func(code string) string {
9089
tmpl := template.New("test")
91-
tmpl.Funcs(template.FuncMap{"QueryBuild": QueryBuild, "HTMLFormat": htmlutil.HTMLFormat})
90+
tmpl.Funcs(template.FuncMap{"QueryBuild": QueryBuild, "HTMLFormat": htmlFormat})
9291
template.Must(tmpl.Parse(code))
9392
w := &strings.Builder{}
9493
assert.NoError(t, tmpl.Execute(w, nil))

web_src/js/features/common-button.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ export function initGlobalDeleteButton(): void {
1717
// Some model/form elements will be filled by `data-id` / `data-name` / `data-data-xxx` attributes.
1818
// If there is a form defined by `data-form`, then the form will be submitted as-is (without any modification).
1919
// If there is no form, then the data will be posted to `data-url`.
20-
// TODO: it's not encouraged to use this method. `show-modal` does far better than this.
20+
// TODO: do not use this method in new code. `show-modal` / `link-action(data-modal-confirm)` does far better than this.
21+
// FIXME: all legacy `delete-button` should be refactored to use `show-modal` or `link-action`
2122
for (const btn of document.querySelectorAll<HTMLElement>('.delete-button')) {
2223
btn.addEventListener('click', (e) => {
2324
e.preventDefault();

web_src/js/utils.test.ts

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ test('parseIssueNewHref', () => {
5050
expect(parseIssueNewHref('/owner/repo/issues/new?query')).toEqual({ownerName: 'owner', repoName: 'repo', pathType: 'issues'});
5151
expect(parseIssueNewHref('/sub/owner/repo/issues/new#hash')).toEqual({ownerName: 'owner', repoName: 'repo', pathType: 'issues'});
5252
expect(parseIssueNewHref('/sub/owner/repo/compare/feature/branch-1...fix/branch-2')).toEqual({ownerName: 'owner', repoName: 'repo', pathType: 'pulls'});
53+
expect(parseIssueNewHref('/other')).toEqual({});
5354
});
5455

5556
test('parseUrl', () => {

web_src/js/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function parseIssueHref(href: string): IssuePathInfo {
4040
export function parseIssueNewHref(href: string): IssuePathInfo {
4141
const path = (href || '').replace(/[#?].*$/, '');
4242
const [_, ownerName, repoName, pathTypeField] = /([^/]+)\/([^/]+)\/(issues\/new|compare\/.+\.\.\.)/.exec(path) || [];
43-
const pathType = pathTypeField.startsWith('issues/new') ? 'issues' : 'pulls';
43+
const pathType = pathTypeField ? (pathTypeField.startsWith('issues/new') ? 'issues' : 'pulls') : undefined;
4444
return {ownerName, repoName, pathType};
4545
}
4646

0 commit comments

Comments
 (0)