From 87272aeac2809feee8d8dba6f6ec25a78a0a7e96 Mon Sep 17 00:00:00 2001 From: Sandesh Kumar Date: Tue, 11 Jun 2024 17:13:17 -0700 Subject: [PATCH] grok validate pattern - change to iterative approach Signed-off-by: Sandesh Kumar --- .../main/java/org/opensearch/grok/Grok.java | 91 ++++++++++++++----- 1 file changed, 66 insertions(+), 25 deletions(-) diff --git a/libs/grok/src/main/java/org/opensearch/grok/Grok.java b/libs/grok/src/main/java/org/opensearch/grok/Grok.java index 7aa3347ba4f4b..e1ce499102632 100644 --- a/libs/grok/src/main/java/org/opensearch/grok/Grok.java +++ b/libs/grok/src/main/java/org/opensearch/grok/Grok.java @@ -39,11 +39,13 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Set; import java.util.Stack; import java.util.function.Consumer; @@ -144,7 +146,7 @@ private Grok( */ private void validatePatternBank() { for (String patternName : patternBank.keySet()) { - validatePatternBank(patternName, new Stack<>()); + validatePatternBank(patternName); } } @@ -156,33 +158,72 @@ private void validatePatternBank() { * a reference to another named pattern. This method will navigate to all these named patterns and * check for a circular reference. */ - private void validatePatternBank(String patternName, Stack path) { - String pattern = patternBank.get(patternName); - boolean isSelfReference = pattern.contains("%{" + patternName + "}") || pattern.contains("%{" + patternName + ":"); - if (isSelfReference) { - throwExceptionForCircularReference(patternName, pattern); - } else if (path.contains(patternName)) { - // current pattern name is already in the path, fetch its predecessor - String prevPatternName = path.pop(); - String prevPattern = patternBank.get(prevPatternName); - throwExceptionForCircularReference(prevPatternName, prevPattern, patternName, path); - } - path.push(patternName); - for (int i = pattern.indexOf("%{"); i != -1; i = pattern.indexOf("%{", i + 1)) { - int begin = i + 2; - int syntaxEndIndex = pattern.indexOf('}', begin); - if (syntaxEndIndex == -1) { - throw new IllegalArgumentException("Malformed pattern [" + patternName + "][" + pattern + "]"); + private void validatePatternBank(String patternName) { + Stack path = new Stack<>(); + Stack stack = new Stack<>(); + Set visited = new HashSet<>(); + + stack.push(new PatternState(patternName, 0)); + + while (!stack.isEmpty()) { + PatternState currentState = stack.pop(); + String currentPatternName = currentState.patternName; + int startIndex = currentState.startIndex; + + if (path.contains(currentPatternName)) { + // Current pattern name is already in the path, indicating a circular reference. + String prevPatternName = path.pop(); + String prevPattern = patternBank.get(prevPatternName); + throwExceptionForCircularReference(prevPatternName, prevPattern, currentPatternName, path); + } + + path.push(currentPatternName); + + String pattern = patternBank.get(currentPatternName); + if (pattern.contains("%{" + currentPatternName + "}") || pattern.contains("%{" + currentPatternName + ":")) { + throwExceptionForCircularReference(currentPatternName, pattern); + } + + boolean hasDependencies = false; + for (int i = startIndex; i < pattern.length(); i = pattern.indexOf("%{", i + 1)) { + if (i == -1) { + break; + } + int begin = i + 2; + int syntaxEndIndex = pattern.indexOf('}', begin); + if (syntaxEndIndex == -1) { + throw new IllegalArgumentException("Malformed pattern [" + currentPatternName + "][" + pattern + "]"); + } + int semanticNameIndex = pattern.indexOf(':', begin); + int end = syntaxEndIndex; + if (semanticNameIndex != -1) { + end = Math.min(syntaxEndIndex, semanticNameIndex); + } + String dependsOnPattern = pattern.substring(begin, end); + + if (!visited.contains(dependsOnPattern)) { + stack.push(new PatternState(currentPatternName, i + 1)); + stack.push(new PatternState(dependsOnPattern, 0)); + hasDependencies = true; + break; + } } - int semanticNameIndex = pattern.indexOf(':', begin); - int end = syntaxEndIndex; - if (semanticNameIndex != -1) { - end = Math.min(syntaxEndIndex, semanticNameIndex); + + if (!hasDependencies) { + visited.add(currentPatternName); + path.pop(); } - String dependsOnPattern = pattern.substring(begin, end); - validatePatternBank(dependsOnPattern, path); } - path.pop(); + } + + private static class PatternState { + String patternName; + int startIndex; + + PatternState(String patternName, int startIndex) { + this.patternName = patternName; + this.startIndex = startIndex; + } } private static void throwExceptionForCircularReference(String patternName, String pattern) {