summaryrefslogtreecommitdiffstats
path: root/routers/web
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2021-11-16 18:18:25 +0000
committerGitHub <noreply@github.com>2021-11-16 18:18:25 +0000
commitbbffcc3aecda040a40d49078e7141213bc8d78af (patch)
tree1b96c621edadabc85dec2c15c30b1b870af09467 /routers/web
parent7e1ae380975df0afab3fdc04c7a926181e5daba9 (diff)
downloadgitea-bbffcc3aecda040a40d49078e7141213bc8d78af.tar.gz
gitea-bbffcc3aecda040a40d49078e7141213bc8d78af.zip
Multiple Escaping Improvements (#17551)
There are multiple places where Gitea does not properly escape URLs that it is building and there are multiple places where it builds urls when there is already a simpler function available to use this. This is an extensive PR attempting to fix these issues. 1. The first commit in this PR looks through all href, src and links in the Gitea codebase and has attempted to catch all the places where there is potentially incomplete escaping. 2. Whilst doing this we will prefer to use functions that create URLs over recreating them by hand. 3. All uses of strings should be directly escaped - even if they are not currently expected to contain escaping characters. The main benefit to doing this will be that we can consider relaxing the constraints on user names and reponames in future. 4. The next commit looks at escaping in the wiki and re-considers the urls that are used there. Using the improved escaping here wiki files containing '/'. (This implementation will currently still place all of the wiki files the root directory of the repo but this would not be difficult to change.) 5. The title generation in feeds is now properly escaped. 6. EscapePound is no longer needed - urls should be PathEscaped / QueryEscaped as necessary but then re-escaped with Escape when creating html with locales Signed-off-by: Andrew Thornton <art27@cantab.net> Signed-off-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'routers/web')
-rw-r--r--routers/web/admin/auths.go6
-rw-r--r--routers/web/admin/repos.go4
-rw-r--r--routers/web/admin/users.go10
-rw-r--r--routers/web/feed/convert.go134
-rw-r--r--routers/web/org/setting.go3
-rw-r--r--routers/web/org/teams.go19
-rw-r--r--routers/web/repo/blame.go10
-rw-r--r--routers/web/repo/commit.go4
-rw-r--r--routers/web/repo/compare.go33
-rw-r--r--routers/web/repo/editor.go12
-rw-r--r--routers/web/repo/issue.go25
-rw-r--r--routers/web/repo/issue_stopwatch.go2
-rw-r--r--routers/web/repo/lfs.go3
-rw-r--r--routers/web/repo/migrate.go3
-rw-r--r--routers/web/repo/milestone.go3
-rw-r--r--routers/web/repo/projects.go3
-rw-r--r--routers/web/repo/pull.go72
-rw-r--r--routers/web/repo/release.go3
-rw-r--r--routers/web/repo/repo.go4
-rw-r--r--routers/web/repo/setting.go16
-rw-r--r--routers/web/repo/setting_protected_branch.go7
-rw-r--r--routers/web/repo/tag.go2
-rw-r--r--routers/web/repo/view.go16
-rw-r--r--routers/web/repo/webhook.go11
-rw-r--r--routers/web/repo/wiki.go65
-rw-r--r--routers/web/repo/wiki_test.go22
-rw-r--r--routers/web/user/home.go2
-rw-r--r--routers/web/user/notification.go5
-rw-r--r--routers/web/user/oauth.go2
-rw-r--r--routers/web/user/profile.go2
-rw-r--r--routers/web/web.go28
31 files changed, 339 insertions, 192 deletions
diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go
index 460b740171..5fd15b5c5a 100644
--- a/routers/web/admin/auths.go
+++ b/routers/web/admin/auths.go
@@ -8,7 +8,9 @@ import (
"errors"
"fmt"
"net/http"
+ "net/url"
"regexp"
+ "strconv"
"code.gitea.io/gitea/models/login"
"code.gitea.io/gitea/modules/auth/pam"
@@ -396,7 +398,7 @@ func EditAuthSourcePost(ctx *context.Context) {
log.Trace("Authentication changed by admin(%s): %d", ctx.User.Name, source.ID)
ctx.Flash.Success(ctx.Tr("admin.auths.update_success"))
- ctx.Redirect(setting.AppSubURL + "/admin/auths/" + fmt.Sprint(form.ID))
+ ctx.Redirect(setting.AppSubURL + "/admin/auths/" + strconv.FormatInt(form.ID, 10))
}
// DeleteAuthSource response for deleting an auth source
@@ -414,7 +416,7 @@ func DeleteAuthSource(ctx *context.Context) {
ctx.Flash.Error(fmt.Sprintf("DeleteLoginSource: %v", err))
}
ctx.JSON(http.StatusOK, map[string]interface{}{
- "redirect": setting.AppSubURL + "/admin/auths/" + ctx.Params(":authid"),
+ "redirect": setting.AppSubURL + "/admin/auths/" + url.PathEscape(ctx.Params(":authid")),
})
return
}
diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go
index a13f7317e4..432dd2f6ae 100644
--- a/routers/web/admin/repos.go
+++ b/routers/web/admin/repos.go
@@ -58,7 +58,7 @@ func DeleteRepo(ctx *context.Context) {
ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success"))
ctx.JSON(http.StatusOK, map[string]interface{}{
- "redirect": setting.AppSubURL + "/admin/repos?page=" + ctx.FormString("page") + "&sort=" + ctx.FormString("sort"),
+ "redirect": setting.AppSubURL + "/admin/repos?page=" + url.QueryEscape(ctx.FormString("page")) + "&sort=" + url.QueryEscape(ctx.FormString("sort")),
})
}
@@ -161,5 +161,5 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.delete_preexisting_success", dir))
}
- ctx.Redirect(setting.AppSubURL + "/admin/repos/unadopted?search=true&q=" + url.QueryEscape(q) + "&page=" + page)
+ ctx.Redirect(setting.AppSubURL + "/admin/repos/unadopted?search=true&q=" + url.QueryEscape(q) + "&page=" + url.QueryEscape(page))
}
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index db7fe7b36f..8bafd1f19c 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -6,8 +6,8 @@
package admin
import (
- "fmt"
"net/http"
+ "net/url"
"strconv"
"strings"
@@ -188,7 +188,7 @@ func NewUserPost(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name))
- ctx.Redirect(setting.AppSubURL + "/admin/users/" + fmt.Sprint(u.ID))
+ ctx.Redirect(setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10))
}
func prepareUserInfo(ctx *context.Context) *models.User {
@@ -366,7 +366,7 @@ func EditUserPost(ctx *context.Context) {
log.Trace("Account profile updated by admin (%s): %s", ctx.User.Name, u.Name)
ctx.Flash.Success(ctx.Tr("admin.users.update_profile_success"))
- ctx.Redirect(setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"))
+ ctx.Redirect(setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")))
}
// DeleteUser response for deleting a user
@@ -382,12 +382,12 @@ func DeleteUser(ctx *context.Context) {
case models.IsErrUserOwnRepos(err):
ctx.Flash.Error(ctx.Tr("admin.users.still_own_repo"))
ctx.JSON(http.StatusOK, map[string]interface{}{
- "redirect": setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"),
+ "redirect": setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")),
})
case models.IsErrUserHasOrgs(err):
ctx.Flash.Error(ctx.Tr("admin.users.still_has_org"))
ctx.JSON(http.StatusOK, map[string]interface{}{
- "redirect": setting.AppSubURL + "/admin/users/" + ctx.Params(":userid"),
+ "redirect": setting.AppSubURL + "/admin/users/" + url.PathEscape(ctx.Params(":userid")),
})
default:
ctx.ServerError("DeleteUser", err)
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index dfb03785a6..4743668621 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -15,10 +15,35 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/util"
"github.com/gorilla/feeds"
)
+func toBranchLink(act *models.Action) string {
+ return act.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
+}
+
+func toTagLink(act *models.Action) string {
+ return act.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
+}
+
+func toIssueLink(act *models.Action) string {
+ return act.GetRepoLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
+}
+
+func toPullLink(act *models.Action) string {
+ return act.GetRepoLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
+}
+
+func toSrcLink(act *models.Action) string {
+ return act.GetRepoLink() + "/src/" + util.PathEscapeSegments(act.GetBranch())
+}
+
+func toReleaseLink(act *models.Action) string {
+ return act.GetRepoLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
+}
+
// feedActionsToFeedItems convert gitea's Action feed to feeds Item
func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (items []*feeds.Item, err error) {
for _, act := range actions {
@@ -32,62 +57,111 @@ func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (ite
title = act.ActUser.DisplayName() + " "
switch act.OpType {
case models.ActionCreateRepo:
- title += ctx.Tr("action.create_repo", act.GetRepoLink(), act.ShortRepoPath())
+ title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoLink(), act.ShortRepoPath())
+ link.Href = act.GetRepoLink()
case models.ActionRenameRepo:
- title += ctx.Tr("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
+ title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
+ link.Href = act.GetRepoLink()
case models.ActionCommitRepo:
- branchLink := act.GetBranch()
+ link.Href = toBranchLink(act)
if len(act.Content) != 0 {
- title += ctx.Tr("action.commit_repo", act.GetRepoLink(), branchLink, act.GetBranch(), act.ShortRepoPath())
+ title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
} else {
- title += ctx.Tr("action.create_branch", act.GetRepoLink(), branchLink, act.GetBranch(), act.ShortRepoPath())
+ title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
}
case models.ActionCreateIssue:
- title += ctx.Tr("action.create_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ link.Href = toIssueLink(act)
+ title += ctx.TrHTMLEscapeArgs("action.create_issue", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionCreatePullRequest:
- title += ctx.Tr("action.create_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ link.Href = toPullLink(act)
+ title += ctx.TrHTMLEscapeArgs("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionTransferRepo:
- title += ctx.Tr("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
+ link.Href = act.GetRepoLink()
+ title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
case models.ActionPushTag:
- title += ctx.Tr("action.push_tag", act.GetRepoLink(), url.QueryEscape(act.GetTag()), act.ShortRepoPath())
+ link.Href = toTagLink(act)
+ title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoLink(), link.Href, act.GetTag(), act.ShortRepoPath())
case models.ActionCommentIssue:
- title += ctx.Tr("action.comment_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ issueLink := toIssueLink(act)
+ if link.Href == "#" {
+ link.Href = issueLink
+ }
+ title += ctx.TrHTMLEscapeArgs("action.comment_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionMergePullRequest:
- title += ctx.Tr("action.merge_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ pullLink := toPullLink(act)
+ if link.Href == "#" {
+ link.Href = pullLink
+ }
+ title += ctx.TrHTMLEscapeArgs("action.merge_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionCloseIssue:
- title += ctx.Tr("action.close_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ issueLink := toIssueLink(act)
+ if link.Href == "#" {
+ link.Href = issueLink
+ }
+ title += ctx.TrHTMLEscapeArgs("action.close_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionReopenIssue:
- title += ctx.Tr("action.reopen_issue", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ issueLink := toIssueLink(act)
+ if link.Href == "#" {
+ link.Href = issueLink
+ }
+ title += ctx.TrHTMLEscapeArgs("action.reopen_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionClosePullRequest:
- title += ctx.Tr("action.close_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ pullLink := toPullLink(act)
+ if link.Href == "#" {
+ link.Href = pullLink
+ }
+ title += ctx.TrHTMLEscapeArgs("action.close_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionReopenPullRequest:
- title += ctx.Tr("action.reopen_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath)
+ pullLink := toPullLink(act)
+ if link.Href == "#" {
+ link.Href = pullLink
+ }
+ title += ctx.TrHTMLEscapeArgs("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionDeleteTag:
- title += ctx.Tr("action.delete_tag", act.GetRepoLink(), html.EscapeString(act.GetTag()), act.ShortRepoPath())
+ link.Href = act.GetRepoLink()
+ title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoLink(), act.GetTag(), act.ShortRepoPath())
case models.ActionDeleteBranch:
- title += ctx.Tr("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
+ link.Href = act.GetRepoLink()
+ title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
case models.ActionMirrorSyncPush:
- title += ctx.Tr("action.mirror_sync_push", act.GetRepoLink(), url.QueryEscape(act.GetBranch()), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
+ srcLink := toSrcLink(act)
+ if link.Href == "#" {
+ link.Href = srcLink
+ }
+ title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
case models.ActionMirrorSyncCreate:
- title += ctx.Tr("action.mirror_sync_create", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
+ srcLink := toSrcLink(act)
+ if link.Href == "#" {
+ link.Href = srcLink
+ }
+ title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
case models.ActionMirrorSyncDelete:
- title += ctx.Tr("action.mirror_sync_delete", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
+ link.Href = act.GetRepoLink()
+ title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoLink(), act.GetBranch(), act.ShortRepoPath())
case models.ActionApprovePullRequest:
- title += ctx.Tr("action.approve_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ pullLink := toPullLink(act)
+ title += ctx.TrHTMLEscapeArgs("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionRejectPullRequest:
- title += ctx.Tr("action.reject_pull_request", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ pullLink := toPullLink(act)
+ title += ctx.TrHTMLEscapeArgs("action.reject_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionCommentPull:
- title += ctx.Tr("action.comment_pull", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath())
+ pullLink := toPullLink(act)
+ title += ctx.TrHTMLEscapeArgs("action.comment_pull", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
case models.ActionPublishRelease:
- title += ctx.Tr("action.publish_release", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath(), act.Content)
+ releaseLink := toReleaseLink(act)
+ if link.Href == "#" {
+ link.Href = releaseLink
+ }
+ title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoLink(), releaseLink, act.ShortRepoPath(), act.Content)
case models.ActionPullReviewDismissed:
- title += ctx.Tr("action.review_dismissed", act.GetRepoLink(), act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
+ pullLink := toPullLink(act)
+ title += ctx.TrHTMLEscapeArgs("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
case models.ActionStarRepo:
- title += ctx.Tr("action.starred_repo", act.GetRepoLink(), act.GetRepoPath())
- link = &feeds.Link{Href: act.GetRepoLink()}
+ link.Href = act.GetRepoLink()
+ title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoLink(), act.GetRepoPath())
case models.ActionWatchRepo:
- title += ctx.Tr("action.watched_repo", act.GetRepoLink(), act.GetRepoPath())
- link = &feeds.Link{Href: act.GetRepoLink()}
+ link.Href = act.GetRepoLink()
+ title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoLink(), act.GetRepoPath())
default:
return nil, fmt.Errorf("unknown action type: %v", act.OpType)
}
@@ -104,7 +178,7 @@ func feedActionsToFeedItems(ctx *context.Context, actions []*models.Action) (ite
desc += "\n\n"
}
desc += fmt.Sprintf("<a href=\"%s\">%s</a>\n%s",
- fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), commit.Sha1),
+ html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), commit.Sha1)),
commit.Sha1,
templates.RenderCommitMessage(commit.Message, repoLink, nil),
)
diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go
index d8add77f66..53c31a1c60 100644
--- a/routers/web/org/setting.go
+++ b/routers/web/org/setting.go
@@ -7,6 +7,7 @@ package org
import (
"net/http"
+ "net/url"
"strings"
"code.gitea.io/gitea/models"
@@ -76,7 +77,7 @@ func SettingsPost(ctx *context.Context) {
return
}
// reset ctx.org.OrgLink with new name
- ctx.Org.OrgLink = setting.AppSubURL + "/org/" + form.Name
+ ctx.Org.OrgLink = setting.AppSubURL + "/org/" + url.PathEscape(form.Name)
log.Trace("Organization name changed: %s -> %s", org.Name, form.Name)
nameChanged = false
}
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index 3fe97142ae..2fc72f0620 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -7,6 +7,7 @@ package org
import (
"net/http"
+ "net/url"
"path"
"strings"
@@ -105,7 +106,7 @@ func TeamsAction(ctx *context.Context) {
}
ctx.JSON(http.StatusOK,
map[string]interface{}{
- "redirect": ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName,
+ "redirect": ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName),
})
return
case "add":
@@ -119,7 +120,7 @@ func TeamsAction(ctx *context.Context) {
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
- ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
+ ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
} else {
ctx.ServerError(" GetUserByName", err)
}
@@ -128,7 +129,7 @@ func TeamsAction(ctx *context.Context) {
if u.IsOrganization() {
ctx.Flash.Error(ctx.Tr("form.cannot_add_org_to_team"))
- ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
+ ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
return
}
@@ -156,7 +157,7 @@ func TeamsAction(ctx *context.Context) {
switch page {
case "team":
- ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName)
+ ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName))
case "home":
ctx.Redirect(ctx.Org.Organization.HomeLink())
default:
@@ -181,7 +182,7 @@ func TeamsRepoAction(ctx *context.Context) {
if err != nil {
if models.IsErrRepoNotExist(err) {
ctx.Flash.Error(ctx.Tr("org.teams.add_nonexistent_repo"))
- ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")
+ ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
return
}
ctx.ServerError("GetRepositoryByName", err)
@@ -204,11 +205,11 @@ func TeamsRepoAction(ctx *context.Context) {
if action == "addall" || action == "removeall" {
ctx.JSON(http.StatusOK, map[string]interface{}{
- "redirect": ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories",
+ "redirect": ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories",
})
return
}
- ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")
+ ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories")
}
// NewTeam render create new team page
@@ -273,7 +274,7 @@ func NewTeamPost(ctx *context.Context) {
return
}
log.Trace("Team created: %s/%s", ctx.Org.Organization.Name, t.Name)
- ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName)
+ ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
}
// TeamMembers render team members page
@@ -375,7 +376,7 @@ func EditTeamPost(ctx *context.Context) {
}
return
}
- ctx.Redirect(ctx.Org.OrgLink + "/teams/" + t.LowerName)
+ ctx.Redirect(ctx.Org.OrgLink + "/teams/" + url.PathEscape(t.LowerName))
}
// DeleteTeam response for the delete team request
diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go
index 3632d1846e..110ec037e1 100644
--- a/routers/web/repo/blame.go
+++ b/routers/web/repo/blame.go
@@ -8,6 +8,7 @@ import (
"fmt"
gotemplate "html/template"
"net/http"
+ "net/url"
"strings"
"code.gitea.io/gitea/models"
@@ -17,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
)
const (
@@ -54,7 +56,7 @@ func RefBlame(ctx *context.Context) {
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
if len(ctx.Repo.TreePath) > 0 {
- treeLink += "/" + ctx.Repo.TreePath
+ treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
var treeNames []string
@@ -85,7 +87,7 @@ func RefBlame(ctx *context.Context) {
ctx.Data["TreeNames"] = treeNames
ctx.Data["BranchLink"] = branchLink
- ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath
+ ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
ctx.Data["PageIsViewCode"] = true
ctx.Data["IsBlame"] = true
@@ -236,8 +238,8 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
br.RepoLink = repoLink
br.PartSha = part.Sha
br.PreviousSha = previousSha
- br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, previousSha, ctx.Repo.TreePath)
- br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, part.Sha)
+ br.PreviousShaURL = fmt.Sprintf("%s/blame/commit/%s/%s", repoLink, url.PathEscape(previousSha), util.PathEscapeSegments(ctx.Repo.TreePath))
+ br.CommitURL = fmt.Sprintf("%s/commit/%s", repoLink, url.PathEscape(part.Sha))
br.CommitMessage = commit.CommitMessage
br.CommitSince = commitSince
}
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index 4c0f94f15d..06cce92417 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -8,7 +8,6 @@ package repo
import (
"errors"
"net/http"
- "path"
"strings"
"code.gitea.io/gitea/models"
@@ -323,8 +322,7 @@ func Diff(ctx *context.Context) {
return
}
}
- headTarget := path.Join(userName, repoName)
- setCompareContext(ctx, parentCommit, commit, headTarget)
+ setCompareContext(ctx, parentCommit, commit, userName, repoName)
ctx.Data["Title"] = commit.Summary() + " ยท " + base.ShortSha(commitID)
ctx.Data["Commit"] = commit
ctx.Data["Diff"] = diff
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 86ecc2bab1..01c324e9e9 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -12,7 +12,7 @@ import (
"html"
"io"
"net/http"
- "path"
+ "net/url"
"path/filepath"
"strings"
@@ -38,7 +38,7 @@ const (
)
// setCompareContext sets context data.
-func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headTarget string) {
+func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headOwner, headName string) {
ctx.Data["BaseCommit"] = base
ctx.Data["HeadCommit"] = head
@@ -54,22 +54,28 @@ func setCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit,
return blob
}
- setPathsCompareContext(ctx, base, head, headTarget)
+ setPathsCompareContext(ctx, base, head, headOwner, headName)
setImageCompareContext(ctx)
setCsvCompareContext(ctx)
}
-// setPathsCompareContext sets context data for source and raw paths
-func setPathsCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headTarget string) {
- sourcePath := setting.AppSubURL + "/%s/src/commit/%s"
- rawPath := setting.AppSubURL + "/%s/raw/commit/%s"
+// SourceCommitURL creates a relative URL for a commit in the given repository
+func SourceCommitURL(owner, name string, commit *git.Commit) string {
+ return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/src/commit/" + url.PathEscape(commit.ID.String())
+}
- ctx.Data["SourcePath"] = fmt.Sprintf(sourcePath, headTarget, head.ID)
- ctx.Data["RawPath"] = fmt.Sprintf(rawPath, headTarget, head.ID)
+// RawCommitURL creates a relative URL for the raw commit in the given repository
+func RawCommitURL(owner, name string, commit *git.Commit) string {
+ return setting.AppSubURL + "/" + url.PathEscape(owner) + "/" + url.PathEscape(name) + "/raw/commit/" + url.PathEscape(commit.ID.String())
+}
+
+// setPathsCompareContext sets context data for source and raw paths
+func setPathsCompareContext(ctx *context.Context, base *git.Commit, head *git.Commit, headOwner, headName string) {
+ ctx.Data["SourcePath"] = SourceCommitURL(headOwner, headName, head)
+ ctx.Data["RawPath"] = RawCommitURL(headOwner, headName, head)
if base != nil {
- baseTarget := path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
- ctx.Data["BeforeSourcePath"] = fmt.Sprintf(sourcePath, baseTarget, base.ID)
- ctx.Data["BeforeRawPath"] = fmt.Sprintf(rawPath, baseTarget, base.ID)
+ ctx.Data["BeforeSourcePath"] = SourceCommitURL(headOwner, headName, head)
+ ctx.Data["BeforeRawPath"] = RawCommitURL(headOwner, headName, head)
}
}
@@ -619,8 +625,7 @@ func PrepareCompareDiff(
ctx.Data["Username"] = ci.HeadUser.Name
ctx.Data["Reponame"] = ci.HeadRepo.Name
- headTarget := path.Join(ci.HeadUser.Name, repo.Name)
- setCompareContext(ctx, baseCommit, headCommit, headTarget)
+ setCompareContext(ctx, baseCommit, headCommit, ci.HeadUser.Name, repo.Name)
return false
}
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index d9f8c20092..088edbfd29 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -204,7 +204,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
ctx.Data["TreePath"] = form.TreePath
ctx.Data["TreeNames"] = treeNames
ctx.Data["TreePaths"] = treePaths
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + ctx.Repo.BranchName
+ ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(ctx.Repo.BranchName)
ctx.Data["FileContent"] = form.Content
ctx.Data["commit_summary"] = form.CommitSummary
ctx.Data["commit_message"] = form.CommitMessage
@@ -299,9 +299,9 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
ctx.Error(http.StatusInternalServerError, err.Error())
}
} else if models.IsErrCommitIDDoesNotMatch(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplEditFile, &form)
+ ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplEditFile, &form)
} else if git.IsErrPushOutOfDate(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
+ ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
} else if git.IsErrPushRejected(err) {
errPushRej := err.(*git.ErrPushRejected)
if len(errPushRej.Message) == 0 {
@@ -495,7 +495,7 @@ func DeleteFilePost(ctx *context.Context) {
ctx.Error(http.StatusInternalServerError, err.Error())
}
} else if models.IsErrCommitIDDoesNotMatch(err) || git.IsErrPushOutOfDate(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+form.LastCommit+"..."+ctx.Repo.CommitID), tplDeleteFile, &form)
+ ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_deleting", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplDeleteFile, &form)
} else if git.IsErrPushRejected(err) {
errPushRej := err.(*git.ErrPushRejected)
if len(errPushRej.Message) == 0 {
@@ -602,7 +602,7 @@ func UploadFilePost(ctx *context.Context) {
ctx.Data["TreePath"] = form.TreePath
ctx.Data["TreeNames"] = treeNames
ctx.Data["TreePaths"] = treePaths
- ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + branchName
+ ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName)
ctx.Data["commit_summary"] = form.CommitSummary
ctx.Data["commit_message"] = form.CommitMessage
ctx.Data["commit_choice"] = form.CommitChoice
@@ -698,7 +698,7 @@ func UploadFilePost(ctx *context.Context) {
branchErr := err.(models.ErrBranchAlreadyExists)
ctx.RenderWithErr(ctx.Tr("repo.editor.branch_already_exists", branchErr.BranchName), tplUploadFile, &form)
} else if git.IsErrPushOutOfDate(err) {
- ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+ctx.Repo.CommitID+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
+ ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(ctx.Repo.CommitID)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplUploadFile, &form)
} else if git.IsErrPushRejected(err) {
errPushRej := err.(*git.ErrPushRejected)
if len(errPushRej.Message) == 0 {
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index d9e15a784f..95363258e9 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -11,6 +11,7 @@ import (
"fmt"
"io"
"net/http"
+ "net/url"
"path"
"strconv"
"strings"
@@ -106,7 +107,7 @@ func MustAllowPulls(ctx *context.Context) {
// User can send pull request if owns a forked repository.
if ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID) {
ctx.Repo.PullRequest.Allowed = true
- ctx.Repo.PullRequest.HeadInfo = ctx.User.Name + ":" + ctx.Repo.BranchName
+ ctx.Repo.PullRequest.HeadInfoSubURL = url.PathEscape(ctx.User.Name) + ":" + util.PathEscapeSegments(ctx.Repo.BranchName)
}
}
@@ -764,7 +765,7 @@ func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleDirs [
for _, repoLabel := range repoLabels {
if strings.EqualFold(repoLabel.Name, metaLabel) {
repoLabel.IsChecked = true
- labelIDs = append(labelIDs, fmt.Sprintf("%d", repoLabel.ID))
+ labelIDs = append(labelIDs, strconv.FormatInt(repoLabel.ID, 10))
break
}
}
@@ -983,6 +984,7 @@ func NewIssuePost(ctx *context.Context) {
issue := &models.Issue{
RepoID: repo.ID,
+ Repo: repo,
Title: form.Title,
PosterID: ctx.User.ID,
Poster: ctx.User,
@@ -1009,9 +1011,9 @@ func NewIssuePost(ctx *context.Context) {
log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
if ctx.FormString("redirect_after_creation") == "project" {
- ctx.Redirect(ctx.Repo.RepoLink + "/projects/" + fmt.Sprint(form.ProjectID))
+ ctx.Redirect(ctx.Repo.RepoLink + "/projects/" + strconv.FormatInt(form.ProjectID, 10))
} else {
- ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
}
}
@@ -1097,13 +1099,16 @@ func ViewIssue(ctx *context.Context) {
}
return
}
+ if issue.Repo == nil {
+ issue.Repo = ctx.Repo.Repository
+ }
// Make sure type and URL matches.
if ctx.Params(":type") == "issues" && issue.IsPull {
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
return
} else if ctx.Params(":type") == "pulls" && !issue.IsPull {
- ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
@@ -1496,7 +1501,7 @@ func ViewIssue(ctx *context.Context) {
log.Error("IsProtectedBranch: %v", err)
} else if !protected {
canDelete = true
- ctx.Data["DeleteBranchLink"] = ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index) + "/cleanup"
+ ctx.Data["DeleteBranchLink"] = issue.Link() + "/cleanup"
}
}
}
@@ -1624,7 +1629,7 @@ func ViewIssue(ctx *context.Context) {
ctx.Data["NumParticipants"] = len(participants)
ctx.Data["Issue"] = issue
ctx.Data["ReadOnly"] = false
- ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + ctx.Data["Link"].(string)
+ ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login?redirect_to=" + url.QueryEscape(ctx.Data["Link"].(string))
ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.User.ID)
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull)
ctx.Data["HasProjectsWritePermission"] = ctx.Repo.CanWrite(unit.TypeProjects)
@@ -1773,7 +1778,7 @@ func UpdateIssueContent(ctx *context.Context) {
}
content, err := markdown.RenderString(&markup.RenderContext{
- URLPrefix: ctx.FormString("context"),
+ URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
Ctx: ctx,
@@ -2205,7 +2210,7 @@ func UpdateCommentContent(ctx *context.Context) {
}
content, err := markdown.RenderString(&markup.RenderContext{
- URLPrefix: ctx.FormString("context"),
+ URLPrefix: ctx.FormString("context"), // FIXME: <- IS THIS SAFE ?
Metas: ctx.Repo.Repository.ComposeMetas(),
GitRepo: ctx.Repo.GitRepo,
Ctx: ctx,
diff --git a/routers/web/repo/issue_stopwatch.go b/routers/web/repo/issue_stopwatch.go
index b8efb3b841..0e9405fde4 100644
--- a/routers/web/repo/issue_stopwatch.go
+++ b/routers/web/repo/issue_stopwatch.go
@@ -94,6 +94,7 @@ func GetActiveStopwatch(c *context.Context) {
}
c.Data["ActiveStopwatch"] = StopwatchTmplInfo{
+ issue.Link(),
issue.Repo.FullName(),
issue.Index,
sw.Seconds() + 1, // ensure time is never zero in ui
@@ -102,6 +103,7 @@ func GetActiveStopwatch(c *context.Context) {
// StopwatchTmplInfo is a view on a stopwatch specifically for template rendering
type StopwatchTmplInfo struct {
+ IssueLink string
RepoSlug string
IssueIndex int64
Seconds int64
diff --git a/routers/web/repo/lfs.go b/routers/web/repo/lfs.go
index 5e24cfa3c0..b15c7628db 100644
--- a/routers/web/repo/lfs.go
+++ b/routers/web/repo/lfs.go
@@ -10,6 +10,7 @@ import (
gotemplate "html/template"
"io"
"net/http"
+ "net/url"
"path"
"strconv"
"strings"
@@ -285,7 +286,7 @@ func LFSFileGet(ctx *context.Context) {
fileSize := meta.Size
ctx.Data["FileSize"] = meta.Size
- ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s.git/info/lfs/objects/%s/%s", setting.AppURL, ctx.Repo.Repository.FullName(), meta.Oid, "direct")
+ ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s/%s.git/info/lfs/objects/%s/%s", setting.AppURL, url.PathEscape(ctx.Repo.Repository.OwnerName), url.PathEscape(ctx.Repo.Repository.Name), url.PathEscape(meta.Oid), "direct")
switch {
case isRepresentableAsText:
if st.IsSvgImage() {
diff --git a/routers/web/repo/migrate.go b/routers/web/repo/migrate.go
index d5e0a7696b..f91c344e94 100644
--- a/routers/web/repo/migrate.go
+++ b/routers/web/repo/migrate.go
@@ -7,6 +7,7 @@ package repo
import (
"net/http"
+ "net/url"
"strings"
"code.gitea.io/gitea/models"
@@ -237,7 +238,7 @@ func MigratePost(ctx *context.Context) {
err = task.MigrateRepository(ctx.User, ctxUser, opts)
if err == nil {
- ctx.Redirect(ctxUser.HomeLink() + "/" + opts.RepoName)
+ ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(opts.RepoName))
return
}
diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go
index 21e1fb2eab..eadc89333f 100644
--- a/routers/web/repo/milestone.go
+++ b/routers/web/repo/milestone.go
@@ -6,6 +6,7 @@ package repo
import (
"net/http"
+ "net/url"
"time"
"code.gitea.io/gitea/models"
@@ -244,7 +245,7 @@ func ChangeMilestoneStatus(ctx *context.Context) {
}
return
}
- ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=" + ctx.Params(":action"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/milestones?state=" + url.QueryEscape(ctx.Params(":action")))
}
// DeleteMilestone delete a milestone
diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go
index 08b285df0a..437da14d45 100644
--- a/routers/web/repo/projects.go
+++ b/routers/web/repo/projects.go
@@ -7,6 +7,7 @@ package repo
import (
"fmt"
"net/http"
+ "net/url"
"strings"
"code.gitea.io/gitea/models"
@@ -173,7 +174,7 @@ func ChangeProjectStatus(ctx *context.Context) {
}
return
}
- ctx.Redirect(ctx.Repo.RepoLink + "/projects?state=" + ctx.Params(":action"))
+ ctx.Redirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action")))
}
// DeleteProject delete a project
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 0ac05a7609..4337278214 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -10,8 +10,10 @@ import (
"crypto/subtle"
"errors"
"fmt"
+ "html"
"net/http"
- "path"
+ "net/url"
+ "strconv"
"strings"
"time"
@@ -34,7 +36,6 @@ import (
"code.gitea.io/gitea/services/gitdiff"
pull_service "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
- "github.com/unknwon/com"
)
const (
@@ -109,8 +110,7 @@ func getForkRepository(ctx *context.Context) *models.Repository {
ctx.Data["IsPrivate"] = forkRepo.IsPrivate || forkRepo.Owner.Visibility == structs.VisibleTypePrivate
canForkToUser := forkRepo.OwnerID != ctx.User.ID && !ctx.User.HasForkedRepo(forkRepo.ID)
- ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name
- ctx.Data["ForkFromOwnerID"] = forkRepo.Owner.ID
+ ctx.Data["ForkRepo"] = forkRepo
if err := ctx.User.GetOwnedOrganizations(); err != nil {
ctx.ServerError("GetOwnedOrganizations", err)
@@ -202,7 +202,7 @@ func ForkPost(ctx *context.Context) {
}
repo, has := models.HasForkedRepo(ctxUser.ID, traverseParentRepo.ID)
if has {
- ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
+ ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
return
}
if !traverseParentRepo.IsFork {
@@ -248,7 +248,7 @@ func ForkPost(ctx *context.Context) {
}
log.Trace("Repository forked[%d]: %s/%s", forkRepo.ID, ctxUser.Name, repo.Name)
- ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
+ ctx.Redirect(ctxUser.HomeLink() + "/" + url.PathEscape(repo.Name))
}
func checkPullInfo(ctx *context.Context) *models.Issue {
@@ -682,8 +682,7 @@ func ViewPullFiles(ctx *context.Context) {
}
}
- headTarget := path.Join(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
- setCompareContext(ctx, baseCommit, commit, headTarget)
+ setCompareContext(ctx, baseCommit, commit, ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
ctx.Data["RequireHighlightJS"] = true
ctx.Data["RequireSimpleMDE"] = true
@@ -746,7 +745,7 @@ func UpdatePullRequest(ctx *context.Context) {
// ToDo: add check if maintainers are allowed to change branch ... (need migration & co)
if (!allowedUpdateByMerge && !rebase) || (rebase && !allowedUpdateByRebase) {
ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
@@ -766,7 +765,7 @@ func UpdatePullRequest(ctx *context.Context) {
return
}
ctx.Flash.Error(flashError)
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
return
} else if models.IsErrRebaseConflicts(err) {
conflictError := err.(models.ErrRebaseConflicts)
@@ -780,19 +779,19 @@ func UpdatePullRequest(ctx *context.Context) {
return
}
ctx.Flash.Error(flashError)
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
ctx.Flash.Error(err.Error())
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
time.Sleep(1 * time.Second)
ctx.Flash.Success(ctx.Tr("repo.pulls.update_branch_success"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
}
// MergePullRequest response for merging pull request
@@ -805,11 +804,11 @@ func MergePullRequest(ctx *context.Context) {
if issue.IsClosed {
if issue.IsPull {
ctx.Flash.Error(ctx.Tr("repo.pulls.is_closed"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
ctx.Flash.Error(ctx.Tr("repo.issues.closed_title"))
- ctx.Redirect(ctx.Repo.RepoLink + "/issues/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
@@ -822,13 +821,13 @@ func MergePullRequest(ctx *context.Context) {
}
if !allowedMerge {
ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
if pr.HasMerged {
ctx.Flash.Error(ctx.Tr("repo.pulls.has_merged"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
@@ -837,11 +836,11 @@ func MergePullRequest(ctx *context.Context) {
if err = pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
if models.IsErrInvalidMergeStyle(err) {
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
+ ctx.Redirect(issue.Link())
return
} else if strings.Contains(err.Error(), "Wrong commit ID") {
ctx.Flash.Error(ctx.Tr("repo.pulls.wrong_commit_id"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
@@ -849,19 +848,19 @@ func MergePullRequest(ctx *context.Context) {
return
}
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
if !pr.CanAutoMerge() {
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(issue.Index))
+ ctx.Redirect(issue.Link())
return
}
if pr.IsWorkInProgress() {
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_wip"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
}
@@ -875,14 +874,14 @@ func MergePullRequest(ctx *context.Context) {
return
} else if !isRepoAdmin {
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
}
}
if ctx.HasError() {
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
}
@@ -914,14 +913,14 @@ func MergePullRequest(ctx *context.Context) {
if !noDeps {
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
}
if err = pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil {
if models.IsErrInvalidMergeStyle(err) {
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
} else if models.IsErrMergeConflicts(err) {
conflictError := err.(models.ErrMergeConflicts)
@@ -935,7 +934,7 @@ func MergePullRequest(ctx *context.Context) {
return
}
ctx.Flash.Error(flashError)
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
} else if models.IsErrRebaseConflicts(err) {
conflictError := err.(models.ErrRebaseConflicts)
@@ -949,17 +948,17 @@ func MergePullRequest(ctx *context.Context) {
return
}
ctx.Flash.Error(flashError)
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
} else if models.IsErrMergeUnrelatedHistories(err) {
log.Debug("MergeUnrelatedHistories error: %v", err)
ctx.Flash.Error(ctx.Tr("repo.pulls.unrelated_histories"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
} else if git.IsErrPushOutOfDate(err) {
log.Debug("MergePushOutOfDate error: %v", err)
ctx.Flash.Error(ctx.Tr("repo.pulls.merge_out_of_date"))
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
} else if git.IsErrPushRejected(err) {
log.Debug("MergePushRejected error: %v", err)
@@ -979,7 +978,7 @@ func MergePullRequest(ctx *context.Context) {
}
ctx.Flash.Error(flashError)
}
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
return
}
ctx.ServerError("Merge", err)
@@ -1008,7 +1007,7 @@ func MergePullRequest(ctx *context.Context) {
deleteBranch(ctx, pr, headRepo)
}
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pr.Index))
+ ctx.Redirect(issue.Link())
}
func stopTimerIfAvailable(user *models.User, issue *models.Issue) error {
@@ -1097,6 +1096,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
pullIssue := &models.Issue{
RepoID: repo.ID,
+ Repo: repo,
Title: form.Title,
PosterID: ctx.User.ID,
Poster: ctx.User,
@@ -1138,7 +1138,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
}
ctx.Flash.Error(flashError)
}
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pullIssue.Index))
+ ctx.Redirect(pullIssue.Link())
return
}
ctx.ServerError("NewPullRequest", err)
@@ -1146,7 +1146,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
}
log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
- ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + fmt.Sprint(pullIssue.Index))
+ ctx.Redirect(pullIssue.Link())
}
// TriggerTask response for a trigger task request
@@ -1261,7 +1261,7 @@ func CleanUpPullRequest(ctx *context.Context) {
defer func() {
ctx.JSON(http.StatusOK, map[string]interface{}{
- "redirect": pr.BaseRepo.Link() + "/pulls/" + fmt.Sprint(issue.Index),
+ "redirect": issue.Link(),
})
}()
@@ -1369,7 +1369,7 @@ func UpdatePullRequestTarget(ctx *context.Context) {
err := err.(models.ErrPullRequestAlreadyExists)
RepoRelPath := ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
- errorMessage := ctx.Tr("repo.pulls.has_pull_request", ctx.Repo.RepoLink, RepoRelPath, err.IssueID)
+ errorMessage := ctx.Tr("repo.pulls.has_pull_request", html.EscapeString(ctx.Repo.RepoLink+"/pulls/"+strconv.FormatInt(err.IssueID, 10)), html.EscapeString(RepoRelPath), err.IssueID) // FIXME: Creates url insidde locale string
ctx.Flash.Error(errorMessage)
ctx.JSON(http.StatusConflict, map[string]interface{}{
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index 20f6ddd2a5..3f12ee72bc 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -20,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/upload"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
releaseservice "code.gitea.io/gitea/services/release"
@@ -350,7 +351,7 @@ func NewReleasePost(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.tag.create_success", form.TagName))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + form.TagName)
+ ctx.Redirect(ctx.Repo.RepoLink + "/src/tag/" + util.PathEscapeSegments(form.TagName))
return
}
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index c70dec6481..46cef7664a 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -244,7 +244,7 @@ func CreatePost(ctx *context.Context) {
repo, err = repo_service.GenerateRepository(ctx.User, ctxUser, templateRepo, opts)
if err == nil {
log.Trace("Repository generated [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
- ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
+ ctx.Redirect(repo.Link())
return
}
} else {
@@ -263,7 +263,7 @@ func CreatePost(ctx *context.Context) {
})
if err == nil {
log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
- ctx.Redirect(ctxUser.HomeLink() + "/" + repo.Name)
+ ctx.Redirect(repo.Link())
return
}
}
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go
index cecd1da07c..641052316c 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting.go
@@ -615,7 +615,7 @@ func SettingsPost(ctx *context.Context) {
log.Trace("Repository transfer process was started: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner)
ctx.Flash.Success(ctx.Tr("repo.settings.transfer_started", newOwner.DisplayName()))
- ctx.Redirect(ctx.Repo.Owner.HomeLink() + "/" + repo.Name + "/settings")
+ ctx.Redirect(repo.Link() + "/settings")
case "cancel_transfer":
if !ctx.Repo.IsOwner() {
@@ -627,7 +627,7 @@ func SettingsPost(ctx *context.Context) {
if err != nil {
if models.IsErrNoPendingTransfer(err) {
ctx.Flash.Error("repo.settings.transfer_abort_invalid")
- ctx.Redirect(ctx.User.HomeLink() + "/" + repo.Name + "/settings")
+ ctx.Redirect(repo.Link() + "/settings")
} else {
ctx.ServerError("GetPendingRepositoryTransfer", err)
}
@@ -647,7 +647,7 @@ func SettingsPost(ctx *context.Context) {
log.Trace("Repository transfer process was cancelled: %s/%s ", ctx.Repo.Owner.Name, repo.Name)
ctx.Flash.Success(ctx.Tr("repo.settings.transfer_abort_success", repoTransfer.Recipient.Name))
- ctx.Redirect(ctx.Repo.Owner.HomeLink() + "/" + repo.Name + "/settings")
+ ctx.Redirect(repo.Link() + "/settings")
case "delete":
if !ctx.Repo.IsOwner() {
@@ -796,7 +796,7 @@ func Collaboration(ctx *context.Context) {
func CollaborationPost(ctx *context.Context) {
name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("collaborator")))
if len(name) == 0 || ctx.Repo.Owner.LowerName == name {
- ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
+ ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
return
}
@@ -804,7 +804,7 @@ func CollaborationPost(ctx *context.Context) {
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Flash.Error(ctx.Tr("form.user_not_exist"))
- ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
+ ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
} else {
ctx.ServerError("GetUserByName", err)
}
@@ -813,14 +813,14 @@ func CollaborationPost(ctx *context.Context) {
if !u.IsActive {
ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_inactive_user"))
- ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
+ ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
return
}
// Organization is not allowed to be added as a collaborator.
if u.IsOrganization() {
ctx.Flash.Error(ctx.Tr("repo.settings.org_not_allowed_to_be_collaborator"))
- ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
+ ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
return
}
@@ -840,7 +840,7 @@ func CollaborationPost(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
- ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
+ ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
}
// ChangeCollaborationAccessMode response for changing access of a collaboration
diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go
index 876ff9ba46..32105b1d43 100644
--- a/routers/web/repo/setting_protected_branch.go
+++ b/routers/web/repo/setting_protected_branch.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
pull_service "code.gitea.io/gitea/services/pull"
@@ -89,7 +90,7 @@ func ProtectedBranchPost(ctx *context.Context) {
log.Trace("Repository basic settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
+ ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
default:
ctx.NotFound("", nil)
}
@@ -197,7 +198,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
}
if f.RequiredApprovals < 0 {
ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_required_approvals_min"))
- ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch))
+ ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch)))
}
var whitelistUsers, whitelistTeams, mergeWhitelistUsers, mergeWhitelistTeams, approvalsWhitelistUsers, approvalsWhitelistTeams []int64
@@ -274,7 +275,7 @@ func SettingsProtectedBranchPost(ctx *context.Context) {
return
}
ctx.Flash.Success(ctx.Tr("repo.settings.update_protect_branch_success", branch))
- ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, branch))
+ ctx.Redirect(fmt.Sprintf("%s/settings/branches/%s", ctx.Repo.RepoLink, util.PathEscapeSegments(branch)))
} else {
if protectBranch != nil {
if err := ctx.Repo.Repository.DeleteProtectedBranch(protectBranch.ID); err != nil {
diff --git a/routers/web/repo/tag.go b/routers/web/repo/tag.go
index a180399c9e..b4d268759c 100644
--- a/routers/web/repo/tag.go
+++ b/routers/web/repo/tag.go
@@ -58,7 +58,7 @@ func NewProtectedTagPost(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
- ctx.Redirect(setting.AppSubURL + ctx.Req.URL.Path)
+ ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
}
// EditProtectedTag render the page to edit a protect tag
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index cecd8437b6..12b3aef505 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -232,7 +232,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
}
if readmeFile != nil {
readmeFile.name = entry.Name() + "/" + readmeFile.name
- readmeTreelink = treeLink + "/" + entry.GetSubJumpablePathName()
+ readmeTreelink = treeLink + "/" + util.PathEscapeSegments(entry.GetSubJumpablePathName())
break
}
}
@@ -301,7 +301,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
fileSize = meta.Size
ctx.Data["FileSize"] = meta.Size
filenameBase64 := base64.RawURLEncoding.EncodeToString([]byte(readmeFile.name))
- ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s.git/info/lfs/objects/%s/%s", setting.AppURL, ctx.Repo.Repository.FullName(), meta.Oid, filenameBase64)
+ ctx.Data["RawFileLink"] = fmt.Sprintf("%s.git/info/lfs/objects/%s/%s", ctx.Repo.Repository.HTMLURL(), url.PathEscape(meta.Oid), url.PathEscape(filenameBase64))
}
}
}
@@ -376,7 +376,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
fileSize := blob.Size()
ctx.Data["FileIsSymlink"] = entry.IsLink()
ctx.Data["FileName"] = blob.Name()
- ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath
+ ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
buf := make([]byte, 1024)
n, _ := util.ReadAtMost(dataRc, buf)
@@ -422,7 +422,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
isTextFile = st.IsText()
fileSize = meta.Size
- ctx.Data["RawFileLink"] = fmt.Sprintf("%s/media/%s/%s", ctx.Repo.RepoLink, ctx.Repo.BranchNameSubURL(), ctx.Repo.TreePath)
+ ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/media/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
}
}
@@ -628,7 +628,7 @@ func checkHomeCodeViewable(ctx *context.Context) {
}
if firstUnit != nil {
- ctx.Redirect(fmt.Sprintf("%s/%s%s", setting.AppSubURL, ctx.Repo.Repository.FullName(), firstUnit.URI))
+ ctx.Redirect(fmt.Sprintf("%s%s", ctx.Repo.Repository.Link(), firstUnit.URI))
return
}
}
@@ -684,7 +684,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
return nil
}
- ctx.Data["LastCommitLoaderURL"] = ctx.Repo.RepoLink + "/lastcommit/" + ctx.Repo.CommitID + "/" + ctx.Repo.TreePath
+ ctx.Data["LastCommitLoaderURL"] = ctx.Repo.RepoLink + "/lastcommit/" + url.PathEscape(ctx.Repo.CommitID) + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
// Get current entry user currently looking at.
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
@@ -766,7 +766,7 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
treeLink := branchLink
if len(ctx.Repo.TreePath) > 0 {
- treeLink += "/" + ctx.Repo.TreePath
+ treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
ctx.Data["TreeLink"] = treeLink
@@ -815,7 +815,7 @@ func renderCode(ctx *context.Context) {
rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
if len(ctx.Repo.TreePath) > 0 {
- treeLink += "/" + ctx.Repo.TreePath
+ treeLink += "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
}
// Get Topics of this repo
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index f47f8d651d..4f6660926e 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -9,6 +9,7 @@ import (
"errors"
"fmt"
"net/http"
+ "net/url"
"path"
"strings"
@@ -414,7 +415,7 @@ func TelegramHooksNewPost(ctx *context.Context) {
w := &webhook.Webhook{
RepoID: orCtx.RepoID,
- URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID),
+ URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID)),
ContentType: webhook.ContentTypeJSON,
HookEvent: ParseHookEvent(form.WebhookForm),
IsActive: form.Active,
@@ -468,7 +469,7 @@ func MatrixHooksNewPost(ctx *context.Context) {
w := &webhook.Webhook{
RepoID: orCtx.RepoID,
- URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID),
+ URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
ContentType: webhook.ContentTypeJSON,
HTTPMethod: "PUT",
HookEvent: ParseHookEvent(form.WebhookForm),
@@ -976,7 +977,7 @@ func TelegramHooksEditPost(ctx *context.Context) {
return
}
w.Meta = string(meta)
- w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", form.BotToken, form.ChatID)
+ w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID))
w.HookEvent = ParseHookEvent(form.WebhookForm)
w.IsActive = form.Active
if err := w.UpdateEvent(); err != nil {
@@ -1020,7 +1021,7 @@ func MatrixHooksEditPost(ctx *context.Context) {
return
}
w.Meta = string(meta)
- w.URL = fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, form.RoomID)
+ w.URL = fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID))
w.HookEvent = ParseHookEvent(form.WebhookForm)
w.IsActive = form.Active
@@ -1162,7 +1163,7 @@ func TestWebhook(ctx *context.Context) {
apiCommit := &api.PayloadCommit{
ID: commit.ID.String(),
Message: commit.Message(),
- URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + commit.ID.String(),
+ URL: ctx.Repo.Repository.HTMLURL() + "/commit/" + url.PathEscape(commit.ID.String()),
Author: &api.PayloadUser{
Name: commit.Author.Name,
Email: commit.Author.Email,
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go
index 16927de2e9..82f56a8c4a 100644
--- a/routers/web/repo/wiki.go
+++ b/routers/web/repo/wiki.go
@@ -180,7 +180,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
ctx.Data["Pages"] = pages
// get requested pagename
- pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
+ pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
if len(pageName) == 0 {
pageName = "Home"
}
@@ -193,7 +193,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
//lookup filename in wiki - get filecontent, gitTree entry , real filename
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
if noEntry {
- ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
+ ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
}
if entry == nil || ctx.Written() {
if wikiRepo != nil {
@@ -276,7 +276,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
}
// get requested pagename
- pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
+ pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
if len(pageName) == 0 {
pageName = "Home"
}
@@ -291,7 +291,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
//lookup filename in wiki - get filecontent, gitTree entry , real filename
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
if noEntry {
- ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
+ ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
}
if entry == nil || ctx.Written() {
if wikiRepo != nil {
@@ -352,7 +352,7 @@ func renderEditPage(ctx *context.Context) {
}()
// get requested pagename
- pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
+ pageName := wiki_service.NormalizeWikiName(ctx.Params("*"))
if len(pageName) == 0 {
pageName = "Home"
}
@@ -365,7 +365,7 @@ func renderEditPage(ctx *context.Context) {
//lookup filename in wiki - get filecontent, gitTree entry , real filename
data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
if noEntry {
- ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
+ ctx.Redirect(ctx.Repo.RepoLink + "/wiki/?action=_pages")
}
if entry == nil || ctx.Written() {
return
@@ -378,6 +378,32 @@ func renderEditPage(ctx *context.Context) {
ctx.Data["footerContent"] = ""
}
+// WikiPost renders post of wiki page
+func WikiPost(ctx *context.Context) {
+ switch ctx.FormString("action") {
+ case "_new":
+ if !ctx.Repo.CanWrite(unit.TypeWiki) {
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ return
+ }
+ NewWikiPost(ctx)
+ return
+ case "_delete":
+ if !ctx.Repo.CanWrite(unit.TypeWiki) {
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ return
+ }
+ DeleteWikiPagePost(ctx)
+ return
+ }
+
+ if !ctx.Repo.CanWrite(unit.TypeWiki) {
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ return
+ }
+ EditWikiPost(ctx)
+}
+
// Wiki renders single wiki page
func Wiki(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true
@@ -389,6 +415,29 @@ func Wiki(ctx *context.Context) {
return
}
+ switch ctx.FormString("action") {
+ case "_pages":
+ WikiPages(ctx)
+ return
+ case "_revision":
+ WikiRevision(ctx)
+ return
+ case "_edit":
+ if !ctx.Repo.CanWrite(unit.TypeWiki) {
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ return
+ }
+ EditWiki(ctx)
+ return
+ case "_new":
+ if !ctx.Repo.CanWrite(unit.TypeWiki) {
+ ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
+ return
+ }
+ NewWiki(ctx)
+ return
+ }
+
wikiRepo, entry := renderViewPage(ctx)
defer func() {
if wikiRepo != nil {
@@ -652,7 +701,7 @@ func EditWikiPost(ctx *context.Context) {
return
}
- oldWikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
+ oldWikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
newWikiName := wiki_service.NormalizeWikiName(form.Title)
if len(form.Message) == 0 {
@@ -669,7 +718,7 @@ func EditWikiPost(ctx *context.Context) {
// DeleteWikiPagePost delete wiki page
func DeleteWikiPagePost(ctx *context.Context) {
- wikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
+ wikiName := wiki_service.NormalizeWikiName(ctx.Params("*"))
if len(wikiName) == 0 {
wikiName = "Home"
}
diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go
index cf49f19afe..87f2779c1a 100644
--- a/routers/web/repo/wiki_test.go
+++ b/routers/web/repo/wiki_test.go
@@ -76,8 +76,8 @@ func assertPagesMetas(t *testing.T, expectedNames []string, metas interface{}) {
func TestWiki(t *testing.T) {
unittest.PrepareTestEnv(t)
- ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
- ctx.SetParams(":page", "Home")
+ ctx := test.MockContext(t, "user2/repo1/wiki/?action=_pages")
+ ctx.SetParams("*", "Home")
test.LoadRepo(t, ctx, 1)
Wiki(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
@@ -88,7 +88,7 @@ func TestWiki(t *testing.T) {
func TestWikiPages(t *testing.T) {
unittest.PrepareTestEnv(t)
- ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
+ ctx := test.MockContext(t, "user2/repo1/wiki/?action=_pages")
test.LoadRepo(t, ctx, 1)
WikiPages(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
@@ -98,7 +98,7 @@ func TestWikiPages(t *testing.T) {
func TestNewWiki(t *testing.T) {
unittest.PrepareTestEnv(t)
- ctx := test.MockContext(t, "user2/repo1/wiki/_new")
+ ctx := test.MockContext(t, "user2/repo1/wiki/?action=_new")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
NewWiki(ctx)
@@ -113,7 +113,7 @@ func TestNewWikiPost(t *testing.T) {
} {
unittest.PrepareTestEnv(t)
- ctx := test.MockContext(t, "user2/repo1/wiki/_new")
+ ctx := test.MockContext(t, "user2/repo1/wiki/?action=_new")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
web.SetForm(ctx, &forms.NewWikiForm{
@@ -131,7 +131,7 @@ func TestNewWikiPost(t *testing.T) {
func TestNewWikiPost_ReservedName(t *testing.T) {
unittest.PrepareTestEnv(t)
- ctx := test.MockContext(t, "user2/repo1/wiki/_new")
+ ctx := test.MockContext(t, "user2/repo1/wiki/?action=_new")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
web.SetForm(ctx, &forms.NewWikiForm{
@@ -148,8 +148,8 @@ func TestNewWikiPost_ReservedName(t *testing.T) {
func TestEditWiki(t *testing.T) {
unittest.PrepareTestEnv(t)
- ctx := test.MockContext(t, "user2/repo1/wiki/_edit/Home")
- ctx.SetParams(":page", "Home")
+ ctx := test.MockContext(t, "user2/repo1/wiki/Home?action=_edit")
+ ctx.SetParams("*", "Home")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
EditWiki(ctx)
@@ -164,8 +164,8 @@ func TestEditWikiPost(t *testing.T) {
"New/<page>",
} {
unittest.PrepareTestEnv(t)
- ctx := test.MockContext(t, "user2/repo1/wiki/_new/Home")
- ctx.SetParams(":page", "Home")
+ ctx := test.MockContext(t, "user2/repo1/wiki/Home?action=_new")
+ ctx.SetParams("*", "Home")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
web.SetForm(ctx, &forms.NewWikiForm{
@@ -186,7 +186,7 @@ func TestEditWikiPost(t *testing.T) {
func TestDeleteWikiPagePost(t *testing.T) {
unittest.PrepareTestEnv(t)
- ctx := test.MockContext(t, "user2/repo1/wiki/Home/delete")
+ ctx := test.MockContext(t, "user2/repo1/wiki/Home?action=_delete")
test.LoadUser(t, ctx, 2)
test.LoadRepo(t, ctx, 1)
DeleteWikiPagePost(ctx)
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 1305b0095a..b9f5d044fa 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -887,5 +887,5 @@ func Email2User(ctx *context.Context) {
}
return
}
- ctx.Redirect(setting.AppSubURL + "/user/" + u.Name)
+ ctx.Redirect(u.HomeLink())
}
diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go
index 080ec4b582..08cd1b8b31 100644
--- a/routers/web/user/notification.go
+++ b/routers/web/user/notification.go
@@ -167,7 +167,7 @@ func NotificationStatusPost(c *context.Context) {
}
if !c.FormBool("noredirect") {
- url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, c.FormString("page"))
+ url := fmt.Sprintf("%s/notifications?page=%s", setting.AppSubURL, url.QueryEscape(c.FormString("page")))
c.Redirect(url, http.StatusSeeOther)
}
@@ -189,6 +189,5 @@ func NotificationPurgePost(c *context.Context) {
return
}
- url := fmt.Sprintf("%s/notifications", setting.AppSubURL)
- c.Redirect(url, http.StatusSeeOther)
+ c.Redirect(setting.AppSubURL+"/notifications", http.StatusSeeOther)
}
diff --git a/routers/web/user/oauth.go b/routers/web/user/oauth.go
index 642a7f33b0..3210d033d3 100644
--- a/routers/web/user/oauth.go
+++ b/routers/web/user/oauth.go
@@ -454,7 +454,7 @@ func AuthorizeOAuth(ctx *context.Context) {
ctx.Data["State"] = form.State
ctx.Data["Scope"] = form.Scope
ctx.Data["Nonce"] = form.Nonce
- ctx.Data["ApplicationUserLink"] = "<a href=\"" + html.EscapeString(setting.AppURL) + html.EscapeString(url.PathEscape(user.LowerName)) + "\">@" + html.EscapeString(user.Name) + "</a>"
+ ctx.Data["ApplicationUserLink"] = "<a href=\"" + html.EscapeString(user.HTMLURL()) + "\">@" + html.EscapeString(user.Name) + "</a>"
ctx.Data["ApplicationRedirectDomainHTML"] = "<strong>" + html.EscapeString(form.RedirectURI) + "</strong>"
// TODO document SESSION <=> FORM
err = ctx.Session.Set("client_id", app.ClientID)
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index f8fcbf6565..17c4783c69 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -364,6 +364,6 @@ func Action(ctx *context.Context) {
ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
return
}
-
+ // FIXME: We should check this URL and make sure that it's a valid Gitea URL
ctx.RedirectToFirst(ctx.FormString("redirect_to"), u.HomeLink())
}
diff --git a/routers/web/web.go b/routers/web/web.go
index 69f737c3e1..a20bf484b3 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -895,21 +895,23 @@ func RegisterRoutes(m *web.Route) {
}, reqRepoProjectsReader, repo.MustEnableProjects)
m.Group("/wiki", func() {
- m.Get("/", repo.Wiki)
- m.Get("/{page}", repo.Wiki)
- m.Get("/_pages", repo.WikiPages)
- m.Get("/{page}/_revision", repo.WikiRevision)
+ m.Combo("/").
+ Get(repo.Wiki).
+ Post(context.RepoMustNotBeArchived(),
+ reqSignIn,
+ reqRepoWikiWriter,
+ bindIgnErr(forms.NewWikiForm{}),
+ repo.WikiPost)
+ m.Combo("/*").
+ Get(repo.Wiki).
+ Post(context.RepoMustNotBeArchived(),
+ reqSignIn,
+ reqRepoWikiWriter,
+ bindIgnErr(forms.NewWikiForm{}),
+ repo.WikiPost)
m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Get("/commit/{sha:[a-f0-9]{7,40}}.{ext:patch|diff}", repo.RawDiff)
-
- m.Group("", func() {
- m.Combo("/_new").Get(repo.NewWiki).
- Post(bindIgnErr(forms.NewWikiForm{}), repo.NewWikiPost)
- m.Combo("/{page}/_edit").Get(repo.EditWiki).
- Post(bindIgnErr(forms.NewWikiForm{}), repo.EditWikiPost)
- m.Post("/{page}/delete", repo.DeleteWikiPagePost)
- }, context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter)
- }, repo.MustEnableWiki, context.RepoRef(), func(ctx *context.Context) {
+ }, repo.MustEnableWiki, func(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true
})