Skip to content

Commit 4435d8a

Browse files
6543Gustedjolheiser
authored
Fix XSS vulnerabilities (#29336)
- The Uncyclo page did not sanitize author name - the reviewer name on a "dismiss review" comment is also affected - the migration page has some spots --------- Signed-off-by: jolheiser <[email protected]> Co-authored-by: Gusted <[email protected]> Co-authored-by: jolheiser <[email protected]>
1 parent 6ca8cb5 commit 4435d8a

File tree

6 files changed

+93
-8
lines changed

6 files changed

+93
-8
lines changed

templates/repo/issue/view_content/comments.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@
615615
{{else}}
616616
{{$reviewerName = .Review.OriginalAuthor}}
617617
{{end}}
618-
{{ctx.Locale.Tr "repo.issues.review.dismissed" $reviewerName $createdStr | Safe}}
618+
<span class="dismissed-message">{{ctx.Locale.Tr "repo.issues.review.dismissed" ($reviewerName | Escape) $createdStr | Safe}}</span>
619619
</span>
620620
</div>
621621
{{if .Content}}

templates/repo/migrate/migrating.tmpl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
<div class="ui stackable middle very relaxed page grid">
2222
<div class="sixteen wide center aligned centered column">
2323
<div id="repo_migrating_progress">
24-
<p>{{ctx.Locale.Tr "repo.migrate.migrating" .CloneAddr | Safe}}</p>
24+
<p>{{ctx.Locale.Tr "repo.migrate.migrating" (.CloneAddr | Escape) | Safe}}</p>
2525
<p id="repo_migrating_progress_message"></p>
2626
</div>
2727
<div id="repo_migrating_failed" class="gt-hidden">
2828
{{if .CloneAddr}}
29-
<p>{{ctx.Locale.Tr "repo.migrate.migrating_failed" .CloneAddr | Safe}}</p>
29+
<p>{{ctx.Locale.Tr "repo.migrate.migrating_failed" (.CloneAddr | Escape) | Safe}}</p>
3030
{{else}}
3131
<p>{{ctx.Locale.Tr "repo.migrate.migrating_failed_no_addr" | Safe}}</p>
3232
{{end}}
@@ -58,7 +58,7 @@
5858
<div class="content">
5959
<div class="ui warning message">
6060
{{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}<br>
61-
{{ctx.Locale.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}}
61+
{{ctx.Locale.Tr "repo.settings.delete_notices_2" (.Repository.FullName | Escape) | Safe}}
6262
{{if .Repository.NumForks}}<br>
6363
{{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}}
6464
{{end}}

templates/repo/settings/options.tmpl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -906,7 +906,7 @@
906906
<div class="content">
907907
<div class="ui warning message">
908908
{{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}<br>
909-
{{ctx.Locale.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}}
909+
{{ctx.Locale.Tr "repo.settings.delete_notices_2" (.Repository.FullName | Escape) | Safe}}
910910
{{if .Repository.NumForks}}<br>
911911
{{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}}
912912
{{end}}
@@ -941,7 +941,7 @@
941941
<div class="content">
942942
<div class="ui warning message">
943943
{{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}<br>
944-
{{ctx.Locale.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name | Safe}}
944+
{{ctx.Locale.Tr "repo.settings.wiki_delete_notices_1" (.Repository.Name | Escape) | Safe}}
945945
</div>
946946
<form class="ui form" action="{{.Link}}" method="post">
947947
{{.CsrfTokenHtml}}

templates/repo/wiki/revision.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
{{$title}}
1111
<div class="ui sub header gt-word-break">
1212
{{$timeSince := TimeSince .Author.When ctx.Locale}}
13-
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}}
13+
{{ctx.Locale.Tr "repo.wiki.last_commit_info" (.Author.Name | Escape) $timeSince | Safe}}
1414
</div>
1515
</div>
1616
</div>

templates/repo/wiki/view.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
{{$title}}
4141
<div class="ui sub header">
4242
{{$timeSince := TimeSince .Author.When ctx.Locale}}
43-
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}}
43+
{{ctx.Locale.Tr "repo.wiki.last_commit_info" (.Author.Name | Escape) $timeSince | Safe}}
4444
</div>
4545
</div>
4646
<div class="eight wide right aligned column">

tests/integration/xss_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,23 @@
44
package integration
55

