- The Wiki 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 <john.olheiser@gmail.com> Co-authored-by: Gusted <postmaster@gusted.xyz> Co-authored-by: jolheiser <john.olheiser@gmail.com>tags/v1.21.6
{{else}} | {{else}} | ||||
{{$reviewerName = .Review.OriginalAuthor}} | {{$reviewerName = .Review.OriginalAuthor}} | ||||
{{end}} | {{end}} | ||||
{{ctx.Locale.Tr "repo.issues.review.dismissed" $reviewerName $createdStr | Safe}} | |||||
<span class="dismissed-message">{{ctx.Locale.Tr "repo.issues.review.dismissed" ($reviewerName | Escape) $createdStr | Safe}}</span> | |||||
</span> | </span> | ||||
</div> | </div> | ||||
{{if .Content}} | {{if .Content}} |
<div class="ui stackable middle very relaxed page grid"> | <div class="ui stackable middle very relaxed page grid"> | ||||
<div class="sixteen wide center aligned centered column"> | <div class="sixteen wide center aligned centered column"> | ||||
<div id="repo_migrating_progress"> | <div id="repo_migrating_progress"> | ||||
<p>{{ctx.Locale.Tr "repo.migrate.migrating" .CloneAddr | Safe}}</p> | |||||
<p>{{ctx.Locale.Tr "repo.migrate.migrating" (.CloneAddr | Escape) | Safe}}</p> | |||||
<p id="repo_migrating_progress_message"></p> | <p id="repo_migrating_progress_message"></p> | ||||
</div> | </div> | ||||
<div id="repo_migrating_failed" class="gt-hidden"> | <div id="repo_migrating_failed" class="gt-hidden"> | ||||
{{if .CloneAddr}} | {{if .CloneAddr}} | ||||
<p>{{ctx.Locale.Tr "repo.migrate.migrating_failed" .CloneAddr | Safe}}</p> | |||||
<p>{{ctx.Locale.Tr "repo.migrate.migrating_failed" (.CloneAddr | Escape) | Safe}}</p> | |||||
{{else}} | {{else}} | ||||
<p>{{ctx.Locale.Tr "repo.migrate.migrating_failed_no_addr" | Safe}}</p> | <p>{{ctx.Locale.Tr "repo.migrate.migrating_failed_no_addr" | Safe}}</p> | ||||
{{end}} | {{end}} | ||||
<div class="content"> | <div class="content"> | ||||
<div class="ui warning message"> | <div class="ui warning message"> | ||||
{{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}<br> | {{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}<br> | ||||
{{ctx.Locale.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}} | |||||
{{ctx.Locale.Tr "repo.settings.delete_notices_2" (.Repository.FullName | Escape) | Safe}} | |||||
{{if .Repository.NumForks}}<br> | {{if .Repository.NumForks}}<br> | ||||
{{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}} | {{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}} | ||||
{{end}} | {{end}} |
<div class="content"> | <div class="content"> | ||||
<div class="ui warning message"> | <div class="ui warning message"> | ||||
{{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}<br> | {{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}<br> | ||||
{{ctx.Locale.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}} | |||||
{{ctx.Locale.Tr "repo.settings.delete_notices_2" (.Repository.FullName | Escape) | Safe}} | |||||
{{if .Repository.NumForks}}<br> | {{if .Repository.NumForks}}<br> | ||||
{{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}} | {{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}} | ||||
{{end}} | {{end}} | ||||
<div class="content"> | <div class="content"> | ||||
<div class="ui warning message"> | <div class="ui warning message"> | ||||
{{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}<br> | {{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}<br> | ||||
{{ctx.Locale.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name | Safe}} | |||||
{{ctx.Locale.Tr "repo.settings.wiki_delete_notices_1" (.Repository.Name | Escape) | Safe}} | |||||
</div> | </div> | ||||
<form class="ui form" action="{{.Link}}" method="post"> | <form class="ui form" action="{{.Link}}" method="post"> | ||||
{{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} |
{{$title}} | {{$title}} | ||||
<div class="ui sub header gt-word-break"> | <div class="ui sub header gt-word-break"> | ||||
{{$timeSince := TimeSince .Author.When ctx.Locale}} | {{$timeSince := TimeSince .Author.When ctx.Locale}} | ||||
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}} | |||||
{{ctx.Locale.Tr "repo.wiki.last_commit_info" (.Author.Name | Escape) $timeSince | Safe}} | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> |
{{$title}} | {{$title}} | ||||
<div class="ui sub header"> | <div class="ui sub header"> | ||||
{{$timeSince := TimeSince .Author.When ctx.Locale}} | {{$timeSince := TimeSince .Author.When ctx.Locale}} | ||||
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}} | |||||
{{ctx.Locale.Tr "repo.wiki.last_commit_info" (.Author.Name | Escape) $timeSince | Safe}} | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="eight wide right aligned column"> | <div class="eight wide right aligned column"> |
package integration | package integration | ||||
import ( | import ( | ||||
"context" | |||||
"fmt" | |||||
"net/http" | "net/http" | ||||
"net/url" | |||||
"os" | |||||
"path/filepath" | |||||
"strings" | |||||
"testing" | "testing" | ||||
"time" | |||||
"code.gitea.io/gitea/models/unittest" | "code.gitea.io/gitea/models/unittest" | ||||
user_model "code.gitea.io/gitea/models/user" | user_model "code.gitea.io/gitea/models/user" | ||||
"code.gitea.io/gitea/modules/git" | |||||
"code.gitea.io/gitea/tests" | "code.gitea.io/gitea/tests" | ||||
gogit "github.com/go-git/go-git/v5" | |||||
"github.com/go-git/go-git/v5/plumbing/object" | |||||
"github.com/stretchr/testify/assert" | "github.com/stretchr/testify/assert" | ||||
) | ) | ||||
htmlDoc.doc.Find("div.content").Find(".header.text.center").Text(), | htmlDoc.doc.Find("div.content").Find(".header.text.center").Text(), | ||||
) | ) | ||||
} | } | ||||
func TestXSSWikiLastCommitInfo(t *testing.T) { | |||||
onGiteaRun(t, func(t *testing.T, u *url.URL) { | |||||
// Prepare the environment. | |||||
dstPath := t.TempDir() | |||||
r := fmt.Sprintf("%suser2/repo1.wiki.git", u.String()) | |||||
u, err := url.Parse(r) | |||||
assert.NoError(t, err) | |||||
u.User = url.UserPassword("user2", userPassword) | |||||
assert.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstPath, git.CloneRepoOptions{})) | |||||
// Use go-git here, because using git wouldn't work, it has code to remove | |||||
// `<`, `>` and `\n` in user names. Even though this is permitted and | |||||
// wouldn't result in a error by a Git server. | |||||
gitRepo, err := gogit.PlainOpen(dstPath) | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
w, err := gitRepo.Worktree() | |||||
if err != nil { | |||||
panic(err) | |||||
} | |||||
filename := filepath.Join(dstPath, "Home.md") | |||||
err = os.WriteFile(filename, []byte("Oh, a XSS attack?"), 0o644) | |||||
if !assert.NoError(t, err) { | |||||
t.FailNow() | |||||
} | |||||
_, err = w.Add("Home.md") | |||||
if !assert.NoError(t, err) { | |||||
t.FailNow() | |||||
} | |||||
_, err = w.Commit("Yay XSS", &gogit.CommitOptions{ | |||||
Author: &object.Signature{ | |||||
Name: `Gusted <script class="evil">alert('Oh no!');</script>`, | |||||
Email: "valid@example.org", | |||||
When: time.Date(2024, time.January, 31, 0, 0, 0, 0, time.UTC), | |||||
}, | |||||
}) | |||||
if !assert.NoError(t, err) { | |||||
t.FailNow() | |||||
} | |||||
// Push. | |||||
_, _, err = git.NewCommand(git.DefaultContext, "push").AddArguments(git.ToTrustedCmdArgs([]string{"origin", "master"})...).RunStdString(&git.RunOpts{Dir: dstPath}) | |||||
assert.NoError(t, err) | |||||
// Check on page view. | |||||
t.Run("Page view", func(t *testing.T) { | |||||
defer tests.PrintCurrentTest(t)() | |||||
req := NewRequest(t, http.MethodGet, "/user2/repo1/wiki/Home") | |||||
resp := MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
htmlDoc.AssertElement(t, "script.evil", false) | |||||
assert.EqualValues(t, `Gusted edited this page 0001-01-01 00:00:00 +00:00`, strings.TrimSpace(htmlDoc.Find(".ui.sub.header").Text())) | |||||
}) | |||||
// Check on revisions page. | |||||
t.Run("Revision page", func(t *testing.T) { | |||||
defer tests.PrintCurrentTest(t)() | |||||
req := NewRequest(t, http.MethodGet, "/user2/repo1/wiki/Home?action=_revision") | |||||
resp := MakeRequest(t, req, http.StatusOK) | |||||
htmlDoc := NewHTMLParser(t, resp.Body) | |||||
htmlDoc.AssertElement(t, "script.evil", false) | |||||
assert.EqualValues(t, `Gusted edited this page 0001-01-01 00:00:00 +00:00`, strings.TrimSpace(htmlDoc.Find(".ui.sub.header").Text())) | |||||
}) | |||||
}) | |||||
} |