-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve URL validation for external wiki and external issues (#4710)
* Improve URL validation for external wiki and external issues * Do not allow also localhost address for external URLs
- Loading branch information
Showing
5 changed files
with
180 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright 2018 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package validation | ||
|
||
import ( | ||
"net" | ||
"net/url" | ||
"strings" | ||
|
||
"code.gitea.io/gitea/modules/setting" | ||
) | ||
|
||
var loopbackIPBlocks []*net.IPNet | ||
|
||
func init() { | ||
for _, cidr := range []string{ | ||
"127.0.0.0/8", // IPv4 loopback | ||
"::1/128", // IPv6 loopback | ||
} { | ||
if _, block, err := net.ParseCIDR(cidr); err == nil { | ||
loopbackIPBlocks = append(loopbackIPBlocks, block) | ||
} | ||
} | ||
} | ||
|
||
func isLoopbackIP(ip string) bool { | ||
pip := net.ParseIP(ip) | ||
if pip == nil { | ||
return false | ||
} | ||
for _, block := range loopbackIPBlocks { | ||
if block.Contains(pip) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
// IsValidURL checks if URL is valid | ||
func IsValidURL(uri string) bool { | ||
if u, err := url.ParseRequestURI(uri); err != nil || | ||
(u.Scheme != "http" && u.Scheme != "https") || | ||
!validPort(portOnly(u.Host)) { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
// IsAPIURL checks if URL is current Gitea instance API URL | ||
func IsAPIURL(uri string) bool { | ||
return strings.HasPrefix(strings.ToLower(uri), strings.ToLower(setting.AppURL+"api")) | ||
} | ||
|
||
// IsValidExternalURL checks if URL is valid external URL | ||
func IsValidExternalURL(uri string) bool { | ||
if !IsValidURL(uri) || IsAPIURL(uri) { | ||
return false | ||
} | ||
|
||
u, err := url.ParseRequestURI(uri) | ||
if err != nil { | ||
return false | ||
} | ||
|
||
// Currently check only if not loopback IP is provided to keep compatibility | ||
if isLoopbackIP(u.Hostname()) || strings.ToLower(u.Hostname()) == "localhost" { | ||
return false | ||
} | ||
|
||
// TODO: Later it should be added to allow local network IP addreses | ||
// only if allowed by special setting | ||
|
||
return true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// Copyright 2018 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package validation | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"code.gitea.io/gitea/modules/setting" | ||
) | ||
|
||
func Test_IsValidURL(t *testing.T) { | ||
cases := []struct { | ||
description string | ||
url string | ||
valid bool | ||
}{ | ||
{ | ||
description: "Empty URL", | ||
url: "", | ||
valid: false, | ||
}, | ||
{ | ||
description: "Loobpack IPv4 URL", | ||
url: "http://127.0.1.1:5678/", | ||
valid: true, | ||
}, | ||
{ | ||
description: "Loobpack IPv6 URL", | ||
url: "https://[::1]/", | ||
valid: true, | ||
}, | ||
{ | ||
description: "Missing semicolon after schema", | ||
url: "http//meh/", | ||
valid: false, | ||
}, | ||
} | ||
|
||
for _, testCase := range cases { | ||
t.Run(testCase.description, func(t *testing.T) { | ||
assert.Equal(t, testCase.valid, IsValidURL(testCase.url)) | ||
}) | ||
} | ||
} | ||
|
||
func Test_IsValidExternalURL(t *testing.T) { | ||
setting.AppURL = "https://try.gitea.io/" | ||
|
||
cases := []struct { | ||
description string | ||
url string | ||
valid bool | ||
}{ | ||
{ | ||
description: "Current instance URL", | ||
url: "https://try.gitea.io/test", | ||
valid: true, | ||
}, | ||
{ | ||
description: "Loobpack IPv4 URL", | ||
url: "http://127.0.1.1:5678/", | ||
valid: false, | ||
}, | ||
{ | ||
description: "Current instance API URL", | ||
url: "https://try.gitea.io/api/v1/user/follow", | ||
valid: false, | ||
}, | ||
{ | ||
description: "Local network URL", | ||
url: "http://192.168.1.2/api/v1/user/follow", | ||
valid: true, | ||
}, | ||
{ | ||
description: "Local URL", | ||
url: "http://LOCALHOST:1234/whatever", | ||
valid: false, | ||
}, | ||
} | ||
|
||
for _, testCase := range cases { | ||
t.Run(testCase.description, func(t *testing.T) { | ||
assert.Equal(t, testCase.valid, IsValidExternalURL(testCase.url)) | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters