Volume XXII, number 280Wednesday, October 7, 2026Latest message 1 hour ago

The Git List

News and archive of git@vger.kernel.org, since April 2005

patchhttp: reject unsupported proxy URL schemes

5 messages between May 1, 2026 and May 5, 2026, from aminnimaj@gmail.com, Junio C Hamano.

Plain Markdown or JSON for tools and agents. Diffs are folded; open one to read it.

aminnimaj@gmail.comMay 1, 2026, 19:04 UTC on lore
From: Aliwoto <aminnimaj@gmail.com>

An explicit proxy URL with an unsupported scheme such as htpp://127.0.0.1 is currently accepted and treated as an HTTP proxy.

This happens because Git parses the URL, extracts the host part, and passes only that host to libcurl without rejecting the unsupported scheme. As a result, the typo is silently accepted.

This patch rejects explicit unsupported proxy schemes while keeping the existing host:port-without-scheme behavior unchanged, and adds a regression test for the unsupported-scheme case.

Aliwoto (1):
  http: reject unsupported proxy URL schemes
 http.c                | 79 +++++++++++++++++++++++++++++--------------
 t/t5564-http-proxy.sh |  5 +++
 2 files changed, 59 insertions(+), 25 deletions(-)
base-commit: 67ad42147a7acc2af6074753ebd03d904476118f
-- 
2.49.0.windows.1
aminnimaj@gmail.comMay 1, 2026, 19:04 UTC in reply to aminnimaj@gmail.com on lore

[PATCH 1/1] http: reject unsupported proxy URL schemes

From: Aliwoto <aminnimaj@gmail.com>

An explicit proxy URL with an unrecognized scheme such as htpp://127.0.0.1 is currently accepted.

Git parses the URL, extracts the host part, and then passes only that host to libcurl. Because no proxy type is selected for the unknown scheme, Git leaves libcurl at its default HTTP proxy type, so the typo is silently treated as an HTTP proxy.

Reject proxy URLs with explicit unsupported schemes instead of silently accepting them. Keep the existing host:port-without-scheme behavior unchanged.

Add a regression test to cover the unsupported-scheme case.
Signed-off-by: Aliwoto <aminnimaj@gmail.com>
---
 http.c                | 79 +++++++++++++++++++++++++++++--------------
 t/t5564-http-proxy.sh |  5 +++
 2 files changed, 59 insertions(+), 25 deletions(-)
Show changes to 2 files +59 −25

http.c, t/t5564-http-proxy.sh

diff --git a/http.c b/http.c
index 7815f144de..0628dc5aab 100644
--- a/http.c
+++ b/http.c
@@ -722,6 +722,55 @@ static int has_proxy_cert_password(void)
 	return 1;
 }
 
