3434import java .io .InputStreamReader ;
3535import java .io .UncheckedIOException ;
3636import java .nio .charset .StandardCharsets ;
37+ import java .util .ArrayList ;
3738import java .util .HashMap ;
3839import java .util .Iterator ;
40+ import java .util .List ;
3941import java .util .Locale ;
4042import java .util .Map ;
4143import java .util .Collections ;
@@ -74,8 +76,6 @@ public final class Grok {
7476 private final Map <String , String > patternBank ;
7577 private final boolean namedCaptures ;
7678 private final Regex compiledExpression ;
77- private final String expression ;
78-
7979
8080 public Grok (Map <String , String > patternBank , String grokPattern ) {
8181 this (patternBank , grokPattern , true );
@@ -86,11 +86,59 @@ public Grok(Map<String, String> patternBank, String grokPattern) {
8686 this .patternBank = patternBank ;
8787 this .namedCaptures = namedCaptures ;
8888
89- this .expression = toRegex (grokPattern );
89+ for (Map .Entry <String , String > entry : patternBank .entrySet ()) {
90+ String name = entry .getKey ();
91+ String pattern = entry .getValue ();
92+ forbidCircularReferences (name , new ArrayList <>(), pattern );
93+ }
94+
95+ String expression = toRegex (grokPattern );
9096 byte [] expressionBytes = expression .getBytes (StandardCharsets .UTF_8 );
9197 this .compiledExpression = new Regex (expressionBytes , 0 , expressionBytes .length , Option .DEFAULT , UTF8Encoding .INSTANCE );
9298 }
9399
100+ /**
101+ * Checks whether patterns reference each other in a circular manner and if so fail with an exception
102+ *
103+ * In a pattern, anything between <code>%{</code> and <code>}</code> or <code>:</code> is considered
104+ * a reference to another named pattern. This method will navigate to all these named patterns and
105+ * check for a circular reference.
106+ */
107+ private void forbidCircularReferences (String patternName , List <String > path , String pattern ) {
108+ if (pattern .contains ("%{" + patternName + "}" ) || pattern .contains ("%{" + patternName + ":" )) {
109+ String message ;
110+ if (path .isEmpty ()) {
111+ message = "circular reference in pattern [" + patternName + "][" + pattern + "]" ;
112+ } else {
113+ message = "circular reference in pattern [" + path .remove (path .size () - 1 ) + "][" + pattern +
114+ "] back to pattern [" + patternName + "]" ;
115+ // add rest of the path:
116+ if (path .isEmpty () == false ) {
117+ message += " via patterns [" + String .join ("=>" , path ) + "]" ;
118+ }
119+ }
120+ throw new IllegalArgumentException (message );
121+ }
122+
123+ for (int i = pattern .indexOf ("%{" ); i != -1 ; i = pattern .indexOf ("%{" , i + 1 )) {
124+ int begin = i + 2 ;
125+ int brackedIndex = pattern .indexOf ('}' , begin );
126+ int columnIndex = pattern .indexOf (':' , begin );
127+ int end ;
128+ if (brackedIndex != -1 && columnIndex == -1 ) {
129+ end = brackedIndex ;
130+ } else if (columnIndex != -1 && brackedIndex == -1 ) {
131+ end = columnIndex ;
132+ } else if (brackedIndex != -1 && columnIndex != -1 ) {
133+ end = Math .min (brackedIndex , columnIndex );
134+ } else {
135+ throw new IllegalArgumentException ("pattern [" + pattern + "] has circular references to other pattern definitions" );
136+ }
137+ String otherPatternName = pattern .substring (begin , end );
138+ path .add (otherPatternName );
139+ forbidCircularReferences (patternName , path , patternBank .get (otherPatternName ));
140+ }
141+ }
94142
95143 public String groupMatch (String name , Region region , String pattern ) {
96144 try {
@@ -125,10 +173,12 @@ public String toRegex(String grokPattern) {
125173 String patternName = groupMatch (PATTERN_GROUP , region , grokPattern );
126174
127175 String pattern = patternBank .get (patternName );
128-
129176 if (pattern == null ) {
130177 throw new IllegalArgumentException ("Unable to find pattern [" + patternName + "] in Grok's pattern dictionary" );
131178 }
179+ if (pattern .contains ("%{" + patternName + "}" ) || pattern .contains ("%{" + patternName + ":" )) {
180+ throw new IllegalArgumentException ("circular reference in pattern back [" + patternName + "]" );
181+ }
132182
133183 String grokPart ;
134184 if (namedCaptures && subName != null ) {
0 commit comments