Skip to content

Commit 0297671

Browse files
committed
Move the XmlStringLookup secure toggle to a system property
1 parent 85f345e commit 0297671

File tree

5 files changed

+39
-47
lines changed

5 files changed

+39
-47
lines changed

pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@
9292
<artifactId>junit-jupiter</artifactId>
9393
<scope>test</scope>
9494
</dependency>
95+
<dependency>
96+
<groupId>org.junit-pioneer</groupId>
97+
<artifactId>junit-pioneer</artifactId>
98+
<scope>test</scope>
99+
</dependency>
95100
<dependency>
96101
<!-- Java 21 support, revisit for Mockito 5 -->
97102
<groupId>net.bytebuddy</groupId>

src/main/java/org/apache/commons/text/lookup/StringLookupFactory.java

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,19 +1618,17 @@ public StringLookup xmlEncoderStringLookup() {
16181618
* if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
16191619
* </p>
16201620
* <p>
1621-
* We looks up values in an XML document in the format {@code "[secure=(true|false):]DocumentPath:XPath"}.
1621+
* We looks up values in an XML document in the format {@code "DocumentPath:XPath"}.
16221622
* </p>
16231623
* <p>
16241624
* For example:
16251625
* </p>
16261626
* <ul>
16271627
* <li>{@code "com/domain/document.xml:/path/to/node"}</li>
1628-
* <li>{@code "secure=false:com/domain/document.xml:/path/to/node"}</li>
1629-
* <li>{@code "secure=true:com/domain/document.xml:/path/to/node"}</li>
16301628
* </ul>
16311629
* <p>
1632-
* Secure processing is enabled by default. The secure boolean String parsing follows the syntax defined by {@link Boolean#parseBoolean(String)}. The secure
1633-
* value in the key overrides instance settings given in the constructor.
1630+
* Secure processing is enabled by default and can be overridden with the system property {@code "XmlStringLookup.secure"} set to {@code false}. The secure
1631+
* boolean String parsing follows the syntax defined by {@link Boolean#parseBoolean(String)}.
16341632
* </p>
16351633
* <p>
16361634
* Using a {@link StringLookup} from the {@link StringLookupFactory}:
@@ -1664,19 +1662,17 @@ public StringLookup xmlStringLookup() {
16641662
* if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
16651663
* </p>
16661664
* <p>
1667-
* We looks up values in an XML document in the format {@code "[secure=(true|false):]DocumentPath:XPath"}.
1665+
* We looks up values in an XML document in the format {@code "]DocumentPath:XPath"}.
16681666
* </p>
16691667
* <p>
16701668
* For example:
16711669
* </p>
16721670
* <ul>
16731671
* <li>{@code "com/domain/document.xml:/path/to/node"}</li>
1674-
* <li>{@code "secure=false:com/domain/document.xml:/path/to/node"}</li>
1675-
* <li>{@code "secure=true:com/domain/document.xml:/path/to/node"}</li>
16761672
* </ul>
16771673
* <p>
1678-
* Secure processing is enabled by default. The secure boolean String parsing follows the syntax defined by {@link Boolean#parseBoolean(String)}. The secure
1679-
* value in the key overrides instance settings given in the constructor.
1674+
* Secure processing is enabled by default and can be overridden with the system property {@code "XmlStringLookup.secure"} set to {@code false}. The secure
1675+
* boolean String parsing follows the syntax defined by {@link Boolean#parseBoolean(String)}.
16801676
* </p>
16811677
* <p>
16821678
* Using a {@link StringLookup} from the {@link StringLookupFactory}:
@@ -1713,19 +1709,17 @@ public StringLookup xmlStringLookup(final Map<String, Boolean> factoryFeatures)
17131709
* if a lookup causes causes a path to resolve outside of these fences. Otherwise, the result is unfenced to preserved behavior from previous versions.
17141710
* </p>
17151711
* <p>
1716-
* We looks up values in an XML document in the format {@code "[secure=(true|false):]DocumentPath:XPath"}.
1712+
* We looks up values in an XML document in the format {@code "DocumentPath:XPath"}.
17171713
* </p>
17181714
* <p>
17191715
* For example:
17201716
* </p>
17211717
* <ul>
17221718
* <li>{@code "com/domain/document.xml:/path/to/node"}</li>
1723-
* <li>{@code "secure=false:com/domain/document.xml:/path/to/node"}</li>
1724-
* <li>{@code "secure=true:com/domain/document.xml:/path/to/node"}</li>
17251719
* </ul>
17261720
* <p>
1727-
* Secure processing is enabled by default. The secure boolean String parsing follows the syntax defined by {@link Boolean#parseBoolean(String)}. The secure
1728-
* value in the key overrides instance settings given in the constructor.
1721+
* Secure processing is enabled by default and can be overridden with the system property {@code "XmlStringLookup.secure"} set to {@code false}. The secure
1722+
* boolean String parsing follows the syntax defined by {@link Boolean#parseBoolean(String)}.
17291723
* </p>
17301724
* <p>
17311725
* Using a {@link StringLookup} from the {@link StringLookupFactory} fenced by the current directory ({@code Paths.get("")}):

src/main/java/org/apache/commons/text/lookup/XmlStringLookup.java

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -30,36 +30,30 @@
3030
import javax.xml.xpath.XPathFactory;
3131

3232
import org.apache.commons.lang3.StringUtils;
33+
import org.apache.commons.lang3.SystemProperties;
3334
import org.w3c.dom.Document;
3435

3536
/**
36-
* Looks up values in an XML document in the format {@code "[secure=(true|false):]DocumentPath:XPath"}.
37+
* Looks up values in an XML document in the format {@code "DocumentPath:XPath"}.
3738
* <p>
3839
* For example:
3940
* </p>
4041
* <ul>
4142
* <li>{@code "com/domain/document.xml:/path/to/node"}</li>
42-
* <li>{@code "secure=false:com/domain/document.xml:/path/to/node"}</li>
43-
* <li>{@code "secure=true:com/domain/document.xml:/path/to/node"}</li>
4443
* </ul>
4544
* <p>
46-
* Secure processing is enabled by default. The secure boolean String parsing follows the syntax defined by {@link Boolean#parseBoolean(String)}. The secure
47-
* value in the key overrides instance settings given in the constructor.
45+
* Secure processing is enabled by default and can be overridden with the system property {@code "XmlStringLookup.secure"} set to {@code false}. The secure
46+
* boolean String parsing follows the syntax defined by {@link Boolean#parseBoolean(String)}.
4847
* </p>
4948
*
5049
* @since 1.5
5150
*/
5251
final class XmlStringLookup extends AbstractPathFencedLookup {
5352

5453
/**
55-
* Minimum number of key parts.
54+
* The number of key parts.
5655
*/
57-
private static final int KEY_PARTS_MIN = 2;
58-
59-
/**
60-
* Minimum number of key parts.
61-
*/
62-
private static final int KEY_PARTS_MAX = 3;
56+
private static final int KEY_PARTS_LEN = 2;
6357

6458
/**
6559
* Defines default XPath factory features.
@@ -76,11 +70,16 @@ final class XmlStringLookup extends AbstractPathFencedLookup {
7670
DEFAULT_XML_FEATURES = new HashMap<>(1);
7771
DEFAULT_XML_FEATURES.put(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE);
7872
}
73+
7974
/**
8075
* Defines the singleton for this class with secure processing enabled.
8176
*/
8277
static final XmlStringLookup INSTANCE = new XmlStringLookup(DEFAULT_XML_FEATURES, DEFAULT_XPATH_FEATURES, (Path[]) null);
8378

79+
private static boolean isSecure() {
80+
return SystemProperties.getBoolean(XmlStringLookup.class, "secure", () -> true);
81+
}
82+
8483
/**
8584
* Defines XPath factory features.
8685
*/
@@ -106,14 +105,12 @@ final class XmlStringLookup extends AbstractPathFencedLookup {
106105
}
107106

108107
/**
109-
* Looks up a value for the key in the format {@code "[secure=(true|false):]DocumentPath:XPath"}.
108+
* Looks up a value for the key in the format {@code "DocumentPath:XPath"}.
110109
* <p>
111110
* For example:
112111
* </p>
113112
* <ul>
114113
* <li>{@code "com/domain/document.xml:/path/to/node"}</li>
115-
* <li>{@code "secure=false:com/domain/document.xml:/path/to/node"}</li>
116-
* <li>{@code "secure=true:com/domain/document.xml:/path/to/node"}</li>
117114
* </ul>
118115
* <p>
119116
* Secure processing is enabled by default. The secure boolean String parsing follows the syntax defined by {@link Boolean#parseBoolean(String)}. The secure
@@ -130,12 +127,11 @@ public String lookup(final String key) {
130127
}
131128
final String[] keys = key.split(SPLIT_STR);
132129
final int keyLen = keys.length;
133-
if (keyLen != KEY_PARTS_MIN && keyLen != KEY_PARTS_MAX) {
134-
throw IllegalArgumentExceptions.format("Bad XML key format '%s'; the expected format is [secure=(true|false):]DocumentPath:XPath.", key);
130+
if (keyLen != KEY_PARTS_LEN) {
131+
throw IllegalArgumentExceptions.format("Bad XML key format '%s'; the expected format is 'DocumentPath:XPath'.", key);
135132
}
136-
final boolean isKeySecure = keyLen == KEY_PARTS_MAX;
137-
final Boolean secure = isKeySecure ? parseSecureKey(keys, key) : null;
138-
final String documentPath = isKeySecure ? keys[1] : keys[0];
133+
final Boolean secure = isSecure();
134+
final String documentPath = keys[0];
139135
final String xpath = StringUtils.substringAfterLast(key, SPLIT_CH);
140136
final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
141137
try {
@@ -160,12 +156,4 @@ public String lookup(final String key) {
160156
throw new IllegalArgumentException(e);
161157
}
162158
}
163-
164-
private Boolean parseSecureKey(final String[] args, final String key) {
165-
final String[] secParts = args[0].split("=");
166-
if (secParts.length != 2 && !Objects.equals(secParts[0], "secure")) {
167-
throw IllegalArgumentExceptions.format("Bad XML key format '%s'; the expected format is [secure=(true|false):]DocumentPath:XPath.", key);
168-
}
169-
return Boolean.valueOf(secParts[1]);
170-
}
171159
}

src/test/java/org/apache/commons/text/lookup/StringLookupFactoryTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import javax.xml.XMLConstants;
3333

3434
import org.junit.jupiter.api.Test;
35+
import org.junitpioneer.jupiter.SetSystemProperty;
3536

3637
/**
3738
* Tests {@link StringLookupFactory}.
@@ -272,6 +273,7 @@ void testXmlStringLookupExternalEntityOff() {
272273
}
273274

274275
@Test
276+
@SetSystemProperty(key = "XmlStringLookup.secure", value = "false")
275277
void testXmlStringLookupExternalEntityOn() {
276278
final String key = XmlStringLookupTest.DOC_DIR + "document-entity-ref.xml:/document/content";
277279
assertEquals(XmlStringLookupTest.DATA, StringLookupFactory.INSTANCE.xmlStringLookup(XmlStringLookupTest.EMPTY_MAP).apply(key).trim());

src/test/java/org/apache/commons/text/lookup/XmlStringLookupTest.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.apache.commons.lang3.StringUtils;
3636
import org.apache.commons.text.StringSubstitutor;
3737
import org.junit.jupiter.api.Test;
38+
import org.junitpioneer.jupiter.SetSystemProperty;
3839

3940
/**
4041
* Tests {@link XmlStringLookup}.
@@ -68,6 +69,7 @@ void testExternalEntityOff() {
6869
}
6970

7071
@Test
72+
@SetSystemProperty(key = "XmlStringLookup.secure", value = "false")
7173
void testExternalEntityOn() {
7274
final String key = DOC_DIR + "document-entity-ref.xml:/document/content";
7375
assertEquals(DATA, new XmlStringLookup(EMPTY_MAP, EMPTY_MAP).apply(key).trim());
@@ -81,9 +83,10 @@ void testInterpolatorExternalEntityOff() {
8183
}
8284

8385
@Test
86+
@SetSystemProperty(key = "XmlStringLookup.secure", value = "false")
8487
void testInterpolatorExternalEntityOffOverride() {
8588
final StringSubstitutor stringSubstitutor = StringSubstitutor.createInterpolator();
86-
assertEquals(DATA, stringSubstitutor.replace("${xml:secure=false:" + DOC_DIR + "document-entity-ref.xml:/document/content}").trim());
89+
assertEquals(DATA, stringSubstitutor.replace("${xml:" + DOC_DIR + "document-entity-ref.xml:/document/content}").trim());
8790
}
8891

8992
@Test
@@ -93,18 +96,18 @@ void testInterpolatorExternalEntityOn() {
9396
}
9497

9598
@Test
99+
@SetSystemProperty(key = "XmlStringLookup.secure", value = "true")
96100
void testInterpolatorExternalEntityOnOverride() {
97101
final StringSubstitutor stringSubstitutor = StringSubstitutor.createInterpolator();
98102
assertThrows(IllegalArgumentException.class,
99-
() -> stringSubstitutor.replace("${xml:secure=true:" + DOC_DIR + "document-entity-ref.xml:/document/content}"));
103+
() -> stringSubstitutor.replace("${xml:" + DOC_DIR + "document-entity-ref.xml:/document/content}"));
100104
}
101105

102106
@Test
103107
void testInterpolatorSecureOnBla() {
104108
final StringSubstitutor stringSubstitutor = StringSubstitutor.createInterpolator();
105109
assertThrows(IllegalArgumentException.class, () -> stringSubstitutor.replace("${xml:" + DOC_DIR + "bla.xml:/document/content}"));
106-
assertThrows(IllegalArgumentException.class, () -> stringSubstitutor.replace("${xml:secure=true:" + DOC_DIR + "bla.xml:/document/content}"));
107-
// Using secure=false allows the BLA to occur.
110+
// Using XmlStringLookup.secure=false allows the BLA to occur.
108111
}
109112

110113
@Test

0 commit comments

Comments
 (0)