diff --git a/src/main/java/org/eu/autogex/core/AbstractAutomatonBuilder.java b/src/main/java/org/eu/autogex/core/AbstractAutomatonBuilder.java index baa8c4d6..4d0a9b4d 100644 --- a/src/main/java/org/eu/autogex/core/AbstractAutomatonBuilder.java +++ b/src/main/java/org/eu/autogex/core/AbstractAutomatonBuilder.java @@ -13,6 +13,9 @@ public abstract class AbstractAutomatonBuilder< B extends AbstractAutomatonBuilder, A extends Automaton> { + /** Maximum allowed states to prevent memory exhaustion (DoS) during manual construction. */ + private static final int MAX_STATES = 10000; + protected final Map states = new HashMap<>(); protected final Set finalStates = new HashSet<>(); protected State initialState; @@ -39,6 +42,11 @@ protected AbstractAutomatonBuilder() { * @return The current builder instance. */ public B addState(String name, boolean isFinal) { + if (states.size() >= MAX_STATES && !states.containsKey(name)) { + throw new IllegalStateException( + "Builder state limit exceeded (Security: DoS prevention)."); + } + State state = new State(name, isFinal); states.put(name, state); if (isFinal) { diff --git a/src/test/java/org/eu/autogex/core/AbstractAutomatonBuilderSecurityTest.java b/src/test/java/org/eu/autogex/core/AbstractAutomatonBuilderSecurityTest.java new file mode 100644 index 00000000..0ec38b09 --- /dev/null +++ b/src/test/java/org/eu/autogex/core/AbstractAutomatonBuilderSecurityTest.java @@ -0,0 +1,36 @@ +package org.eu.autogex.core; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class AbstractAutomatonBuilderSecurityTest { + + static class TestBuilder extends AbstractAutomatonBuilder { + @Override + protected TestBuilder self() { + return this; + } + + @Override + public Automaton build() { + return null; + } + } + + @Test + void testStateExplosionDoSPreventionInBuilder() { + TestBuilder builder = new TestBuilder(); + for (int i = 0; i < 10000; i++) { + builder.addState("q" + i, false); + } + + IllegalStateException exception = + assertThrows( + IllegalStateException.class, () -> builder.addState("overflow", false)); + assertTrue( + exception.getMessage().contains("Builder state limit exceeded"), + "Should detect state explosion to prevent DoS during manual construction"); + } +}