From 5c4393363d8c4e39a3f9d4fed6c71da3d9873321 Mon Sep 17 00:00:00 2001 From: youssefm Date: Tue, 27 Mar 2012 14:41:47 -0700 Subject: [PATCH] Lower MaxDepth quota --- .../Formatting/JsonMediaTypeFormatter.cs | 15 +++++++-- .../Formatting/JsonReaderQuotaException.cs | 32 ++++++++++++++++++ .../Formatting/SecureJsonTextReader.cs | 4 +-- .../FormattingUtilities.cs | 9 ++--- .../System.Net.Http.Formatting.csproj | 1 + .../JsonNetSerializationTest.cs | 2 +- .../JsonNetValidationTest.cs | 33 +++++++++++++++++-- ...DataContractJsonMediaTypeFormatterTests.cs | 2 +- .../FormUrlEncodedMediaTypeFormatterTests.cs | 2 +- .../Formatting/JsonMediaTypeFormatterTests.cs | 2 +- .../Formatting/XmlMediaTypeFormatterTests.cs | 2 +- 11 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 src/System.Net.Http.Formatting/Formatting/JsonReaderQuotaException.cs diff --git a/src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs b/src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs index 0dd05a0b..eb56f033 100644 --- a/src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs +++ b/src/System.Net.Http.Formatting/Formatting/JsonMediaTypeFormatter.cs @@ -254,8 +254,19 @@ public override Task ReadFromStreamAsync(Type type, Stream stream, HttpC { jsonSerializer.Error += (sender, e) => { - formatterLogger.LogError(e.ErrorContext.Path, e.ErrorContext.Error.Message); - e.ErrorContext.Handled = true; + Exception exception = e.ErrorContext.Error; + + // reader quota exceptions are fatal and cannot be recovered from + // we need to shortcircuit any further deserialization + if (exception is JsonReaderQuotaException) + { + e.ErrorContext.Handled = false; + } + else + { + formatterLogger.LogError(e.ErrorContext.Path, exception.Message); + e.ErrorContext.Handled = true; + } }; } return jsonSerializer.Deserialize(jsonTextReader, type); diff --git a/src/System.Net.Http.Formatting/Formatting/JsonReaderQuotaException.cs b/src/System.Net.Http.Formatting/Formatting/JsonReaderQuotaException.cs new file mode 100644 index 00000000..646cf627 --- /dev/null +++ b/src/System.Net.Http.Formatting/Formatting/JsonReaderQuotaException.cs @@ -0,0 +1,32 @@ +using System.Runtime.Serialization; +using Newtonsoft.Json; + +namespace System.Net.Http.Formatting +{ + /// + /// Exception type to indicate that json reader quotas have been exceeded. + /// + [Serializable] + internal class JsonReaderQuotaException : JsonReaderException + { + public JsonReaderQuotaException() + : base() + { + } + + public JsonReaderQuotaException(string message) + : base(message) + { + } + + protected JsonReaderQuotaException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + public JsonReaderQuotaException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/src/System.Net.Http.Formatting/Formatting/SecureJsonTextReader.cs b/src/System.Net.Http.Formatting/Formatting/SecureJsonTextReader.cs index aaa46107..96d546ca 100644 --- a/src/System.Net.Http.Formatting/Formatting/SecureJsonTextReader.cs +++ b/src/System.Net.Http.Formatting/Formatting/SecureJsonTextReader.cs @@ -29,9 +29,9 @@ public override object Value public override bool Read() { - if (this.Depth > _maxDepth) + if (Depth > _maxDepth) { - throw new JsonReaderException(RS.Format(Properties.Resources.JsonTooDeep, _maxDepth)); + throw new JsonReaderQuotaException(RS.Format(Properties.Resources.JsonTooDeep, _maxDepth)); } return base.Read(); } diff --git a/src/System.Net.Http.Formatting/FormattingUtilities.cs b/src/System.Net.Http.Formatting/FormattingUtilities.cs index 6d0b063f..c2ea2743 100644 --- a/src/System.Net.Http.Formatting/FormattingUtilities.cs +++ b/src/System.Net.Http.Formatting/FormattingUtilities.cs @@ -15,9 +15,9 @@ namespace System.Net.Http internal static class FormattingUtilities { /// - /// The default max depth for our formatter is 1K + /// The default max depth for our formatter is 256 /// - public const int DefaultMaxDepth = 1024; + public const int DefaultMaxDepth = 256; /// /// The default min depth for our formatter is 1 @@ -34,11 +34,6 @@ internal static class FormattingUtilities /// public const string HttpRequestedWithHeaderValue = @"xmlhttprequest"; - /// - /// JSON literal for 'null' - /// - public const string JsonNullLiteral = "null"; - /// /// HTTP Host header field name /// diff --git a/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj b/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj index 256f9e76..4c2b7a54 100644 --- a/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj +++ b/src/System.Net.Http.Formatting/System.Net.Http.Formatting.csproj @@ -85,6 +85,7 @@ Properties\TransparentCommonAssemblyInfo.cs + diff --git a/test/System.Net.Http.Formatting.Test.Integration/JsonNetSerializationTest.cs b/test/System.Net.Http.Formatting.Test.Integration/JsonNetSerializationTest.cs index 11e9401f..8f4ff6ca 100644 --- a/test/System.Net.Http.Formatting.Test.Integration/JsonNetSerializationTest.cs +++ b/test/System.Net.Http.Formatting.Test.Integration/JsonNetSerializationTest.cs @@ -200,7 +200,7 @@ public void DeserializingDeepArraysThrows() } string json = sb.ToString(); - Assert.Throws(typeof(JsonReaderException), () => Deserialize(json, typeof(object))); + Assert.Throws(typeof(JsonReaderQuotaException), () => Deserialize(json, typeof(object))); } [Theory] diff --git a/test/System.Net.Http.Formatting.Test.Integration/JsonNetValidationTest.cs b/test/System.Net.Http.Formatting.Test.Integration/JsonNetValidationTest.cs index a862b7fb..6875c7c6 100644 --- a/test/System.Net.Http.Formatting.Test.Integration/JsonNetValidationTest.cs +++ b/test/System.Net.Http.Formatting.Test.Integration/JsonNetValidationTest.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; -using System.Runtime.Serialization; +using System.Reflection; +using System.Text; using Microsoft.TestCommon; using Moq; using Xunit; using Xunit.Extensions; -using System.Reflection; namespace System.Net.Http.Formatting { @@ -57,6 +57,30 @@ public void ModelErrorsPopulatedWithValidationErrors(string json, Type type, int Assert.DoesNotThrow(() => JsonNetSerializationTest.Deserialize(json, type, formatter, mockLogger.Object)); Assert.Equal(expectedErrors, errors); } + + [Fact] + public void HittingMaxDepthRaisesOnlyOneValidationError() + { + // Arrange + JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter(); + int errors = 0; + Mock mockLogger = new Mock(); + mockLogger.Setup(mock => mock.LogError(It.IsAny(), It.IsAny())).Callback(() => errors++); + + StringBuilder sb = new StringBuilder("{'A':null}"); + for (int i = 0; i < 5000; i++) + { + sb.Insert(0, "{'A':"); + sb.Append('}'); + } + string json = sb.ToString(); + + // Act + Assert.DoesNotThrow(() => JsonNetSerializationTest.Deserialize(json, typeof(Nest), formatter, mockLogger.Object)); + + // Assert + Assert.Equal(1, errors); + } } // this IRMS treats all member names that start with "Required" as required @@ -89,4 +113,9 @@ public string Throws } } } + + public class Nest + { + public Nest A { get; set; } + } } \ No newline at end of file diff --git a/test/System.Net.Http.Formatting.Test.Unit/Formatting/DataContractJsonMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test.Unit/Formatting/DataContractJsonMediaTypeFormatterTests.cs index ef9a83d9..a8fe4994 100644 --- a/test/System.Net.Http.Formatting.Test.Unit/Formatting/DataContractJsonMediaTypeFormatterTests.cs +++ b/test/System.Net.Http.Formatting.Test.Unit/Formatting/DataContractJsonMediaTypeFormatterTests.cs @@ -74,7 +74,7 @@ public void MaxDepth_RoundTrips() Assert.Reflection.IntegerProperty( new DataContractJsonMediaTypeFormatter(), c => c.MaxDepth, - expectedDefaultValue: 1024, + expectedDefaultValue: 256, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null, diff --git a/test/System.Net.Http.Formatting.Test.Unit/Formatting/FormUrlEncodedMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test.Unit/Formatting/FormUrlEncodedMediaTypeFormatterTests.cs index 656bb028..0a80599f 100644 --- a/test/System.Net.Http.Formatting.Test.Unit/Formatting/FormUrlEncodedMediaTypeFormatterTests.cs +++ b/test/System.Net.Http.Formatting.Test.Unit/Formatting/FormUrlEncodedMediaTypeFormatterTests.cs @@ -62,7 +62,7 @@ public void MaxDepthReturnsCorrectValue() Assert.Reflection.IntegerProperty( new FormUrlEncodedMediaTypeFormatter(), f => f.MaxDepth, - expectedDefaultValue: 1024, + expectedDefaultValue: 256, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null, diff --git a/test/System.Net.Http.Formatting.Test.Unit/Formatting/JsonMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test.Unit/Formatting/JsonMediaTypeFormatterTests.cs index 5f42dcec..46c56019 100644 --- a/test/System.Net.Http.Formatting.Test.Unit/Formatting/JsonMediaTypeFormatterTests.cs +++ b/test/System.Net.Http.Formatting.Test.Unit/Formatting/JsonMediaTypeFormatterTests.cs @@ -82,7 +82,7 @@ public void MaxDepth_RoundTrips() Assert.Reflection.IntegerProperty( new JsonMediaTypeFormatter(), c => c.MaxDepth, - expectedDefaultValue: 1024, + expectedDefaultValue: 256, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null, diff --git a/test/System.Net.Http.Formatting.Test.Unit/Formatting/XmlMediaTypeFormatterTests.cs b/test/System.Net.Http.Formatting.Test.Unit/Formatting/XmlMediaTypeFormatterTests.cs index e2bf0562..c0fab779 100644 --- a/test/System.Net.Http.Formatting.Test.Unit/Formatting/XmlMediaTypeFormatterTests.cs +++ b/test/System.Net.Http.Formatting.Test.Unit/Formatting/XmlMediaTypeFormatterTests.cs @@ -50,7 +50,7 @@ public void MaxDepthReturnsCorrectValue() Assert.Reflection.IntegerProperty( new XmlMediaTypeFormatter(), f => f.MaxDepth, - expectedDefaultValue: 1024, + expectedDefaultValue: 256, minLegalValue: 1, illegalLowerValue: 0, maxLegalValue: null,