This is an automated email from the ASF dual-hosted git repository. rgoers pushed a commit to branch LOG4J2-3301 in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 78075d411fee0693f02256dd0d343ed1327602cc Author: Ralph Goers <[email protected]> AuthorDate: Fri Dec 31 15:48:25 2021 -0700 LOG4J2-3301 - Prevent recursion --- .../logging/log4j/core/util/OptionConverter.java | 73 ++++++++++++++++++++-- .../log4j/core/util/OptionConverterTest.java | 34 ++++++++++ 2 files changed, 103 insertions(+), 4 deletions(-) diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java index c1f0b13..bd31fd3 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/OptionConverter.java @@ -17,13 +17,16 @@ package org.apache.logging.log4j.core.util; import java.io.InterruptedIOException; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.Properties; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.core.lookup.StrSubstitutor; import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.util.Strings; /** * A convenience class to convert property values to specific types. @@ -32,6 +35,10 @@ public final class OptionConverter { private static final Logger LOGGER = StatusLogger.getLogger(); + private static final String DELIM_START = "${"; + private static final char DELIM_STOP = '}'; + private static final int DELIM_START_LEN = 2; + private static final int DELIM_STOP_LEN = 1; private static final int ONE_K = 1024; /** @@ -162,9 +169,10 @@ public final class OptionConverter { if (hashIndex == -1) { if("NULL".equalsIgnoreCase(value)) { return null; + } else { + // no class name specified : use standard Level class + return Level.toLevel(value, defaultValue); } - // no class name specified : use standard Level class - return Level.toLevel(value, defaultValue); } Level result = defaultValue; @@ -341,6 +349,63 @@ public final class OptionConverter { */ public static String substVars(final String val, final Properties props) throws IllegalArgumentException { - return StrSubstitutor.replace(val, props); + return substVars(val, props, new ArrayList<>()); + } + + private static String substVars(final String val, final Properties props, List<String> keys) + throws IllegalArgumentException { + + final StringBuilder sbuf = new StringBuilder(); + + int i = 0; + int j; + int k; + + while (true) { + j = val.indexOf(DELIM_START, i); + if (j == -1) { + // no more variables + if (i == 0) { // this is a simple string + return val; + } + // add the tail string which contails no variables and return the result. + sbuf.append(val.substring(i, val.length())); + return sbuf.toString(); + } + sbuf.append(val.substring(i, j)); + k = val.indexOf(DELIM_STOP, j); + if (k == -1) { + throw new IllegalArgumentException(Strings.dquote(val) + + " has no closing brace. Opening brace at position " + j + + '.'); + } + j += DELIM_START_LEN; + final String key = val.substring(j, k); + // first try in System properties + String replacement = PropertiesUtil.getProperties().getStringProperty(key, null); + // then try props parameter + if (replacement == null && props != null) { + replacement = props.getProperty(key); + } + + if (replacement != null) { + + // Do variable substitution on the replacement string + // such that we can solve "Hello ${x2}" as "Hello p1" + // the where the properties are + // x1=p1 + // x2=${x1} + if (!keys.contains(key)) { + List<String> usedKeys = new ArrayList<>(keys); + usedKeys.add(key); + final String recursiveReplacement = substVars(replacement, props, usedKeys); + sbuf.append(recursiveReplacement); + } else { + sbuf.append(replacement); + } + + } + i = k + DELIM_STOP_LEN; + } } } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/OptionConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/OptionConverterTest.java index d0d7f90..8725af0 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/util/OptionConverterTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/util/OptionConverterTest.java @@ -31,6 +31,40 @@ public class OptionConverterTest { public void testSubstVars() { Properties props = new Properties(); props.setProperty("key", "${key}"); + props.setProperty("testKey", "Log4j"); assertEquals("Value of key is ${key}.", OptionConverter.substVars("Value of key is ${key}.", props)); + assertEquals("Value of key is .", OptionConverter.substVars("Value of key is ${key2}.", props)); + assertEquals("Value of testKey:testKey is Log4j:Log4j", + OptionConverter.substVars("Value of testKey:testKey is ${testKey}:${testKey}", props)); + } + + @Test + public void testRecursion() { + Properties props = new RecursiveProperties(); + props.setProperty("name", "Neo"); + props.setProperty("greeting", "Hello ${name}"); + + String s = props.getProperty("greeting"); + System.out.println("greeting = '"+s+"'"); + } + + private static class RecursiveProperties extends Properties { + @Override + public String getProperty(String key) + { + System.out.println("getProperty for "+key); + try + { + String val = super.getProperty(key); + // The following call works for log4j 2.17.0 and causes StackOverflowError for 2.17.1 + // This is because substVars change implementation in 2.17.1 to call StrSubstitutor.replace(val, props) + // which calls props.getProperty() for EVERY property making it recursive + return OptionConverter.substVars(val, this); + } + catch (Exception e) + { + return super.getProperty(key); + } + } } }
