* [PATCH 0/1] http: reject unsupported proxy URL schemes @ 2026-05-01 19:04 aminnimaj 2026-05-01 19:04 ` [PATCH 1/1] " aminnimaj 2026-05-05 9:19 ` [PATCH v2 0/1] " aminnimaj 0 siblings, 2 replies; 5+ messages in thread From: aminnimaj @ 2026-05-01 19:04 UTC (permalink / raw) To: git; +Cc: peff, ryan.hendrickson, Aliwoto 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 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/1] http: reject unsupported proxy URL schemes 2026-05-01 19:04 [PATCH 0/1] http: reject unsupported proxy URL schemes aminnimaj @ 2026-05-01 19:04 ` aminnimaj 2026-05-03 22:19 ` Junio C Hamano 2026-05-05 9:19 ` [PATCH v2 0/1] " aminnimaj 1 sibling, 1 reply; 5+ messages in thread From: aminnimaj @ 2026-05-01 19:04 UTC (permalink / raw) To: git; +Cc: peff, ryan.hendrickson, Aliwoto 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(-) 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 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/1] http: reject unsupported proxy URL schemes 2026-05-01 19:04 ` [PATCH 1/1] " aminnimaj @ 2026-05-03 22:19 ` Junio C Hamano 0 siblings, 0 replies; 5+ messages in thread From: Junio C Hamano @ 2026-05-03 22:19 UTC (permalink / raw) To: aminnimaj; +Cc: git, peff, ryan.hendrickson aminnimaj@gmail.com writes: > +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")) { ... } } > 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. ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 0/1] http: reject unsupported proxy URL schemes 2026-05-01 19:04 [PATCH 0/1] http: reject unsupported proxy URL schemes aminnimaj 2026-05-01 19:04 ` [PATCH 1/1] " aminnimaj @ 2026-05-05 9:19 ` aminnimaj 2026-05-05 9:19 ` [PATCH v2 1/1] " aminnimaj 1 sibling, 1 reply; 5+ messages in thread From: aminnimaj @ 2026-05-05 9:19 UTC (permalink / raw) To: git; +Cc: gitster, peff, ryan.hendrickson, Aliwoto 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 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v2 1/1] http: reject unsupported proxy URL schemes 2026-05-05 9:19 ` [PATCH v2 0/1] " aminnimaj @ 2026-05-05 9:19 ` aminnimaj 0 siblings, 0 replies; 5+ messages in thread From: aminnimaj @ 2026-05-05 9:19 UTC (permalink / raw) To: git; +Cc: gitster, peff, ryan.hendrickson, Aliwoto 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(-) 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 ^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-05-05 9:20 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-01 19:04 [PATCH 0/1] http: reject unsupported proxy URL schemes aminnimaj 2026-05-01 19:04 ` [PATCH 1/1] " aminnimaj 2026-05-03 22:19 ` Junio C Hamano 2026-05-05 9:19 ` [PATCH v2 0/1] " aminnimaj 2026-05-05 9:19 ` [PATCH v2 1/1] " aminnimaj
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox