From 4a31e17507f01a1d966455fc30e25e0ea667e86b Mon Sep 17 00:00:00 2001 From: tetri <2242122+tetri@users.noreply.github.com> Date: Fri, 8 May 2026 18:06:48 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20Fix=20unbounded=20string=20split?= =?UTF-8?q?=20in=20default=20Parse=20(v2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added input length check (max 256 chars) to prevent excessive memory allocation. - Updated default parsing to use bounded Split('.', 5). - Updated custom format parsing to use bounded Split based on token count. - Bounded token split for format pattern. - Resolved CS0136 variable naming conflict from previous attempt. - Added security test cases for long inputs and excessive delimiters. --- src/CalendarVersioning/CalendarVersion.cs | 10 +++++++-- .../UnitTests/ParsingTests.cs | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/CalendarVersioning/CalendarVersion.cs b/src/CalendarVersioning/CalendarVersion.cs index cdf778f..17b9462 100644 --- a/src/CalendarVersioning/CalendarVersion.cs +++ b/src/CalendarVersioning/CalendarVersion.cs @@ -30,13 +30,17 @@ public static CalendarVersion Parse(string input, CalendarVersionFormat? format if (string.IsNullOrWhiteSpace(input)) throw new ArgumentException("Input cannot be null or empty", nameof(input)); + if (input.Length > 256) + throw new ArgumentException("Input version string is too long", nameof(input)); int year = 0, month = 0; int? day = null, minor = null; - var parts = input.Split('.'); + string[] parts; if (format == null) { + parts = input.Split('.', 5); + // Default parsing: YYYY.MM[.DD[.Minor]] if (parts.Length is < 2 or > 4) throw new FormatException($"Version string '{input}' does not match default format 'YYYY.MM[.DD[.Minor]]'"); @@ -51,7 +55,9 @@ public static CalendarVersion Parse(string input, CalendarVersionFormat? format // Parsing using the format string pattern = format.Pattern; - var tokens = pattern.Split('.'); + var tokens = pattern.Split('.', 10); + parts = input.Split('.', tokens.Length + 1); + if (tokens.Length != parts.Length) throw new FormatException($"Version string '{input}' does not match format '{pattern}'"); diff --git a/tests/CalendarVersioning.Tests/UnitTests/ParsingTests.cs b/tests/CalendarVersioning.Tests/UnitTests/ParsingTests.cs index 23173d4..c48b0bf 100644 --- a/tests/CalendarVersioning.Tests/UnitTests/ParsingTests.cs +++ b/tests/CalendarVersioning.Tests/UnitTests/ParsingTests.cs @@ -103,5 +103,27 @@ public void Parse_CustomFormat_InvalidYY_ShouldThrowFormatException() var format = new CalendarVersionFormat("YY.MM"); Assert.Throws(() => CalendarVersion.Parse("123.04", format)); } + + [Fact] + public void Parse_TooLongInput_ShouldThrowArgumentException() + { + string longInput = new string('a', 257); + Assert.Throws(() => CalendarVersion.Parse(longInput)); + } + + [Fact] + public void Parse_TooManyDots_ShouldThrowFormatException() + { + string inputWithManyDots = "2025.04.01.1.extra.dots"; // 6 parts + Assert.Throws(() => CalendarVersion.Parse(inputWithManyDots)); + } + + [Fact] + public void Parse_CustomFormat_TooManyDots_ShouldThrowFormatException() + { + var format = new CalendarVersionFormat("YYYY.MM"); + string inputWithManyDots = "2025.04.01"; // 3 parts for 2 tokens + Assert.Throws(() => CalendarVersion.Parse(inputWithManyDots, format)); + } } } \ No newline at end of file