From 2ce120c51b2256f43129493834d37546afc3352c Mon Sep 17 00:00:00 2001
From: "google-labs-jules[bot]"
<161369871+google-labs-jules[bot]@users.noreply.github.com>
Date: Tue, 31 Mar 2026 21:02:16 +0000
Subject: [PATCH] fix: enforce maximum expression length in MathParser
Enforce a maximum length of 100,000 characters for mathematical
expressions in `ArenaMathParser.Tokenize` to prevent Denial of Service
attacks via unbounded memory allocation. Added a test suite to verify
the fix.
Co-authored-by: myarichuk <1473701+myarichuk@users.noreply.github.com>
---
samples/SimpleMathParser/MathParser.cs | 9 +++-
.../SimpleMathParser.Tests.csproj | 30 +++++++++++++
.../TokenizationTests.cs | 44 +++++++++++++++++++
3 files changed, 82 insertions(+), 1 deletion(-)
create mode 100644 tests/SimpleMathParser.Tests/SimpleMathParser.Tests.csproj
create mode 100644 tests/SimpleMathParser.Tests/TokenizationTests.cs
diff --git a/samples/SimpleMathParser/MathParser.cs b/samples/SimpleMathParser/MathParser.cs
index adb347b..10a93ab 100644
--- a/samples/SimpleMathParser/MathParser.cs
+++ b/samples/SimpleMathParser/MathParser.cs
@@ -9,15 +9,22 @@ namespace SimpleMathParser;
///
public class ArenaMathParser
{
+ private const int MaxExpressionLength = 100_000;
+
///
/// Tokenizes the given math expression into a list of tokens allocated in the provided arena.
///
/// The math expression as a span of characters.
/// The arena allocator to use for memory allocations.
/// A list of structures.
- /// Thrown when an unknown character is encountered.
+ /// Thrown when an unknown character is encountered or if the expression exceeds the maximum length.
public static ArenaList Tokenize(ReadOnlySpan input, ArenaAllocator arena)
{
+ if (input.Length > MaxExpressionLength)
+ {
+ throw new SyntaxErrorException("Expression too long.");
+ }
+
var tokens = new ArenaList(arena);
int i = 0;
diff --git a/tests/SimpleMathParser.Tests/SimpleMathParser.Tests.csproj b/tests/SimpleMathParser.Tests/SimpleMathParser.Tests.csproj
new file mode 100644
index 0000000..7dd86d9
--- /dev/null
+++ b/tests/SimpleMathParser.Tests/SimpleMathParser.Tests.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net8.0
+ enable
+ enable
+ false
+ true
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/SimpleMathParser.Tests/TokenizationTests.cs b/tests/SimpleMathParser.Tests/TokenizationTests.cs
new file mode 100644
index 0000000..ea461da
--- /dev/null
+++ b/tests/SimpleMathParser.Tests/TokenizationTests.cs
@@ -0,0 +1,44 @@
+using SharpArena.Allocators;
+using SimpleMathParser;
+using Xunit;
+using FluentAssertions;
+
+namespace SimpleMathParser.Tests;
+
+public class TokenizationTests : IDisposable
+{
+ private readonly ArenaAllocator _arena = new(1024);
+
+ [Fact]
+ public void Tokenize_WhenInputExceedsMaxLength_ShouldThrowSyntaxErrorException()
+ {
+ // Arrange
+ // We'll use a large string to exceed the planned 100,000 character limit.
+ string input = new string('1', 100_001);
+
+ // Act
+ Action act = () => ArenaMathParser.Tokenize(input.AsSpan(), _arena);
+
+ // Assert
+ act.Should().Throw()
+ .WithMessage("Expression too long.");
+ }
+
+ [Fact]
+ public void Tokenize_WhenInputIsWithinLimit_ShouldSucceed()
+ {
+ // Arrange
+ string input = "1 + 2";
+
+ // Act
+ var tokens = ArenaMathParser.Tokenize(input.AsSpan(), _arena);
+
+ // Assert
+ tokens.Length.Should().Be(3);
+ }
+
+ public void Dispose()
+ {
+ _arena.Dispose();
+ }
+}