fix(urllib): fix rejection of valid proxy URLs in feeds

When attempting to set a proxy in a feed, due to a regression caused by 4cd9dd6af7
the form validation would now only accept HTTP(S) proxies, and reject SOCKS proxies, despite this being
a valid configuration before. Adding a new function to validate proxy URLs separates the concerns
and allows using a SOCKS proxy url again.
This commit is contained in:
CULT PONY
2026-06-15 09:57:13 +02:00
committed by fguillot
parent b0d25c1c68
commit 530b0c5739
5 changed files with 49 additions and 4 deletions
+1 -1
View File
@@ -57,7 +57,7 @@ func (s *SubscriptionForm) Validate() *locale.LocalizedError {
return locale.NewLocalizedError("error.feed_invalid_urlrewrite_rule")
}
if s.ProxyURL != "" && !urllib.IsAbsoluteURL(s.ProxyURL) {
if s.ProxyURL != "" && !urllib.IsValidProxyURL(s.ProxyURL) {
return locale.NewLocalizedError("error.invalid_feed_proxy_url")
}
+17
View File
@@ -43,6 +43,11 @@ func hasHTTPPrefix(inputURL string) bool {
return strings.HasPrefix(inputURL, "https://") || strings.HasPrefix(inputURL, "http://")
}
// hasSOCKSPrefix reports whether the URL string begins with an SOCKS5 or SOCKS5H scheme.
func hasSOCKSPrefix(inputURL string) bool {
return strings.HasPrefix(inputURL, "socks5://") || strings.HasPrefix(inputURL, "socks5h://")
}
// IsAbsoluteURL reports whether the link is absolute and starts with an HTTP or HTTPS scheme.
func IsAbsoluteURL(inputURL string) bool {
if !hasHTTPPrefix(inputURL) {
@@ -55,6 +60,18 @@ func IsAbsoluteURL(inputURL string) bool {
return parsedURL.IsAbs()
}
// IsValidProxyURL reports whether the url is absolute, has a host and starts with an HTTP, HTTPS, SOCKS5 or SOCKS5H scheme.
func IsValidProxyURL(inputURL string) bool {
if !hasHTTPPrefix(inputURL) && !hasSOCKSPrefix(inputURL) {
return false
}
parsedURL, err := url.Parse(inputURL)
if err != nil {
return false
}
return parsedURL.IsAbs() && parsedURL.Host != ""
}
// resolveToAbsoluteURL resolves a relative URL using a base URL, parsing the base only if needed.
func resolveToAbsoluteURL(parsedBaseURL *url.URL, baseURL, relativeURL string) (string, error) {
// Avoid parsing the relative URL if it's already absolute
+28
View File
@@ -68,6 +68,34 @@ func TestIsAbsoluteURL(t *testing.T) {
}
}
func TestIsValidProxyURL(t *testing.T) {
scenarios := map[string]bool{
"http://127.0.0.1:3128": true,
"http://[::1]:1055": true,
"https://proxy.example.org": true,
"socks5://127.0.0.1:1080": true,
"socks5h://127.0.0.1:1080": true,
"socks5://[::1]:1055": true,
"socks5h://[::1]:1055": true,
"socks5://[::1%25eno1]:1055": true,
"ftp://host": false,
"/relative/path": false,
"invalid url": false,
"http://[::1": false,
"http:///var/run/socket": false,
"sock:///socket.file": false,
"socks5://[::1%eno1]:1055": false,
"": false,
}
for input, expected := range scenarios {
actual := IsValidProxyURL(input)
if actual != expected {
t.Errorf(`Unexpected result, got %v instead of %v for %q`, actual, expected, input)
}
}
}
func TestAbsoluteURL(t *testing.T) {
type absoluteScenario struct {
name string
+2 -2
View File
@@ -48,7 +48,7 @@ func ValidateFeedCreation(store *storage.Storage, userID int64, request *model.F
}
}
if request.ProxyURL != "" && !urllib.IsAbsoluteURL(request.ProxyURL) {
if request.ProxyURL != "" && !urllib.IsValidProxyURL(request.ProxyURL) {
return locale.NewLocalizedError("error.invalid_feed_proxy_url")
}
@@ -122,7 +122,7 @@ func ValidateFeedModification(store *storage.Storage, userID, feedID int64, requ
return locale.NewLocalizedError("error.proxy_url_not_empty")
}
if !urllib.IsAbsoluteURL(*request.ProxyURL) {
if !urllib.IsValidProxyURL(*request.ProxyURL) {
return locale.NewLocalizedError("error.invalid_feed_proxy_url")
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ func ValidateSubscriptionDiscovery(request *model.SubscriptionDiscoveryRequest)
return locale.NewLocalizedError("error.invalid_site_url")
}
if request.ProxyURL != "" && !urllib.IsAbsoluteURL(request.ProxyURL) {
if request.ProxyURL != "" && !urllib.IsValidProxyURL(request.ProxyURL) {
return locale.NewLocalizedError("error.invalid_proxy_url")
}