From 4bc07e32ac33e36109e692e7735641c2acc8eb53 Mon Sep 17 00:00:00 2001 From: Roman Bulanenko Date: Wed, 22 Nov 2023 16:30:02 +0100 Subject: [PATCH] Handle number overflow during parsing of min/max values --- .../ParseNodes/ParserHelper.cs | 35 +++++++++++++++++++ .../V2/OpenApiHeaderDeserializer.cs | 6 ++-- .../V2/OpenApiParameterDeserializer.cs | 4 +-- .../V2/OpenApiSchemaDeserializer.cs | 4 +-- .../V3/OpenApiSchemaDeserializer.cs | 4 +-- .../ParseNodes/ParserHelperTests.cs | 24 +++++++++++++ 6 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 src/Microsoft.OpenApi.Readers/ParseNodes/ParserHelper.cs create mode 100644 test/Microsoft.OpenApi.Readers.Tests/ParseNodes/ParserHelperTests.cs diff --git a/src/Microsoft.OpenApi.Readers/ParseNodes/ParserHelper.cs b/src/Microsoft.OpenApi.Readers/ParseNodes/ParserHelper.cs new file mode 100644 index 000000000..9dd05ebdd --- /dev/null +++ b/src/Microsoft.OpenApi.Readers/ParseNodes/ParserHelper.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Globalization; + +namespace Microsoft.OpenApi.Readers.ParseNodes +{ + /// + /// Useful tools to parse data + /// + internal class ParserHelper + { + /// + /// Parses decimal in invariant culture. + /// If the decimal is too big or small, it returns the default value + /// + /// Note: sometimes developers put Double.MaxValue or Long.MaxValue as min/max values for numbers in json schema even if their numbers are not expected to be that big/small. + /// As we have already released the library with Decimal type for Max/Min, let's not introduce the breaking change and just fallback to Decimal.Max / Min. This should satisfy almost every scenario. + /// We can revisit this if somebody really needs to have double or long here. + /// + /// + public static decimal ParseDecimalWithFallbackOnOverflow(string value, decimal defaultValue) + { + try + { + return decimal.Parse(value, NumberStyles.Float, CultureInfo.InvariantCulture); + } + catch (OverflowException) + { + return defaultValue; + } + } + } +} diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs index a6c99a2d9..ff873f4a0 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiHeaderDeserializer.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -44,7 +44,7 @@ internal static partial class OpenApiV2Deserializer }, { "maximum", - (o, n) => GetOrCreateSchema(o).Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture) + (o, n) => GetOrCreateSchema(o).Maximum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MaxValue) }, { "exclusiveMaximum", @@ -52,7 +52,7 @@ internal static partial class OpenApiV2Deserializer }, { "minimum", - (o, n) => GetOrCreateSchema(o).Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture) + (o, n) => GetOrCreateSchema(o).Minimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MinValue) }, { "exclusiveMinimum", diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs index 0429d30ba..f74600e66 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs @@ -61,11 +61,11 @@ internal static partial class OpenApiV2Deserializer }, { "minimum", - (o, n) => GetOrCreateSchema(o).Minimum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture) + (o, n) => GetOrCreateSchema(o).Minimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MinValue) }, { "maximum", - (o, n) => GetOrCreateSchema(o).Maximum = decimal.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture) + (o, n) => GetOrCreateSchema(o).Maximum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MaxValue) }, { "maxLength", diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs index 5fd3ba5c8..dd884d574 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiSchemaDeserializer.cs @@ -27,7 +27,7 @@ internal static partial class OpenApiV2Deserializer }, { "maximum", - (o, n) => o.Maximum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture) + (o, n) => o.Maximum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MaxValue) }, { "exclusiveMaximum", @@ -35,7 +35,7 @@ internal static partial class OpenApiV2Deserializer }, { "minimum", - (o, n) => o.Minimum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture) + (o, n) => o.Minimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MinValue) }, { "exclusiveMinimum", diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs index 52b79a1f9..1e386a33d 100644 --- a/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiSchemaDeserializer.cs @@ -27,7 +27,7 @@ internal static partial class OpenApiV3Deserializer }, { "maximum", - (o, n) => o.Maximum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture) + (o, n) => o.Maximum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MaxValue) }, { "exclusiveMaximum", @@ -35,7 +35,7 @@ internal static partial class OpenApiV3Deserializer }, { "minimum", - (o, n) => o.Minimum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture) + (o, n) => o.Minimum = ParserHelper.ParseDecimalWithFallbackOnOverflow(n.GetScalarValue(), decimal.MinValue) }, { "exclusiveMinimum", diff --git a/test/Microsoft.OpenApi.Readers.Tests/ParseNodes/ParserHelperTests.cs b/test/Microsoft.OpenApi.Readers.Tests/ParseNodes/ParserHelperTests.cs new file mode 100644 index 000000000..b343abb5c --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/ParseNodes/ParserHelperTests.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using Microsoft.OpenApi.Readers.ParseNodes; +using Xunit; + +namespace Microsoft.OpenApi.Readers.Tests.ParseNodes +{ + [Collection("DefaultSettings")] + public class ParserHelperTests + { + [Fact] + public void ParseDecimalWithFallbackOnOverflow_ReturnsParsedValue() + { + Assert.Equal(23434, ParserHelper.ParseDecimalWithFallbackOnOverflow("23434", 10)); + } + + [Fact] + public void ParseDecimalWithFallbackOnOverflow_Overflows_ReturnsFallback() + { + Assert.Equal(10, ParserHelper.ParseDecimalWithFallbackOnOverflow(double.MaxValue.ToString(), 10)); + } + } +}