Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@
<value>The underlying connection was closed: An unexpected error occurred on a receive</value>
</data>
<data name="net_ftp_no_newlines" xml:space="preserve">
<value>CRLF character pair is not allowed in FtpWebRequest inputs.</value>
<value>CR and LF characters are not allowed in FtpWebRequest inputs.</value>
</data>
<data name="net_webstatus_NameResolutionFailure" xml:space="preserve">
<value>The remote name could not be resolved</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1147,7 +1147,7 @@ private string GetPortCommandLine()
/// </summary>
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Comment thread
mrek-msft marked this conversation as resolved.
throw new FormatException(SR.net_ftp_no_newlines);
Comment thread
mrek-msft marked this conversation as resolved.

_timerCallback = new TimerThread.Callback(TimerCallback);
Expand Down
40 changes: 37 additions & 3 deletions src/libraries/System.Net.Requests/tests/FtpWebRequestTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FormatException>(() => WebRequest.Create(uri));
}

[Fact]
public void Ctor_VerifyDefaults_Success()
{
Expand Down Expand Up @@ -211,19 +228,36 @@ public void Ftp_Ignore_NewLine_Constructor_Throws_FormatException()
Assert.Throws<FormatException>(() => 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<WebException>(() => ftpWebRequest.GetRequestStream());
Assert.True(requestException.InnerException is FormatException);

var responseException = Assert.Throws<WebException>(() => ftpWebRequest.GetResponse());
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<WebException>(() => request.GetResponse());
Assert.IsType<FormatException>(ex.InnerException);
}

private static async Task<MemoryStream> DoAsync(FtpWebRequest request, MemoryStream requestBody)
{
if (requestBody != null)
Expand Down
Loading