From 18ce50f5439ef3bccbbd60759ed8bb506fef9bcb Mon Sep 17 00:00:00 2001 From: huysentruitw Date: Wed, 15 May 2019 18:34:50 +0200 Subject: [PATCH] ContentDispositionHeaderValue must encode/sanitize new-line char (#9971) --- .../src/ContentDispositionHeaderValue.cs | 6 ++--- .../test/ContentDispositionHeaderValueTest.cs | 26 +++++++++++++++++++ src/Mvc/Mvc.Core/test/FileResultTest.cs | 16 ++++-------- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/Http/Headers/src/ContentDispositionHeaderValue.cs b/src/Http/Headers/src/ContentDispositionHeaderValue.cs index 5084f8ea06..cb868ce897 100644 --- a/src/Http/Headers/src/ContentDispositionHeaderValue.cs +++ b/src/Http/Headers/src/ContentDispositionHeaderValue.cs @@ -444,7 +444,7 @@ namespace Microsoft.Net.Http.Headers for (int i = 0; i < result.Length; i++) { var c = result[i]; - if ((int)c > 0x7f) + if ((int)c > 0x7f || (int)c < 0x20) { c = '_'; // Replace out-of-range characters } @@ -472,7 +472,7 @@ namespace Microsoft.Net.Http.Headers for (int i = 0; i < input.Length; i++) { - if ((int)input[i] > 0x7f) + if ((int)input[i] > 0x7f || (int)input[i] < 0x20) { return true; } @@ -722,4 +722,4 @@ namespace Microsoft.Net.Http.Headers + 10))); } } -} \ No newline at end of file +} diff --git a/src/Http/Headers/test/ContentDispositionHeaderValueTest.cs b/src/Http/Headers/test/ContentDispositionHeaderValueTest.cs index f2c53a8727..6076b2cc15 100644 --- a/src/Http/Headers/test/ContentDispositionHeaderValueTest.cs +++ b/src/Http/Headers/test/ContentDispositionHeaderValueTest.cs @@ -141,6 +141,21 @@ namespace Microsoft.Net.Http.Headers Assert.Null(contentDisposition.FileName.Value); } + [Fact] + public void FileName_NeedsEncodingBecauseOfNewLine_EncodedAndDecodedCorrectly() + { + var contentDisposition = new ContentDispositionHeaderValue("inline"); + + contentDisposition.FileName = "File\nName.bat"; + Assert.Equal("File\nName.bat", contentDisposition.FileName); + Assert.Equal(1, contentDisposition.Parameters.Count); + Assert.Equal("filename", contentDisposition.Parameters.First().Name); + Assert.Equal("\"=?utf-8?B?RmlsZQpOYW1lLmJhdA==?=\"", contentDisposition.Parameters.First().Value); + + contentDisposition.Parameters.Remove(contentDisposition.Parameters.First()); + Assert.Null(contentDisposition.FileName.Value); + } + [Fact] public void FileName_UnknownOrBadEncoding_PropertyFails() { @@ -222,6 +237,17 @@ namespace Microsoft.Net.Http.Headers Assert.Null(contentDisposition.FileNameStar.Value); } + [Theory] + [InlineData("FileName.bat", "FileName.bat")] + [InlineData("FileÃName.bat", "File_Name.bat")] + [InlineData("File\nName.bat", "File_Name.bat")] + public void SetHttpFileName_ShouldSanitizeFileNameWhereNeeded(string httpFileName, string expectedFileName) + { + var contentDisposition = new ContentDispositionHeaderValue("inline"); + contentDisposition.SetHttpFileName(httpFileName); + Assert.Equal(expectedFileName, contentDisposition.FileName); + } + [Fact] public void Dates_AddDateParameterThenUseProperty_ParametersEntryIsOverwritten() { diff --git a/src/Mvc/Mvc.Core/test/FileResultTest.cs b/src/Mvc/Mvc.Core/test/FileResultTest.cs index 8e467419b2..3b517db4f1 100644 --- a/src/Mvc/Mvc.Core/test/FileResultTest.cs +++ b/src/Mvc/Mvc.Core/test/FileResultTest.cs @@ -174,7 +174,6 @@ namespace Microsoft.AspNetCore.Mvc { "{", "attachment; filename=\"{\"; filename*=UTF-8''%7B" }, { "}", "attachment; filename=\"}\"; filename*=UTF-8''%7D" }, { " ", "attachment; filename=\" \"; filename*=UTF-8''%20" }, - { "a\tb", "attachment; filename=\"a\tb\"; filename*=UTF-8''a%09b" }, { "a b", "attachment; filename=\"a b\"; filename*=UTF-8''a%20b" }, // Values that need to be escaped @@ -182,12 +181,13 @@ namespace Microsoft.AspNetCore.Mvc { "\\", "attachment; filename=\"\\\\\"; filename*=UTF-8''%5C" }, // Values that need to be specially encoded (Base64, see rfc2047) - { "a\nb", "attachment; filename=\"a\nb\"; filename*=UTF-8''a%0Ab" }, + { "a\tb", "attachment; filename=a_b; filename*=UTF-8''a%09b" }, + { "a\nb", "attachment; filename=a_b; filename*=UTF-8''a%0Ab" }, // Values with non unicode characters { "résumé.txt", "attachment; filename=r_sum_.txt; filename*=UTF-8''r%C3%A9sum%C3%A9.txt" }, { "Δ", "attachment; filename=_; filename*=UTF-8''%CE%94" }, - { "Δ\t", "attachment; filename=\"_\t\"; filename*=UTF-8''%CE%94%09" }, + { "Δ\t", "attachment; filename=__; filename*=UTF-8''%CE%94%09" }, { "ABCXYZabcxyz012789!@#$%^&*()-=_+.:~Δ", @"attachment; filename=""ABCXYZabcxyz012789!@#$%^&*()-=_+.:~_""; filename*=UTF-8''ABCXYZabcxyz012789!%40#$%25^&%2A%28%29-%3D_+.%3A~%CE%94" }, }; } @@ -200,16 +200,10 @@ namespace Microsoft.AspNetCore.Mvc var data = new TheoryData(); for (var i = 0; i < 32; i++) { - if (i == 10) - { - // skip \n as it has a special encoding - continue; - } - - data.Add(char.ConvertFromUtf32(i), "attachment; filename=\"" + char.ConvertFromUtf32(i) + "\"; filename*=UTF-8''%" + i.ToString("X2")); + data.Add(char.ConvertFromUtf32(i), $"attachment; filename=_; filename*=UTF-8''%{i:X2}"); } - data.Add(char.ConvertFromUtf32(127), "attachment; filename=\"" + char.ConvertFromUtf32(127) + "\"; filename*=UTF-8''%7F"); + data.Add(char.ConvertFromUtf32(127), $"attachment; filename=\"{char.ConvertFromUtf32(127)}\"; filename*=UTF-8''%7F"); return data; }