+static int is_socks_proxy_protocol(const char *protocol)
+{
+	return protocol &&
+		(!strcmp(protocol, "socks") ||
+		 !strcmp(protocol, "socks4") ||
+		 !strcmp(protocol, "socks4a") ||
+		 !strcmp(protocol, "socks5") ||
+		 !strcmp(protocol, "socks5h"));
+}
+
+static int set_curl_proxy_type(CURL *result, const char *protocol)
+{
+	if (!protocol || !strcmp(protocol, "http"))
+		return 0;
+
+	if (!strcmp(protocol, "socks5h"))
+		curl_easy_setopt(result, CURLOPT_PROXYTYPE,
+				 (long)CURLPROXY_SOCKS5_HOSTNAME);
+	else if (!strcmp(protocol, "socks5"))
+		curl_easy_setopt(result, CURLOPT_PROXYTYPE,
+				 (long)CURLPROXY_SOCKS5);
+	else if (!strcmp(protocol, "socks4a"))
+		curl_easy_setopt(result, CURLOPT_PROXYTYPE,
+				 (long)CURLPROXY_SOCKS4A);
+	else if (!strcmp(protocol, "socks") ||
+		 !strcmp(protocol, "socks4"))
+		curl_easy_setopt(result, CURLOPT_PROXYTYPE,
+				 (long)CURLPROXY_SOCKS4);
+	else if (!strcmp(protocol, "https")) {
+		curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS);
+
+		if (http_proxy_ssl_cert)
+			curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT,
+					 http_proxy_ssl_cert);
+
+		if (http_proxy_ssl_key)
+			curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY,
+					 http_proxy_ssl_key);
+
+		if (has_proxy_cert_password())
+			curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD,
+					 proxy_cert_auth.password);
+	} else {
+		return -1;
+	}
+
+	return 0;
+}
+
 /* Return 1 if redactions have been made, 0 otherwise. */
 static int redact_sensitive_header(struct strbuf *header, size_t offset)
 {
@@ -1192,30 +1241,6 @@ static CURL *get_curl_handle(void)
 	} else if (curl_http_proxy) {
 		struct strbuf proxy = STRBUF_INIT;
 
-		if (starts_with(curl_http_proxy, "socks5h"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5_HOSTNAME);
-		else if (starts_with(curl_http_proxy, "socks5"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5);
-		else if (starts_with(curl_http_proxy, "socks4a"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4A);
-		else if (starts_with(curl_http_proxy, "socks"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4);
-		else if (starts_with(curl_http_proxy, "https")) {
-			curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS);
-
-			if (http_proxy_ssl_cert)
-				curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);
-
-			if (http_proxy_ssl_key)
-				curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);
-
-			if (has_proxy_cert_password())
-				curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);
-		}
 		if (strstr(curl_http_proxy, "://"))
 			credential_from_url(&proxy_auth, curl_http_proxy);
 		else {
@@ -1225,6 +1250,10 @@ static CURL *get_curl_handle(void)
 			strbuf_release(&url);
 		}
 
+		if (set_curl_proxy_type(result, proxy_auth.protocol) < 0)
+			die("Invalid proxy URL '%s': unsupported proxy scheme '%s'",
+			    curl_http_proxy, proxy_auth.protocol);
+
 		if (!proxy_auth.host)
 			die("Invalid proxy URL '%s'", curl_http_proxy);
 
@@ -1235,7 +1264,7 @@ static CURL *get_curl_handle(void)
 			if (ver->version_num < 0x075400)
 				die("libcurl 7.84 or later is required to support paths in proxy URLs");
 
-			if (!starts_with(proxy_auth.protocol, "socks"))
+			if (!is_socks_proxy_protocol(proxy_auth.protocol))
 				die("Invalid proxy URL '%s': only SOCKS proxies support paths",
 				    curl_http_proxy);
 
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
index 3bcbdef409..db69aa2295 100755
--- a/t/t5564-http-proxy.sh
+++ b/t/t5564-http-proxy.sh
@@ -95,4 +95,9 @@ test_expect_success 'Unix socket requires localhost' - <<\EOT
 	}
 EOT
 
+test_expect_success 'unknown proxy scheme is rejected' '
+	! git clone -c http.proxy=htpp://127.0.0.1 https://example.com/repo.git 2>err &&
+	grep -Fx "fatal: Invalid proxy URL '\''htpp://127.0.0.1'\'': unsupported proxy scheme '\''htpp'\''" err
+'
+
 test_done
-- 
2.49.0.windows.1
Junio C HamanoMay 3, 2026, 22:19 UTC in reply to aminnimaj@gmail.com on lore

Re: [PATCH 1/1] http: reject unsupported proxy URL schemes

aminnimaj@gmail.com writes:
Show 48 quoted lines
> +static int is_socks_proxy_protocol(const char *protocol)
> +{
> +	return protocol &&
> +		(!strcmp(protocol, "socks") ||
> +		 !strcmp(protocol, "socks4") ||
> +		 !strcmp(protocol, "socks4a") ||
> +		 !strcmp(protocol, "socks5") ||
> +		 !strcmp(protocol, "socks5h"));
> +}
> +
> +static int set_curl_proxy_type(CURL *result, const char *protocol)
> +{
> +	if (!protocol || !strcmp(protocol, "http"))
> +		return 0;
> +
> +	if (!strcmp(protocol, "socks5h"))
> +		curl_easy_setopt(result, CURLOPT_PROXYTYPE,
> +				 (long)CURLPROXY_SOCKS5_HOSTNAME);
> +	else if (!strcmp(protocol, "socks5"))
> +		curl_easy_setopt(result, CURLOPT_PROXYTYPE,
> +				 (long)CURLPROXY_SOCKS5);
> +	else if (!strcmp(protocol, "socks4a"))
> +		curl_easy_setopt(result, CURLOPT_PROXYTYPE,
> +				 (long)CURLPROXY_SOCKS4A);
> +	else if (!strcmp(protocol, "socks") ||
> +		 !strcmp(protocol, "socks4"))
> +		curl_easy_setopt(result, CURLOPT_PROXYTYPE,
> +				 (long)CURLPROXY_SOCKS4);
> +	else if (!strcmp(protocol, "https")) {
> +		curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS);
> +
> +		if (http_proxy_ssl_cert)
> +			curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT,
> +					 http_proxy_ssl_cert);
> +
> +		if (http_proxy_ssl_key)
> +			curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY,
> +					 http_proxy_ssl_key);
> +
> +		if (has_proxy_cert_password())
> +			curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD,
> +					 proxy_cert_auth.password);
> +	} else {
> +		return -1;
> +	}
> +
> +	return 0;
> +}
Can these two be rewritten to be more table driven?  I.e.,
static struct socks_proxy_type {
	const char *name;
	long curlsym;
} socks_proxy_type[] = {
	{ "socks", CURLPROXY_SOCKS4_HOSTNAME },
	...
	{ "socks5h", CURLPROXY_SOCKS5_HOSTNAME },
};
static bool is_socks_proxy_protocol(const char *protocol)
{
	if (!protocol)
		return false;
	for (int i = 0; i < ARRAY_SIZE(socks_proxy_type); i++)
        	if (!strcmp(socks_proxy_type[i].name, protocol))
                	return true;
	return false;
}
static int set_curl_proxy_type(...)
{
	for (int i = 0; i < ARRAY_SIZE(socks_proxy_type); i++) {
        	if (!strcmp(socks_proxy_type[i].name, protocol)) {
			curl_easy_setopt(result, CURLOPT_PROXYTYPE, 
					socks_proxy_type[i].curlsym);
			return 0;
		}
	}
	/* otherwise ... */
        if (!strcmp(protocol, "https")) {
		...
	}
}
Show 10 quoted lines
> diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
> index 3bcbdef409..db69aa2295 100755
> --- a/t/t5564-http-proxy.sh
> +++ b/t/t5564-http-proxy.sh
> @@ -95,4 +95,9 @@ test_expect_success 'Unix socket requires localhost' - <<\EOT
>  	}
>  EOT
>  
> +test_expect_success 'unknown proxy scheme is rejected' '
> +	! git clone -c http.proxy=htpp://127.0.0.1 https://example.com/repo.git 2>err &&

