http: reject unsupported proxy URL schemes
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> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Aliwoto committed
May 5, 2026 at 09:19 UTC
663d7abe07ea376c2657019a03297ae87037c993
2 files changed
+74
-25
http.c
+68
-25
@@ -744,6 +744,69 @@ static int has_proxy_cert_password(void)
744
return 1;
745
}
746
747
+static const struct socks_proxy_type {
748
+ const char *name;
749
+ long curlsym;
750
+} socks_proxy_types[] = {
751
+ { "socks", CURLPROXY_SOCKS4 },
752
+ { "socks4", CURLPROXY_SOCKS4 },
753
+ { "socks4a", CURLPROXY_SOCKS4A },
754
+ { "socks5", CURLPROXY_SOCKS5 },
755
+ { "socks5h", CURLPROXY_SOCKS5_HOSTNAME },
756
+};
757
+
758
+static const struct socks_proxy_type *find_socks_proxy_type(const char *protocol)
759
+{
760
+ int i;
761
+
762
+ if (!protocol)
763
+ return NULL;
764
+
765
+ for (i = 0; i < ARRAY_SIZE(socks_proxy_types); i++) {
766
+ if (!strcmp(socks_proxy_types[i].name, protocol))
767
+ return &socks_proxy_types[i];
768
+ }
769
+
770
+ return NULL;
771
+}
772
+
773
+static int is_socks_proxy_protocol(const char *protocol)
774
+{
775
+ return !!find_socks_proxy_type(protocol);
776
+}
777
+
778
+static int set_curl_proxy_type(CURL *result, const char *protocol)
779
+{
780
+ const struct socks_proxy_type *socks_proxy_type;
781
+
782
+ if (!protocol || !strcmp(protocol, "http"))
783
+ return 0;
784
+
785
+ socks_proxy_type = find_socks_proxy_type(protocol);
786
+ if (socks_proxy_type) {
787
+ curl_easy_setopt(result, CURLOPT_PROXYTYPE, socks_proxy_type->curlsym);
788
+ return 0;
789
+ }
790
+
791
+ if (!strcmp(protocol, "https")) {
792
+ curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS);
793
+
794
+ if (http_proxy_ssl_cert)
795
+ curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT,
796
+ http_proxy_ssl_cert);
797
+
798
+ if (http_proxy_ssl_key)
799
+ curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY,
800
+ http_proxy_ssl_key);
801
+
802
+ if (has_proxy_cert_password())
803
+ curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD,
804
+ proxy_cert_auth.password);
805
+ }
806
+
807
+ return -1;
808
+}
809
+
810
/* Return 1 if redactions have been made, 0 otherwise. */
811
static int redact_sensitive_header(struct strbuf *header, size_t offset)
812
{
@@ -1214,30 +1277,6 @@ static CURL *get_curl_handle(void)
1277
} else if (curl_http_proxy) {
1278
struct strbuf proxy = STRBUF_INIT;
1279
1217
- if (starts_with(curl_http_proxy, "socks5h"))
1218
- curl_easy_setopt(result,
1219
- CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5_HOSTNAME);
1220
- else if (starts_with(curl_http_proxy, "socks5"))
1221
- curl_easy_setopt(result,
1222
- CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS5);
1223
- else if (starts_with(curl_http_proxy, "socks4a"))
1224
- curl_easy_setopt(result,
1225
- CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4A);
1226
- else if (starts_with(curl_http_proxy, "socks"))
1227
- curl_easy_setopt(result,
1228
- CURLOPT_PROXYTYPE, (long)CURLPROXY_SOCKS4);
1229
- else if (starts_with(curl_http_proxy, "https")) {
1230
- curl_easy_setopt(result, CURLOPT_PROXYTYPE, (long)CURLPROXY_HTTPS);
1231
-
1232
- if (http_proxy_ssl_cert)
1233
- curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);
1234
-
1235
- if (http_proxy_ssl_key)
1236
- curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);
1237
-
1238
- if (has_proxy_cert_password())
1239
- curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);
1240
- }
1280
if (strstr(curl_http_proxy, "://"))
1281
credential_from_url(&proxy_auth, curl_http_proxy);
1282
else {
@@ -1247,6 +1286,10 @@ static CURL *get_curl_handle(void)
1286
strbuf_release(&url);
1287
}
1288
1289
+ if (set_curl_proxy_type(result, proxy_auth.protocol) < 0)
1290
+ die("Invalid proxy URL '%s': unsupported proxy scheme '%s'",
1291
+ curl_http_proxy, proxy_auth.protocol);
1292
+
1293
if (!proxy_auth.host)
1294
die("Invalid proxy URL '%s'", curl_http_proxy);
1295
@@ -1257,7 +1300,7 @@ static CURL *get_curl_handle(void)
1300
if (ver->version_num < 0x075400)
1301
die("libcurl 7.84 or later is required to support paths in proxy URLs");
1302
1260
- if (!starts_with(proxy_auth.protocol, "socks"))
1303
+ if (!is_socks_proxy_protocol(proxy_auth.protocol))
1304
die("Invalid proxy URL '%s': only SOCKS proxies support paths",
1305
curl_http_proxy);
1306
t/t5564-http-proxy.sh
+6
@@ -95,4 +95,10 @@ test_expect_success 'Unix socket requires localhost' - <<\EOT
95
}
96
EOT
97
98
+test_expect_success 'unknown proxy scheme is rejected' '
99
+ test_must_fail git clone -c http.proxy=htpp://127.0.0.1 \
100
+ https://example.com/repo.git 2>err &&
101
+ test_grep "unsupported proxy scheme '\''htpp'\''" err
102
+'
103
+
104
test_done