Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple Escaping Improvements #17551

Merged
merged 18 commits into from
Nov 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions integrations/issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func TestViewIssuesSortByType(t *testing.T) {
repo := unittest.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)

session := loginUser(t, user.Name)
req := NewRequest(t, "GET", repo.RelLink()+"/issues?type=created_by")
req := NewRequest(t, "GET", repo.Link()+"/issues?type=created_by")
resp := session.MakeRequest(t, req, http.StatusOK)

htmlDoc := NewHTMLParser(t, resp.Body)
Expand Down Expand Up @@ -97,7 +97,7 @@ func TestViewIssuesKeyword(t *testing.T) {
issues.UpdateIssueIndexer(issue)
time.Sleep(time.Second * 1)
const keyword = "first"
req := NewRequestf(t, "GET", "%s/issues?q=%s", repo.RelLink(), keyword)
req := NewRequestf(t, "GET", "%s/issues?q=%s", repo.Link(), keyword)
resp := MakeRequest(t, req, http.StatusOK)

htmlDoc := NewHTMLParser(t, resp.Body)
Expand Down
2 changes: 1 addition & 1 deletion integrations/links_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func testLinksAsUser(userName string, t *testing.T) {
"/releases",
"/releases/new",
//"/wiki/_pages",
"/wiki/_new",
"/wiki/?action=_new",
}

for _, repo := range apiRepos {
Expand Down
32 changes: 16 additions & 16 deletions integrations/nonascii_branches_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,17 @@ func TestNonasciiBranches(t *testing.T) {
},
{
from: "ГлавнаяВетка",
to: "branch/%d0%93%d0%bb%d0%b0%d0%b2%d0%bd%d0%b0%d1%8f%d0%92%d0%b5%d1%82%d0%ba%d0%b0",
to: "branch/%D0%93%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F%D0%92%D0%B5%D1%82%D0%BA%D0%B0",
status: http.StatusOK,
},
{
from: "а/б/в",
to: "branch/%d0%b0/%d0%b1/%d0%b2",
to: "branch/%D0%B0/%D0%B1/%D0%B2",
status: http.StatusOK,
},
{
from: "Grüßen/README.md",
to: "branch/Gr%c3%bc%c3%9fen/README.md",
to: "branch/Gr%C3%BC%C3%9Fen/README.md",
status: http.StatusOK,
},
{
Expand All @@ -83,7 +83,7 @@ func TestNonasciiBranches(t *testing.T) {
},
{
from: "Plus+Is+Not+Space/Файл.md",
to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
status: http.StatusOK,
},
{
Expand All @@ -93,28 +93,28 @@ func TestNonasciiBranches(t *testing.T) {
},
{
from: "ブランチ",
to: "branch/%e3%83%96%e3%83%a9%e3%83%b3%e3%83%81",
to: "branch/%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81",
status: http.StatusOK,
},
// Tags
{
from: "Тэг",
to: "tag/%d0%a2%d1%8d%d0%b3",
to: "tag/%D0%A2%D1%8D%D0%B3",
status: http.StatusOK,
},
{
from: "Ё/人",
to: "tag/%d0%81/%e4%ba%ba",
to: "tag/%D0%81/%E4%BA%BA",
status: http.StatusOK,
},
{
from: "タグ",
to: "tag/%e3%82%bf%e3%82%b0",
to: "tag/%E3%82%BF%E3%82%B0",
status: http.StatusOK,
},
{
from: "タグ/ファイル.md",
to: "tag/%e3%82%bf%e3%82%b0/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
to: "tag/%E3%82%BF%E3%82%B0/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
status: http.StatusOK,
},
// Files
Expand All @@ -125,38 +125,38 @@ func TestNonasciiBranches(t *testing.T) {
},
{
from: "Файл.md",
to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
status: http.StatusOK,
},
{
from: "ファイル.md",
to: "branch/Plus+Is+Not+Space/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
to: "branch/Plus+Is+Not+Space/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
status: http.StatusNotFound, // it's not on default branch
},
// Same but url-encoded (few tests)
{
from: "%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81",
to: "branch/%e3%83%96%e3%83%a9%e3%83%b3%e3%83%81",
to: "branch/%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81",
status: http.StatusOK,
},
{
from: "%E3%82%BF%E3%82%b0",
to: "tag/%e3%82%bf%e3%82%b0",
to: "tag/%E3%82%BF%E3%82%B0",
status: http.StatusOK,
},
{
from: "%D0%A4%D0%B0%D0%B9%D0%BB.md",
to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
status: http.StatusOK,
},
{
from: "%D0%81%2F%E4%BA%BA",
to: "tag/%d0%81/%e4%ba%ba",
to: "tag/%D0%81/%E4%BA%BA",
status: http.StatusOK,
},
{
from: "Ё%2F%E4%BA%BA",
to: "tag/%d0%81/%e4%ba%ba",
to: "tag/%D0%81/%E4%BA%BA",
status: http.StatusOK,
},
}
Expand Down
7 changes: 3 additions & 4 deletions models/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package models

import (
"fmt"
"net/url"
"path"
"strconv"
"strings"
Expand Down Expand Up @@ -185,10 +186,8 @@ func (a *Action) ShortRepoPath() string {

// GetRepoLink returns relative link to action repository.
func (a *Action) GetRepoLink() string {
if len(setting.AppSubURL) > 0 {
return path.Join(setting.AppSubURL, a.GetRepoPath())
}
return "/" + a.GetRepoPath()
// path.Join will skip empty strings
return path.Join(setting.AppSubURL, "/", url.PathEscape(a.GetRepoUserName()), url.PathEscape(a.GetRepoName()))
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
}

// GetRepositoryFromMatch returns a *Repository from a username and repo strings
Expand Down
3 changes: 2 additions & 1 deletion models/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package models
import (
"context"
"fmt"
"net/url"
"path"

"code.gitea.io/gitea/models/db"
Expand Down Expand Up @@ -59,7 +60,7 @@ func (a *Attachment) RelativePath() string {

// DownloadURL returns the download url of the attached file
func (a *Attachment) DownloadURL() string {
return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID)
return setting.AppURL + "attachments/" + url.PathEscape(a.UUID)
}

// LinkedRepository returns the linked repo if any
Expand Down
8 changes: 4 additions & 4 deletions models/avatars/avatar.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,15 @@ func GenerateUserAvatarFastLink(userName string, size int) string {
if size < 0 {
size = 0
}
return setting.AppSubURL + "/user/avatar/" + userName + "/" + strconv.Itoa(size)
return setting.AppSubURL + "/user/avatar/" + url.PathEscape(userName) + "/" + strconv.Itoa(size)
}

// GenerateUserAvatarImageLink returns a link for `User.Avatar` image file: "/avatars/${User.Avatar}"
func GenerateUserAvatarImageLink(userAvatar string, size int) string {
if size > 0 {
return setting.AppSubURL + "/avatars/" + userAvatar + "?size=" + strconv.Itoa(size)
return setting.AppSubURL + "/avatars/" + url.PathEscape(userAvatar) + "?size=" + strconv.Itoa(size)
}
return setting.AppSubURL + "/avatars/" + userAvatar
return setting.AppSubURL + "/avatars/" + url.PathEscape(userAvatar)
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
}

// generateRecognizedAvatarURL generate a recognized avatar (Gravatar/Libravatar) URL, it modifies the URL so the parameter is passed by a copy
Expand Down Expand Up @@ -155,7 +155,7 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
return generateRecognizedAvatarURL(*avatarURL, size)
}
// for non-final link, we should return fast (use a 302 redirection link)
urlStr := setting.AppSubURL + "/avatar/" + emailHash
urlStr := setting.AppSubURL + "/avatar/" + url.PathEscape(emailHash)
if size > 0 {
urlStr += "?size=" + strconv.Itoa(size)
}
Expand Down
4 changes: 2 additions & 2 deletions models/commit_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package models
import (
"crypto/sha1"
"fmt"
"net/url"
"strings"
"time"

Expand Down Expand Up @@ -137,8 +138,7 @@ func (status *CommitStatus) loadAttributes(e db.Engine) (err error) {
// APIURL returns the absolute APIURL to this commit-status.
func (status *CommitStatus) APIURL() string {
_ = status.loadAttributes(db.GetEngine(db.DefaultContext))
return fmt.Sprintf("%sapi/v1/repos/%s/statuses/%s",
setting.AppURL, status.Repo.FullName(), status.SHA)
return status.Repo.APIURL() + "/statuses/" + url.PathEscape(status.SHA)
}

// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
Expand Down
11 changes: 11 additions & 0 deletions models/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,17 @@ func (issue *Issue) HTMLURL() string {
return fmt.Sprintf("%s/%s/%d", issue.Repo.HTMLURL(), path, issue.Index)
}

// Link returns the Link URL to this issue.
func (issue *Issue) Link() string {
var path string
if issue.IsPull {
path = "pulls"
} else {
path = "issues"
}
return fmt.Sprintf("%s/%s/%d", issue.Repo.Link(), path, issue.Index)
}

// DiffURL returns the absolute URL to this diff
func (issue *Issue) DiffURL() string {
if issue.IsPull {
Expand Down
3 changes: 2 additions & 1 deletion models/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package models

import (
"fmt"
"net/url"
"strconv"

"code.gitea.io/gitea/models/db"
Expand Down Expand Up @@ -475,7 +476,7 @@ func (n *Notification) HTMLURL() string {
}
return n.Issue.HTMLURL()
case NotificationSourceCommit:
return n.Repository.HTMLURL() + "/commit/" + n.CommitID
return n.Repository.HTMLURL() + "/commit/" + url.PathEscape(n.CommitID)
case NotificationSourceRepository:
return n.Repository.HTMLURL()
}
Expand Down
11 changes: 5 additions & 6 deletions models/release.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
"errors"
"fmt"
"sort"
"strconv"
"strings"

"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
Expand Down Expand Up @@ -78,23 +78,22 @@ func (r *Release) LoadAttributes() error {

// APIURL the api url for a release. release must have attributes loaded
func (r *Release) APIURL() string {
return fmt.Sprintf("%sapi/v1/repos/%s/releases/%d",
setting.AppURL, r.Repo.FullName(), r.ID)
return r.Repo.APIURL() + "/releases/" + strconv.FormatInt(r.ID, 10)
}

// ZipURL the zip url for a release. release must have attributes loaded
func (r *Release) ZipURL() string {
return fmt.Sprintf("%s/archive/%s.zip", r.Repo.HTMLURL(), r.TagName)
return r.Repo.HTMLURL() + "/archive/" + util.PathEscapeSegments(r.TagName) + ".zip"
}

// TarURL the tar.gz url for a release. release must have attributes loaded
func (r *Release) TarURL() string {
return fmt.Sprintf("%s/archive/%s.tar.gz", r.Repo.HTMLURL(), r.TagName)
return r.Repo.HTMLURL() + "/archive/" + util.PathEscapeSegments(r.TagName) + ".tar.gz"
}

// HTMLURL the url for a release on the web UI. release must have attributes loaded
func (r *Release) HTMLURL() string {
return fmt.Sprintf("%s/releases/tag/%s", r.Repo.HTMLURL(), r.TagName)
return r.Repo.HTMLURL() + "/releases/tag/" + util.PathEscapeSegments(r.TagName)
}

// IsReleaseExist returns true if release with given tag name already exists.
Expand Down
21 changes: 8 additions & 13 deletions models/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ func (repo *Repository) FullName() string {

// HTMLURL returns the repository HTML URL
func (repo *Repository) HTMLURL() string {
return setting.AppURL + repo.FullName()
return setting.AppURL + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
}

// CommitLink make link to by commit full ID
Expand All @@ -323,14 +323,14 @@ func (repo *Repository) CommitLink(commitID string) (result string) {
if commitID == "" || commitID == "0000000000000000000000000000000000000000" {
result = ""
} else {
result = repo.HTMLURL() + "/commit/" + commitID
result = repo.HTMLURL() + "/commit/" + url.PathEscape(commitID)
}
return
}

// APIURL returns the repository API URL
func (repo *Repository) APIURL() string {
return setting.AppURL + "api/v1/repos/" + repo.FullName()
return setting.AppURL + "api/v1/repos/" + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
}

// GetCommitsCountCacheKey returns cache key used for commits count caching.
Expand Down Expand Up @@ -709,19 +709,14 @@ func (repo *Repository) GitConfigPath() string {
return GitConfigPath(repo.RepoPath())
}

// RelLink returns the repository relative link
func (repo *Repository) RelLink() string {
return "/" + repo.FullName()
}

// Link returns the repository link
func (repo *Repository) Link() string {
return setting.AppSubURL + "/" + repo.FullName()
return setting.AppSubURL + "/" + url.PathEscape(repo.OwnerName) + "/" + url.PathEscape(repo.Name)
}

// ComposeCompareURL returns the repository comparison URL
func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) string {
return fmt.Sprintf("%s/compare/%s...%s", repo.FullName(), oldCommitID, newCommitID)
return fmt.Sprintf("%s/%s/compare/%s...%s", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name), util.PathEscapeSegments(oldCommitID), util.PathEscapeSegments(newCommitID))
zeripath marked this conversation as resolved.
Show resolved Hide resolved
}

// UpdateDefaultBranch updates the default branch
Expand Down Expand Up @@ -930,11 +925,11 @@ func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
}

if setting.SSH.Port != 22 {
cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, net.JoinHostPort(setting.SSH.Domain, strconv.Itoa(setting.SSH.Port)), repo.OwnerName, repoName)
cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, net.JoinHostPort(setting.SSH.Domain, strconv.Itoa(setting.SSH.Port)), url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
} else if setting.Repository.UseCompatSSHURI {
cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshDomain, repo.OwnerName, repoName)
cl.SSH = fmt.Sprintf("ssh://%s@%s/%s/%s.git", sshUser, sshDomain, url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
} else {
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshDomain, repo.OwnerName, repoName)
cl.SSH = fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshDomain, url.PathEscape(repo.OwnerName), url.PathEscape(repoName))
}
cl.HTTPS = ComposeHTTPSCloneURL(repo.OwnerName, repoName)
return cl
Expand Down
3 changes: 2 additions & 1 deletion models/repo_avatar.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"image/png"
"io"
"net/url"
"strconv"
"strings"

Expand Down Expand Up @@ -96,7 +97,7 @@ func (repo *Repository) relAvatarLink(e db.Engine) string {
return ""
}
}
return setting.AppSubURL + "/repo-avatars/" + repo.Avatar
return setting.AppSubURL + "/repo-avatars/" + url.PathEscape(repo.Avatar)
wxiaoguang marked this conversation as resolved.
Show resolved Hide resolved
}

// AvatarLink returns a link to the repository's avatar.
Expand Down
Loading