Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,19 @@
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.SaxonApiException;

import java.util.regex.Pattern;

public class XQueryImplUtil {

private static final Pattern XML_CHARACTER_REFERENCES_PATTERN = Pattern.compile("['\"&<>]");
/**
* Single Saxon Processor instance. Processor is thread-safe and expensive to construct
* (it initialises the Saxon Configuration and performs a license check). One instance
* per JVM is the Saxon-recommended pattern.
*/
private static final Processor PROCESSOR = new Processor(false);
Comment thread
yesamer marked this conversation as resolved.

/**
* Single XQueryCompiler instance. XQueryCompiler is thread-safe and reusable.
*/
private static final XQueryCompiler COMPILER = PROCESSOR.newXQueryCompiler();
Comment thread
yesamer marked this conversation as resolved.

private XQueryImplUtil() {
// Util class with static methods only.
Expand All @@ -50,9 +58,7 @@ public static String executeReplaceFunction(String input, String pattern, String

static <T> T evaluateXQueryExpression(String expression, Class<T> expectedTypeResult) {
try {
Processor processor = new Processor(false);
XQueryCompiler compiler = processor.newXQueryCompiler();
XQueryExecutable executable = compiler.compile(expression);
XQueryExecutable executable = COMPILER.compile(expression);
Comment thread
yesamer marked this conversation as resolved.
XQueryEvaluator queryEvaluator = executable.load();
XdmItem resultItem = queryEvaluator.evaluateSingle();

Expand All @@ -66,22 +72,41 @@ static <T> T evaluateXQueryExpression(String expression, Class<T> expectedTypeRe
} catch (SaxonApiException e) {
throw new IllegalArgumentException(e);
}
}
}

/**
* It replaces all the XML Character References (&, ", ', <, >) in a given input string with their "escaping" characters.
* This is required to run XPath functions containing XML Character References.
* @param input A string input representing one of the parameter of managed functions
* @return A sanitized string
* Escapes the five XML special characters (&amp; &quot; &apos; &lt; &gt;) in a single
* pass over the string, so they are safe to embed as XQuery string literals.
* Returns {@code null} unchanged; returns the original reference when no escaping is needed.
*
* @param input A string parameter of a managed XQuery function
* @return The escaped string, or the original if no special characters were present
*/
static String escapeXmlCharactersReferencesForXPath(String input) {
Comment thread
yesamer marked this conversation as resolved.
if (input != null && XML_CHARACTER_REFERENCES_PATTERN.matcher(input).find()) {
input = input.contains("&") ? input.replace("&", "&amp;") : input;
input = input.contains("\"") ? input.replace("\"", "&quot;") : input;
input = input.contains("'") ? input.replace("'", "&apos;") : input;
input = input.contains("<") ? input.replace("<", "&lt;") : input;
input = input.contains(">") ? input.replace(">", "&gt;") : input;
if (input == null) {
return null;
}
StringBuilder sb = null;
Comment thread
yesamer marked this conversation as resolved.
for (int i = 0; i < input.length(); i++) {
char ch = input.charAt(i);
String replacement = switch (ch) {
case '&' -> "&amp;";
case '"' -> "&quot;";
case '\'' -> "&apos;";
case '<' -> "&lt;";
case '>' -> "&gt;";
default -> null;
};
if (replacement != null) {
if (sb == null) {
sb = new StringBuilder(input.length() + 16);
sb.append(input, 0, i);
}
sb.append(replacement);
} else if (sb != null) {
sb.append(ch);
}
}
return input;
return sb != null ? sb.toString() : input;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ private static Object[][] executeMatchesFunctionTestData() {
{ "test", "^test", "i", true },
{ "fo\nbar", "o.b", null, false },
{ "TEST", "test", "i", true },
// flags
{ "test", "^test", "", true }, // flags = "" explicitly
{ "FO\nBAR", "fo.bar", "si", true }, // dotall + case-insensitive
{ "hello\nworld", "^WORLD", "mi", true }, // multiline + case-insensitive
// XML special characters in input — exercises the escape→embed→evaluate pipeline
{ "it's", "it.s", "", true }, // single quote in input
{ "say \"hi\"", "say .hi.", "", true }, // double quote in input
{ "a&b", "a.b", "", true }, // ampersand in input
{ "a<b", "a.b", "", true }, // less-than in input
{ "a>b", "a.b", "", true }, // greater-than in input
// XML special characters in pattern
{ "<tag>", "<tag>", "", true }, // angle brackets in pattern
};
}

Expand Down Expand Up @@ -66,6 +78,16 @@ private static Object[][] executeReplaceFunctionTestData() {
return new Object[][] {
{ "testString", "^test", "ttt", "", "tttString" },
{ "fo\nbar", "o.b", "ttt", "s", "ftttar" },
// flags
{ "FO\nBAR", "fo.bar", "X", "si", "X" }, // dotall + case-insensitive
// XML special characters in input — exercises the escape→embed→evaluate pipeline
{ "a&b", "a.b", "X", "", "X" }, // ampersand in input
{ "it's", "it.s", "X", "", "X" }, // single quote in input
// XML special characters in replacement
{ "hello", "hello", "a&b", "", "a&b" }, // ampersand in replacement
{ "hello", "hello", "it's", "", "it's" }, // single quote in replacement
// backreference in replacement
{ "hello", "(h)", "$1$1", "", "hhello" }, // $1 backreference
};
}

Expand Down
2 changes: 1 addition & 1 deletion kie-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@
<version.net.byte-buddy>1.17.6</version.net.byte-buddy>
<version.net.java.dev.glazedlists>1.8.0</version.net.java.dev.glazedlists>
<version.net.minidev.jsonsmart>2.4.10</version.net.minidev.jsonsmart>
<version.net.sf.saxon.Saxon-HE>12.7</version.net.sf.saxon.Saxon-HE>
<version.net.sf.saxon.Saxon-HE>12.10</version.net.sf.saxon.Saxon-HE>
<version.net.thisptr.jackson-jq>1.0.0-preview.20240207</version.net.thisptr.jackson-jq>
<version.org.antlr>3.5.2</version.org.antlr>
<version.org.antlr.ST4>4.0.7</version.org.antlr.ST4>
Expand Down
Loading