diff --git a/src/libraries/System.Net.Requests/src/Resources/Strings.resx b/src/libraries/System.Net.Requests/src/Resources/Strings.resx index e746e70fefa499..4919a50fbaa0a8 100644 --- a/src/libraries/System.Net.Requests/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Requests/src/Resources/Strings.resx @@ -196,7 +196,7 @@ The underlying connection was closed: An unexpected error occurred on a receive - CRLF character pair is not allowed in FtpWebRequest inputs. + CR and LF characters are not allowed in FtpWebRequest inputs. The remote name could not be resolved diff --git a/src/libraries/System.Net.Requests/src/System/Net/FtpControlStream.cs b/src/libraries/System.Net.Requests/src/System/Net/FtpControlStream.cs index ba72c6f8a9dc76..6673d8483ec158 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/FtpControlStream.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/FtpControlStream.cs @@ -1147,7 +1147,7 @@ private string GetPortCommandLine() /// private static string FormatFtpCommand(string command, string? parameter) { - if (parameter is not null && parameter.Contains("\r\n", StringComparison.Ordinal)) + if (parameter is not null && parameter.AsSpan().ContainsAny('\r', '\n')) { throw new FormatException(SR.net_ftp_no_newlines); } diff --git a/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs b/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs index f470879e993e41..91b95a5eea6604 100644 --- a/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs +++ b/src/libraries/System.Net.Requests/src/System/Net/FtpWebRequest.cs @@ -487,7 +487,9 @@ internal FtpWebRequest(Uri uri) if ((object)uri.Scheme != (object)Uri.UriSchemeFtp) throw new ArgumentOutOfRangeException(nameof(uri)); - if (uri.OriginalString.Contains("\r\n", StringComparison.Ordinal)) + if (uri.OriginalString.AsSpan().ContainsAny('\r', '\n') || + uri.OriginalString.Contains("%0A", StringComparison.OrdinalIgnoreCase) || + uri.OriginalString.Contains("%0D", StringComparison.OrdinalIgnoreCase)) throw new FormatException(SR.net_ftp_no_newlines); _timerCallback = new TimerThread.Callback(TimerCallback); diff --git a/src/libraries/System.Net.Requests/tests/FtpWebRequestTest.cs b/src/libraries/System.Net.Requests/tests/FtpWebRequestTest.cs index 5401b1e93ebbb7..aae99260d14301 100644 --- a/src/libraries/System.Net.Requests/tests/FtpWebRequestTest.cs +++ b/src/libraries/System.Net.Requests/tests/FtpWebRequestTest.cs @@ -29,6 +29,23 @@ public FtpExecutionMode(bool useSsl, bool usePassive, bool useAsync, bool useOld } } + [Theory] + [InlineData("ftp://foo.com/bar\r\nbaz")] + [InlineData("ftp://foo.com/bar\rbaz")] + [InlineData("ftp://foo.com/bar\nbaz")] + [InlineData("ftp://foo.com/bar%0D%0Abaz")] + [InlineData("ftp://foo.com/bar%0d%0abaz")] + [InlineData("ftp://foo.com/bar%0D%0abaz")] + [InlineData("ftp://foo.com/bar%0Dbaz")] + [InlineData("ftp://foo.com/bar%0dbaz")] + [InlineData("ftp://foo.com/bar%0Abaz")] + [InlineData("ftp://foo.com/bar%0abaz")] + public void Ctor_NewLineInUri_ThrowsFormatException(string uriString) + { + Uri uri = new Uri(uriString, UriKind.Absolute); + Assert.Throws(() => WebRequest.Create(uri)); + } + [Fact] public void Ctor_VerifyDefaults_Success() { @@ -211,12 +228,15 @@ public void Ftp_Ignore_NewLine_Constructor_Throws_FormatException() Assert.Throws(() => WebRequest.Create($"{uri}\r\n{WebRequestMethods.Ftp.AppendFile} {Guid.NewGuid().ToString()}")); } - [ConditionalFact(typeof(FtpWebRequestTest), nameof(LocalServerAvailable))] - public void Ftp_Ignore_NewLine_GetRequestStream_And_GetResponse_Throws_FormatException_As_InnerException() + [ConditionalTheory(typeof(FtpWebRequestTest), nameof(LocalServerAvailable))] + [InlineData("test\r\ntest2")] + [InlineData("test\rtest2")] + [InlineData("test\ntest2")] + public void Ftp_Ignore_NewLine_GetRequestStream_And_GetResponse_Throws_FormatException_As_InnerException(string credential) { FtpWebRequest ftpWebRequest = (FtpWebRequest)WebRequest.Create(absoluteUri + Guid.NewGuid().ToString()); ftpWebRequest.Method = "APPE"; - ftpWebRequest.Credentials = new NetworkCredential("test\r\ntest2", "test\r\ntest2"); + ftpWebRequest.Credentials = new NetworkCredential(credential, credential); var requestException = Assert.Throws(() => ftpWebRequest.GetRequestStream()); Assert.True(requestException.InnerException is FormatException); @@ -224,6 +244,20 @@ public void Ftp_Ignore_NewLine_GetRequestStream_And_GetResponse_Throws_FormatExc Assert.True(responseException.InnerException is FormatException); } + [ConditionalTheory(typeof(FtpWebRequestTest), nameof(LocalServerAvailable))] + [InlineData("ok\r\nbad")] + [InlineData("ok\rbad")] + [InlineData("ok\nbad")] + public void Ftp_NewLineInRenameTo_GetResponse_Throws_FormatException_As_InnerException(string renameTo) + { + FtpWebRequest request = (FtpWebRequest)WebRequest.Create(absoluteUri + Guid.NewGuid().ToString()); + request.Method = WebRequestMethods.Ftp.Rename; + request.RenameTo = renameTo; + + WebException ex = Assert.Throws(() => request.GetResponse()); + Assert.IsType(ex.InnerException); + } + private static async Task DoAsync(FtpWebRequest request, MemoryStream requestBody) { if (requestBody != null)