Skip to content
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
19 changes: 19 additions & 0 deletions modules/validation/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ package validation
import (
"net"
"net/url"
"regexp"
"strings"

"code.gitea.io/gitea/modules/setting"
)

var loopbackIPBlocks []*net.IPNet

var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`)

func init() {
for _, cidr := range []string{
"127.0.0.0/8", // IPv4 loopback
Expand Down Expand Up @@ -75,3 +78,19 @@ func IsValidExternalURL(uri string) bool {

return true
}

// IsValidExternalTrackerURLFormat checks if URL matches required syntax for external trackers
func IsValidExternalTrackerURLFormat(uri string) bool {
if !IsValidExternalURL(uri) {
return false
}

// check for typoed variables like /{index/ or /[repo}
for _, match := range externalTrackerRegex.FindAllStringSubmatch(uri, -1) {
if (match[1] == "{" || match[2] == "}") && (match[1] != "{" || match[2] != "}") {
return false
}
}

return true
}
67 changes: 67 additions & 0 deletions modules/validation/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,70 @@ func Test_IsValidExternalURL(t *testing.T) {
})
}
}

func Test_IsValidExternalTrackerURLFormat(t *testing.T) {
setting.AppURL = "https://try.gitea.io/"

cases := []struct {
description string
url string
valid bool
}{
{
description: "Correct external tracker URL with all placeholders",
url: "https://github.com/{user}/{repo}/issues/{index}",
valid: true,
},
{
description: "Local external tracker URL with all placeholders",
url: "https://127.0.0.1/{user}/{repo}/issues/{index}",
valid: false,
},
{
description: "External tracker URL with typo placeholder",
url: "https://github.com/{user}/{repo/issues/{index}",
valid: false,
},
{
description: "External tracker URL with typo placeholder",
url: "https://github.com/[user}/{repo/issues/{index}",
valid: false,
},
{
description: "External tracker URL with typo placeholder",
url: "https://github.com/{user}/repo}/issues/{index}",
valid: false,
},
{
description: "External tracker URL missing optional placeholder",
url: "https://github.com/{user}/issues/{index}",
valid: true,
},
{
description: "External tracker URL missing optional placeholder",
url: "https://github.com/{repo}/issues/{index}",
valid: true,
},
{
description: "External tracker URL missing optional placeholder",
url: "https://github.com/issues/{index}",
valid: true,
},
{
description: "External tracker URL missing optional placeholder",
url: "https://github.com/issues/{user}",
valid: true,
},
{
description: "External tracker URL with similar placeholder names test",
url: "https://github.com/user/repo/issues/{index}",
valid: true,
},
}

for _, testCase := range cases {
t.Run(testCase.description, func(t *testing.T) {
assert.Equal(t, testCase.valid, IsValidExternalTrackerURLFormat(testCase.url))
})
}
}
2 changes: 1 addition & 1 deletion routers/repo/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
ctx.Redirect(repo.Link() + "/settings")
return
}
if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalURL(form.TrackerURLFormat) {
if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) {
ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error"))
ctx.Redirect(repo.Link() + "/settings")
return
Expand Down