Use test_must_fail to tell between uncontrolled failures like crashes and controlled die()s.

> +	grep -Fx "fatal: Invalid proxy URL '\''htpp://127.0.0.1'\'': unsupported proxy scheme '\''htpp'\''" err
> +'

Avoid insisting the exact match with such a long line and stick to the essential part, like "unsupported proxy scheme '...'".

Also use test_grep for better debuggability.
aminnimaj@gmail.comMay 5, 2026, 09:19 UTC in reply to aminnimaj@gmail.com on lore

[PATCH v2 0/1] http: reject unsupported proxy URL schemes

From: Aliwoto <aminnimaj@gmail.com>

An explicit proxy URL with an unsupported scheme such as htpp://127.0.0.1 is currently accepted and treated as an HTTP proxy.

This happens because Git parses the URL, extracts the host part, and passes only that host to libcurl without rejecting the unsupported scheme. As a result, the typo is silently accepted.

This patch rejects explicit unsupported proxy schemes while keeping the existing host:port-without-scheme behavior unchanged, and adds a regression test for the unsupported-scheme case.

---
Changes in v2:
- make SOCKS proxy type handling table-driven
- use test_must_fail in the regression test
- use test_grep on the essential error text
Aliwoto (1):
  http: reject unsupported proxy URL schemes
 http.c                | 93 +++++++++++++++++++++++++++++++------------
 t/t5564-http-proxy.sh |  6 +++
 2 files changed, 74 insertions(+), 25 deletions(-)
base-commit: 67ad42147a7acc2af6074753ebd03d904476118f
-- 
2.49.0.windows.1
aminnimaj@gmail.comMay 5, 2026, 09:19 UTC in reply to aminnimaj@gmail.com on lore

[PATCH v2 1/1] http: reject unsupported proxy URL schemes

From: Aliwoto <aminnimaj@gmail.com>

An explicit proxy URL with an unrecognized scheme such as htpp://127.0.0.1 is currently accepted.

Git parses the URL, extracts the host part, and then passes only that host to libcurl. Because no proxy type is selected for the unknown scheme, Git leaves libcurl at its default HTTP proxy type, so the typo is silently treated as an HTTP proxy.

Reject proxy URLs with explicit unsupported schemes instead of silently accepting them. Keep the existing host:port-without-scheme behavior unchanged.

Implement the SOCKS proxy handling with a shared table-driven mapping.
Add a regression test to cover the unsupported-scheme case.
Signed-off-by: Aliwoto <aminnimaj@gmail.com>
---
 http.c                | 93 +++++++++++++++++++++++++++++++------------
 t/t5564-http-proxy.sh |  6 +++
 2 files changed, 74 insertions(+), 25 deletions(-)
Show changes to 2 files +74 −25

http.c, t/t5564-http-proxy.sh

diff --git a/http.c b/http.c
index 7815f144de..b945267c9c 100644
--- a/http.c
+++ b/http.c
@@ -722,6 +722,69 @@ static int has_proxy_cert_password(void)
 	return 1;
 }
 
