From 83291524448bd46b988208a92896c971d4a4c9c1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 06:17:41 +0000 Subject: [PATCH 1/3] Initial plan From 7a4dbc37d9d0119e0834279aabc1f14d744760e8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 06:59:54 +0000 Subject: [PATCH 2/3] Add URL scheme validation on redirects Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com> --- .../HttpClientHandlerTest.AutoRedirect.cs | 32 +++++++++++++++++++ .../SocketsHttpHandler/RedirectHandler.cs | 11 +++++++ 2 files changed, 43 insertions(+) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs index d94b57c5b825a0..a08193a86d5913 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs @@ -269,5 +269,37 @@ await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => }); } } + + [Theory] + [InlineData("ftp://ftp.example.com/file.txt")] + [InlineData("file:///etc/passwd")] + [InlineData("gopher://gopher.example.com")] + [InlineData("telnet://telnet.example.com")] + public async Task GetAsync_AllowAutoRedirectTrue_UnsupportedRedirectScheme_ReturnsOriginalResponse(string redirectLocation) + { + if (IsWinHttpHandler) + { + // WinHttpHandler may have different behavior for unsupported schemes + return; + } + + HttpClientHandler handler = CreateHttpClientHandler(); + handler.AllowAutoRedirect = true; + using (HttpClient client = CreateHttpClient(handler)) + { + await LoopbackServer.CreateServerAsync(async (server, url) => + { + Task getTask = client.GetAsync(url); + Task> serverTask = server.AcceptConnectionSendResponseAndCloseAsync(HttpStatusCode.Found, $"Location: {redirectLocation}\r\n"); + await TestHelper.WhenAllCompletedOrAnyFailed(getTask, serverTask); + + using (HttpResponseMessage response = await getTask) + { + Assert.Equal(302, (int)response.StatusCode); + Assert.Equal(url, response.RequestMessage.RequestUri); + } + }); + } + } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs index bd8710f367b114..20bf795b7a215a 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/RedirectHandler.cs @@ -141,6 +141,17 @@ internal override async ValueTask SendAsync(HttpRequestMess return null; } + // Disallow automatic redirection to unsupported schemes + if (!HttpUtilities.IsSupportedScheme(location.Scheme)) + { + if (NetEventSource.Log.IsEnabled()) + { + TraceError($"Redirect from '{requestUri}' to '{location}' blocked due to unsupported scheme '{location.Scheme}'.", response.RequestMessage!.GetHashCode()); + } + + return null; + } + return location; } From 29aacc178a1cd698fe645b4d56085a8634e1bed8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 3 Nov 2025 09:04:01 +0000 Subject: [PATCH 3/3] Update test to verify WinHttpHandler throws on unsupported redirect schemes Co-authored-by: MihaZupan <25307628+MihaZupan@users.noreply.github.com> --- .../HttpClientHandlerTest.AutoRedirect.cs | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs index a08193a86d5913..8161d1a8198659 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AutoRedirect.cs @@ -277,12 +277,6 @@ await LoopbackServer.CreateServerAsync(async (origServer, origUrl) => [InlineData("telnet://telnet.example.com")] public async Task GetAsync_AllowAutoRedirectTrue_UnsupportedRedirectScheme_ReturnsOriginalResponse(string redirectLocation) { - if (IsWinHttpHandler) - { - // WinHttpHandler may have different behavior for unsupported schemes - return; - } - HttpClientHandler handler = CreateHttpClientHandler(); handler.AllowAutoRedirect = true; using (HttpClient client = CreateHttpClient(handler)) @@ -291,12 +285,23 @@ await LoopbackServer.CreateServerAsync(async (server, url) => { Task getTask = client.GetAsync(url); Task> serverTask = server.AcceptConnectionSendResponseAndCloseAsync(HttpStatusCode.Found, $"Location: {redirectLocation}\r\n"); - await TestHelper.WhenAllCompletedOrAnyFailed(getTask, serverTask); - using (HttpResponseMessage response = await getTask) + if (IsWinHttpHandler) { - Assert.Equal(302, (int)response.StatusCode); - Assert.Equal(url, response.RequestMessage.RequestUri); + // WinHttpHandler throws HttpRequestException for unsupported redirect schemes + await Assert.ThrowsAsync(async () => await getTask); + await serverTask; + } + else + { + // SocketsHttpHandler refuses to follow the redirect and returns the original response + await TestHelper.WhenAllCompletedOrAnyFailed(getTask, serverTask); + + using (HttpResponseMessage response = await getTask) + { + Assert.Equal(302, (int)response.StatusCode); + Assert.Equal(url, response.RequestMessage.RequestUri); + } } }); }