From cd07f6d42bfd4d72754f658f5cf639bb0ea86642 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Wed, 21 Mar 2018 10:53:30 +0100 Subject: [PATCH] Xeger can cause stackoverflow and doesn't understand \d \w \s fixes gh-589, fixes gh-590 --- .../repackaged/nl/flotsam/xeger/Xeger.java | 22 +++++++-- .../nl/flotsam/xeger/XegerTest.java | 45 ++++++++++++++++++- 2 files changed, 61 insertions(+), 6 deletions(-) diff --git a/spring-cloud-contract-spec/src/main/groovy/repackaged/nl/flotsam/xeger/Xeger.java b/spring-cloud-contract-spec/src/main/groovy/repackaged/nl/flotsam/xeger/Xeger.java index a963fac9c2..81743c649f 100644 --- a/spring-cloud-contract-spec/src/main/groovy/repackaged/nl/flotsam/xeger/Xeger.java +++ b/spring-cloud-contract-spec/src/main/groovy/repackaged/nl/flotsam/xeger/Xeger.java @@ -31,11 +31,16 @@ import dk.brics.automaton.Transition; /** * An object that will generate text from a regular expression. In a way, it's the opposite of a regular expression * matcher: an instance of this class will produce text that is guaranteed to match the regular expression passed in. + * + * slight modifications by Marcin Grzejszczak */ public class Xeger { private final Automaton automaton; private Random random; + // Added by Marcin Grzejszczak + // may lead to stackoverflow when regex is unbounded + static int ITERATION_LIMIT = 200; /** * Constructs a new instance, accepting the regular expression and the randomizer. @@ -47,7 +52,12 @@ public class Xeger { public Xeger(String regex, Random random) { assert regex != null; assert random != null; - this.automaton = new RegExp(regex).toAutomaton(); + // https://stackoverflow.com/questions/1578789/how-do-i-generate-text-matching-a-regular-expression-from-a-regular-expression + String pattern = regex + .replace("\\d", "[0-9]") // Used d=Digit + .replace("\\w", "[A-Za-z0-9_]") // Used =Word + .replace("\\s", "[ \t\r\n]"); // Used s="White"Space + this.automaton = new RegExp(pattern).toAutomaton(); this.random = random; } @@ -67,11 +77,15 @@ public class Xeger { */ public String generate() { StringBuilder builder = new StringBuilder(); - generate(builder, this.automaton.getInitialState()); + int counter = 0; + generate(builder, this.automaton.getInitialState(), counter); return builder.toString(); } - private void generate(StringBuilder builder, State state) { + private void generate(StringBuilder builder, State state, int counter) { + if (counter >= ITERATION_LIMIT) { + return; + } List transitions = state.getSortedTransitions(false); if (transitions.size() == 0) { assert state.isAccept(); @@ -85,7 +99,7 @@ public class Xeger { // Moving on to next transition Transition transition = transitions.get(option - (state.isAccept() ? 1 : 0)); appendChoice(builder, transition); - generate(builder, transition.getDest()); + generate(builder, transition.getDest(), ++counter); } private void appendChoice(StringBuilder builder, Transition transition) { diff --git a/spring-cloud-contract-spec/src/test/groovy/repackaged/nl/flotsam/xeger/XegerTest.java b/spring-cloud-contract-spec/src/test/groovy/repackaged/nl/flotsam/xeger/XegerTest.java index 90f0952349..7816ff9305 100644 --- a/spring-cloud-contract-spec/src/test/groovy/repackaged/nl/flotsam/xeger/XegerTest.java +++ b/spring-cloud-contract-spec/src/test/groovy/repackaged/nl/flotsam/xeger/XegerTest.java @@ -17,12 +17,12 @@ */ package repackaged.nl.flotsam.xeger; -import org.junit.Test; - import java.util.ArrayList; import java.util.List; import java.util.Random; +import org.junit.Test; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -38,6 +38,47 @@ public class XegerTest { } } + @Test + public void shouldNotGenerateMoreThanTheLimit() { + String regex = "[ab]{5}"; + Xeger.ITERATION_LIMIT = 1; + Xeger generator = new Xeger(regex); + for (int i = 0; i < 100; i++) { + String text = generator.generate(); + assertTrue(text.length() == 1); + } + } + + @Test + public void shouldGenerateTextCorrectlyForDigitSign() { + String regex = "\\d+"; + Xeger generator = new Xeger(regex); + for (int i = 0; i < 100; i++) { + String text = generator.generate(); + assertTrue(text.matches(regex)); + } + } + + @Test + public void shouldGenerateTextCorrectlyForWordSign() { + String regex = "\\w+"; + Xeger generator = new Xeger(regex); + for (int i = 0; i < 100; i++) { + String text = generator.generate(); + assertTrue(text.matches(regex)); + } + } + + @Test + public void shouldGenerateTextCorrectlyForWhiteSpaceSign() { + String regex = "\\s+"; + Xeger generator = new Xeger(regex); + for (int i = 0; i < 100; i++) { + String text = generator.generate(); + assertTrue(text.matches(regex)); + } + } + @Test public void testRepeatableRegex() { for (int x = 0; x < 1000; x++) {