66
import (
7+
"context"
8+
"fmt"
79
"net/http"
10+
"net/url"
11+
"os"
12+
"path/filepath"
13+
"strings"
814
"testing"
15+
"time"
916

1017
"code.gitea.io/gitea/models/unittest"
1118
user_model "code.gitea.io/gitea/models/user"
19+
"code.gitea.io/gitea/modules/git"
1220
"code.gitea.io/gitea/tests"
1321

22+
gogit "github.com/go-git/go-git/v5"
23+
"github.com/go-git/go-git/v5/plumbing/object"
1424
"github.com/stretchr/testify/assert"
1525
)
1626

@@ -37,3 +47,78 @@ func TestXSSUserFullName(t *testing.T) {
3747
htmlDoc.doc.Find("div.content").Find(".header.text.center").Text(),
3848
)
3949
}
50+
51+
func TestXSSUncycloLastCommitInfo(t *testing.T) {
52+
onGiteaRun(t, func(t *testing.T, u *url.URL) {
53+
// Prepare the environment.
54+
dstPath := t.TempDir()
55+
r := fmt.Sprintf("%suser2/repo1.wiki.git", u.String())
56+
u, err := url.Parse(r)
57+
assert.NoError(t, err)
58+
u.User = url.UserPassword("user2", userPassword)
59+
assert.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstPath, git.CloneRepoOptions{}))
60+
61+
// Use go-git here, because using git wouldn't work, it has code to remove
62+
// `<`, `>` and `\n` in user names. Even though this is permitted and
63+
// wouldn't result in a error by a Git server.
64+
gitRepo, err := gogit.PlainOpen(dstPath)
65+
if err != nil {
66+
panic(err)
67+
}
68+
69+
w, err := gitRepo.Worktree()
70+
if err != nil {
71+
panic(err)
72+
}
73+
74+
filename := filepath.Join(dstPath, "Home.md")
75+
err = os.WriteFile(filename, []byte("Oh, a XSS attack?"), 0o644)
76+
if !assert.NoError(t, err) {
77+
t.FailNow()
78+
}
79+
80+
_, err = w.Add("Home.md")
81+
if !assert.NoError(t, err) {
82+
t.FailNow()
83+
}
84+
85+
_, err = w.Commit("Yay XSS", &gogit.CommitOptions{
86+
Author: &object.Signature{
87+
Name: `Gusted <script class="evil">alert('Oh no!');</script>`,
88+
89+
When: time.Date(2024, time.January, 31, 0, 0, 0, 0, time.UTC),
90+
},
91+
})
92+
if !assert.NoError(t, err) {
93+
t.FailNow()
94+
}
95+
96+
// Push.
97+
_, _, err = git.NewCommand(git.DefaultContext, "push").AddArguments(git.ToTrustedCmdArgs([]string{"origin", "master"})...).RunStdString(&git.RunOpts{Dir: dstPath})
98+
assert.NoError(t, err)
99+
100+
// Check on page view.
101+
t.Run("Page view", func(t *testing.T) {
102+
defer tests.PrintCurrentTest(t)()
103+
104+
req := NewRequest(t, http.MethodGet, "/user2/repo1/wiki/Home")
105+
resp := MakeRequest(t, req, http.StatusOK)
106+
htmlDoc := NewHTMLParser(t, resp.Body)
107+
108+
htmlDoc.AssertElement(t, "script.evil", false)
109+
assert.EqualValues(t, `Gusted edited this page 0001-01-01 00:00:00 +00:00`, strings.TrimSpace(htmlDoc.Find(".ui.sub.header").Text()))
110+
})
111+
112+
// Check on revisions page.
113+
t.Run("Revision page", func(t *testing.T) {
114+
defer tests.PrintCurrentTest(t)()
115+
116+
req := NewRequest(t, http.MethodGet, "/user2/repo1/wiki/Home?action=_revision")
117+
resp := MakeRequest(t, req, http.StatusOK)
118+
htmlDoc := NewHTMLParser(t, resp.Body)
119+
120+
htmlDoc.AssertElement(t, "script.evil", false)
121+
assert.EqualValues(t, `Gusted edited this page 0001-01-01 00:00:00 +00:00`, strings.TrimSpace(htmlDoc.Find(".ui.sub.header").Text()))
122+
})
123+
})
124+
}

0 commit comments

Comments
 (0)