+static const struct socks_proxy_type {
+	const char *name;
+	long curlsym;
+} socks_proxy_types[] = {
+	{ "socks", CURLPROXY_SOCKS4 },
+	{ "socks4", CURLPROXY_SOCKS4 },
+	{ "socks4a", CURLPROXY_SOCKS4A },
+	{ "socks5", CURLPROXY_SOCKS5 },
+	{ "socks5h", CURLPROXY_SOCKS5_HOSTNAME },
+};
+
+static const struct socks_proxy_type *find_socks_proxy_type(const char *protocol)
+{
+	int i;
+
+	if (!protocol)
+		return NULL;
+
+	for (i = 0; i < ARRAY_SIZE(socks_proxy_types); i++) {
+		if (!strcmp(socks_proxy_types[i].name, protocol))
+			return &socks_proxy_types[i];
+	}
+
+	return NULL;
+}
+
+static int is_socks_proxy_protocol(const char *protocol)
+{
+	return !!find_socks_proxy_type(protocol);
+}
+
+static int set_curl_proxy_type(CURL *result, const char *protocol)
+{
+	const struct socks_proxy_type *socks_proxy_type;
+
+	if (!protocol || !strcmp(protocol, "http"))
+		return 0;
+
+	socks_proxy_type = find_socks_proxy_type(protocol);
+	if (socks_proxy_type) {
+		curl_easy_setopt(result, CURLOPT_PROXYTYPE, socks_proxy_type->curlsym);
+		return 0;
+	}
+
+	if (!strcmp(protocol, "https")) {
+		curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS);
+
+		if (http_proxy_ssl_cert)
+			curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT,
+					 http_proxy_ssl_cert);
+
+		if (http_proxy_ssl_key)
+			curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY,
+					 http_proxy_ssl_key);
+
+		if (has_proxy_cert_password())
+			curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD,
+					 proxy_cert_auth.password);
+	}
+
+	return -1;
+}
+
 /* Return 1 if redactions have been made, 0 otherwise. */
 static int redact_sensitive_header(struct strbuf *header, size_t offset)
 {
@@ -1192,30 +1255,6 @@ static CURL *get_curl_handle(void)
 	} else if (curl_http_proxy) {
 		struct strbuf proxy = STRBUF_INIT;
 
-		if (starts_with(curl_http_proxy, "socks5h"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5_HOSTNAME);
-		else if (starts_with(curl_http_proxy, "socks5"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5);
-		else if (starts_with(curl_http_proxy, "socks4a"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4A);
-		else if (starts_with(curl_http_proxy, "socks"))
-			curl_easy_setopt(result,
-				CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4);
-		else if (starts_with(curl_http_proxy, "https")) {
-			curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS);
-
-			if (http_proxy_ssl_cert)
-				curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);
-
-			if (http_proxy_ssl_key)
-				curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);
-
-			if (has_proxy_cert_password())
-				curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);
-		}
 		if (strstr(curl_http_proxy, "://"))
 			credential_from_url(&proxy_auth, curl_http_proxy);
 		else {
@@ -1225,6 +1264,10 @@ static CURL *get_curl_handle(void)
 			strbuf_release(&url);
 		}
 
+		if (set_curl_proxy_type(result, proxy_auth.protocol) < 0)
+			die("Invalid proxy URL '%s': unsupported proxy scheme '%s'",
+			    curl_http_proxy, proxy_auth.protocol);
+
 		if (!proxy_auth.host)
 			die("Invalid proxy URL '%s'", curl_http_proxy);
 
@@ -1235,7 +1278,7 @@ static CURL *get_curl_handle(void)
 			if (ver->version_num < 0x075400)
 				die("libcurl 7.84 or later is required to support paths in proxy URLs");
 
-			if (!starts_with(proxy_auth.protocol, "socks"))
+			if (!is_socks_proxy_protocol(proxy_auth.protocol))
 				die("Invalid proxy URL '%s': only SOCKS proxies support paths",
 				    curl_http_proxy);
 
diff --git a/t/t5564-http-proxy.sh b/t/t5564-http-proxy.sh
index 3bcbdef409..5669ce37d8 100755
--- a/t/t5564-http-proxy.sh
+++ b/t/t5564-http-proxy.sh
@@ -95,4 +95,10 @@ test_expect_success 'Unix socket requires localhost' - <<\EOT
 	}
 EOT
 
+test_expect_success 'unknown proxy scheme is rejected' '
+	test_must_fail git clone -c http.proxy=htpp://127.0.0.1 \
+		https://example.com/repo.git 2>err &&
+	test_grep "unsupported proxy scheme '\''htpp'\''" err
+'
+
 test_done
-- 
2.49.0.windows.1

Back to recent threads