This is an automated email from the ASF dual-hosted git repository.

andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/main by this push:
     new 4694c3e93a GH-3268: Permit injecting UNDEF in ParameterizedSparqlString
4694c3e93a is described below

commit 4694c3e93af00711cd7b7c221bdd2ea4c0cbb70b
Author: Rob Vesse <[email protected]>
AuthorDate: Mon Jun 23 14:22:23 2025 +0100

    GH-3268: Permit injecting UNDEF in ParameterizedSparqlString
    
    This commit adds the ability for ParameterizedSparqlString to support
    injecting the UNDEF keyword into VALUES clauses as needed.  A new
    undef() method is added to the class that provides access to a special
    instance level RDFNode that is used to indicate that the caller wants to
    inject UNDEF.
    
    Also cleans up related tests to remove defunct debugging code
---
 .../jena/query/ParameterizedSparqlString.java      | 48 ++++++++++---
 .../jena/query/TestParameterizedSparqlString.java  | 81 +++++++++++-----------
 2 files changed, 79 insertions(+), 50 deletions(-)

diff --git 
a/jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java 
b/jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java
index 93cc4d3727..a1ee3ee681 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/query/ParameterizedSparqlString.java
@@ -120,7 +120,7 @@ import org.apache.jena.update.UpdateRequest;
  * While this class was in part designed to prevent SPARQL injection it is by 
no
  * means foolproof because it works purely at the textual level. The current
  * version of the code addresses some possible attack vectors that the
- * developers have identified but we do not claim to be sufficiently devious to
+ * developers have identified, but we do not claim to be sufficiently devious 
to
  * have thought of and prevented every possible attack vector.
  * </p>
  * <p>
@@ -134,7 +134,10 @@ import org.apache.jena.update.UpdateRequest;
  */
 public class ParameterizedSparqlString implements PrefixMapping {
 
+    private static final Node UNDEF = NodeFactory.createURI("urn:x-arq:undef");
+
     private Model model = ModelFactory.createDefaultModel();
+    private final RDFNode undef = model.wrapAsResource(UNDEF);
 
     private StringBuilder cmd = new StringBuilder();
     private String baseUri;
@@ -626,6 +629,16 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
         return this.baseUri;
     }
 
+    /**
+     * Gets a special {@link RDFNode} value that can be used with the various 
{@link #setValues(Map)}-like methods to
+     * inject the {@code UNDEF} keyword into a parameterised {@code VALUES} 
clause.
+     *
+     * @return Special undefined node value
+     */
+    public RDFNode undef() {
+        return this.undef;
+    }
+
     /**
      * Helper method which does the validation of the parameters
      *
@@ -1290,7 +1303,7 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
         Pattern p = Pattern.compile("\"[?$]" + var + "\"|'[?$]" + var + "'");
 
         if (p.matcher(command).find() && n.isLiteral()) {
-            throw new ARQException("Command string is vunerable to injection 
attack, variable ?" + var
+            throw new ARQException("Command string is vulnerable to injection 
attack, variable ?" + var
                     + " appears surrounded directly by quotes and is bound to 
a literal which provides a SPARQL injection attack vector");
         }
 
@@ -1305,7 +1318,7 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
 
             if (n.isLiteral()) {
                 if (delims.isInsideLiteral(posMatch.start(1), 
posMatch.end(1))) {
-                    throw new ARQException("Command string is vunerable to 
injection attack, variable ?" + var
+                    throw new ARQException("Command string is vulnerable to 
injection attack, variable ?" + var
                             + " appears inside of a literal and is bound to a 
literal which provides a SPARQL injection attack vector");
                 }
             }
@@ -1336,7 +1349,7 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
         // Check each occurrence of the variable for safety
         if (n.isLiteral()) {
             if (delims.isInsideLiteral(position, position)) {
-                throw new ARQException("Command string is vunerable to 
injection attack, a positional paramter (index "
+                throw new ARQException("Command string is vulnerable to 
injection attack, a positional parameter (index "
                         + index
                         + ") appears inside of a literal and is bound to a 
literal which provides a SPARQL injection attack vector");
             }
@@ -1358,6 +1371,10 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
     }
 
     protected final String stringForNode(Node n, SerializationContext context) 
{
+        if (n == UNDEF) {
+            return "UNDEF";
+        }
+
         String str = FmtUtils.stringForNode(n, context);
         if (n.isLiteral() && str.contains("'")) {
             // Should escape ' to avoid a possible injection vulnerability
@@ -1369,7 +1386,7 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
     /**
      * <p>
      * This method is where the actual work happens, the original command text
-     * is always preserved and we just generated a temporary command string by
+     * is always preserved, and we just generate a temporary command string by
      * prepending the defined Base URI and namespace prefixes at the start of
      * the command and injecting the set parameters into a copy of that base
      * command string and return the resulting command.
@@ -1849,7 +1866,10 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
      * See setRowValues to assign multiple values to multiple variables.<br>
      * Using "valueName" with list(prop_A, obj_A) on query "VALUES (?p ?o)
      * {?valueName}" * would produce "VALUES (?p ?o) {(prop_A obj_A)}".
-     *
+     * <p>
+     * Note that if you want to inject the SPARQL keyword {@code UNDEF} for 
any of the values then you
+     * <strong>MUST</strong> use the special {@link #undef()} value in the 
items parameter as appropriate.
+     * </p>
      *
      * @param valueName
      * @param items
@@ -1871,6 +1891,10 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
      * Assign a VALUES valueName with a single item.<br>
      * Using "valueName" with Literal obj_A on query "VALUES ?o {?valueName}"
      * would produce * "VALUES ?o {obj_A}".
+     * <p>
+     * Note that if you want to inject the SPARQL keyword {@code UNDEF} for 
the value then you <strong>MUST</strong> use
+     * the special {@link #undef()} value as the item parameter.
+     * </p>
      *
      * @param valueName
      * @param item
@@ -1880,10 +1904,14 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
     }
 
     /**
-     * ** Sets a map of VALUES valueNames and their items.<br>
+     * Sets a map of VALUES valueNames and their items.<br>
      * Can be used to assign multiple values to a single variable or single
      * value to multiple variables (if using a List) in the SPARQL query.<br>
      * See setRowValues to assign multiple values to multiple variables.
+     * <p>
+     * Note that if you want to inject the SPARQL keyword {@code UNDEF} for 
any of the values then you
+     * <strong>MUST</strong> use the special {@link #undef()} value in the 
itemsMap parameter as appropriate.
+     * </p>
      *
      * @param itemsMap
      */
@@ -1896,6 +1924,10 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
      * Using "valuesName" with list(list(prop_A, obj_A), list(prop_B, obj_B)) 
on
      * query "VALUES (?p ?o) {?valuesName}" would produce "VALUES (?p ?o)
      * {(prop_A obj_A) * (prop_B obj_B)}".
+     * <p>
+     * Note that if you want to inject the SPARQL keyword {@code UNDEF} for 
any of the values then you
+     * <strong>MUST</strong> use the special {@link #undef()} value in the 
rowItems parameter as appropriate.
+     * </p>
      *
      * @param valueName
      * @param rowItems
@@ -2008,8 +2040,6 @@ public class ParameterizedSparqlString implements 
PrefixMapping {
 
         /**
          * Tidy up valueName if doesn't start with a ? or $.
-         *
-         * @param valueName
          * @return
          */
         private String createTarget() {
diff --git 
a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java
 
b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java
index de470ee8b1..cfd3932dda 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java
@@ -1674,7 +1674,6 @@ public class TestParameterizedSparqlString {
         queryStr.asQuery();
     }
 
-
     @Test
     public void test_param_string_non_injection_03() {
         String prefixes="PREFIX : <http://purl.bdrc.io/ontology/core/>\n" +
@@ -1982,8 +1981,7 @@ public class TestParameterizedSparqlString {
 
         String exp = "SELECT * WHERE { VALUES ?o {(\"test\")} ?s ?p ?o }";
         String res = pss.toString();
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
+
         Assert.assertEquals(exp, res);
     }
 
@@ -1996,8 +1994,20 @@ public class TestParameterizedSparqlString {
 
         String exp = "SELECT * WHERE { VALUES $o {(\"test\")} $s $p $o }";
         String res = pss.toString();
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
+
+        Assert.assertEquals(exp, res);
+    }
+
+    @Test
+    public void test_set_values_undef() {
+        // Tests a single value being added using '$' variable syntax - always 
adding parenthesis.
+        String str = "SELECT * WHERE { VALUES $o {$objs} $s $p $o }";
+        ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
+        pss.setValues("objs", pss.undef());
+
+        String exp = "SELECT * WHERE { VALUES $o {(UNDEF)} $s $p $o }";
+        String res = pss.toString();
+
         Assert.assertEquals(exp, res);
     }
 
@@ -2010,8 +2020,7 @@ public class TestParameterizedSparqlString {
 
         String exp = "SELECT * WHERE { ?o {?objs} ?s ?p ?o }";
         String res = pss.toString();
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
+
         Assert.assertEquals(exp, res);
     }
 
@@ -2024,8 +2033,7 @@ public class TestParameterizedSparqlString {
 
         String exp = "SELECT * WHERE { VALUES ?o ?objs ?s ?p ?o }";
         String res = pss.toString();
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
+
         Assert.assertEquals(exp, res);
     }
 
@@ -2038,8 +2046,7 @@ public class TestParameterizedSparqlString {
 
         String exp = "SELECT * WHERE { VALUES ?o {?objs} ?s ?p ?o }";
         String res = pss.toString();
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
+
         Assert.assertEquals(exp, res);
     }
 
@@ -2055,8 +2062,7 @@ public class TestParameterizedSparqlString {
 
         String exp = "SELECT * WHERE { VALUES (?o) {(\"obj_A\") (\"obj_B\")} 
?s ?p ?o }";
         String res = pss.toString();
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
+
         Assert.assertEquals(exp, res);
     }
 
@@ -2071,8 +2077,6 @@ public class TestParameterizedSparqlString {
         String exp = "SELECT * WHERE { VALUES (?o) {} ?s ?p ?o }";
         String res = pss.toString();
 
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
         Assert.assertEquals(exp, res);
     }
 
@@ -2088,8 +2092,23 @@ public class TestParameterizedSparqlString {
 
         String exp = "SELECT * WHERE { VALUES (?p ?o) 
{(<http://example.org/prop_A> \"obj_A\")} ?s ?p ?o }";
         String res = pss.toString();
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
+
+        Assert.assertEquals(exp, res);
+    }
+
+    @Test
+    public void test_set_values_multiple_variables_including_undef() {
+        // Tests two values for same variable.
+        String str = "SELECT * WHERE { VALUES (?p ?o) {?vars} ?s ?p ?o }";
+        ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
+        List<RDFNode> vars = new ArrayList<>();
+        vars.add(ResourceFactory.createProperty("http://example.org/prop_A";));
+        vars.add(pss.undef());
+        pss.setValues("vars", vars);
+
+        String exp = "SELECT * WHERE { VALUES (?p ?o) 
{(<http://example.org/prop_A> UNDEF)} ?s ?p ?o }";
+        String res = pss.toString();
+
         Assert.assertEquals(exp, res);
     }
 
@@ -2133,13 +2152,13 @@ public class TestParameterizedSparqlString {
 
         List<RDFNode> props = new ArrayList<>();
         props.add(ResourceFactory.createProperty("http://example.org/prop_A";));
+        props.add(pss.undef());
         props.add(ResourceFactory.createProperty("http://example.org/prop_B";));
         pss.setValues("props", props);
 
-        String exp = "SELECT * WHERE { VALUES ?p 
{(<http://example.org/prop_A>) (<http://example.org/prop_B>)} VALUES ?o 
{(\"obj_A\") (\"obj_B\")} ?s ?p ?o }";
+        String exp = "SELECT * WHERE { VALUES ?p 
{(<http://example.org/prop_A>) (UNDEF) (<http://example.org/prop_B>)} VALUES ?o 
{(\"obj_A\") (\"obj_B\")} ?s ?p ?o }";
         String res = pss.toString();
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
+
         Assert.assertEquals(exp, res);
     }
 
@@ -2164,8 +2183,7 @@ public class TestParameterizedSparqlString {
 
         String exp = "SELECT * WHERE { VALUES (?p ?o) 
{(<http://example.org/prop_A> \"obj_A\") (<http://example.org/prop_B> 
\"obj_B\")} ?s ?p ?o }";
         String res = pss.toString();
-        //System.out.println("Exp: " + exp);
-        //System.out.println("Res: " + res);
+
         Assert.assertEquals(exp, res);
     }
 
@@ -2188,8 +2206,6 @@ public class TestParameterizedSparqlString {
         String[] res = ParameterizedSparqlString.extractTargetVars(cmd, 
valueName);
         String[] exp = new String[]{"o"};
 
-        //System.out.println("Exp: " + String.join(",", exp));
-        //System.out.println("Res: " + String.join(",", res));
         Assert.assertArrayEquals(exp, res);
     }
 
@@ -2201,8 +2217,6 @@ public class TestParameterizedSparqlString {
         String[] res = ParameterizedSparqlString.extractTargetVars(cmd, 
valueName);
         String[] exp = new String[]{"p", "o"};
 
-        ///System.out.println("Exp: " + String.join(",", exp));
-        //System.out.println("Res: " + String.join(",", res));
         Assert.assertArrayEquals(exp, res);
     }
 
@@ -2214,8 +2228,6 @@ public class TestParameterizedSparqlString {
         String[] res = ParameterizedSparqlString.extractTargetVars(cmd, 
valueName);
         String[] exp = new String[]{"o"};
 
-        //System.out.println("Exp: " + String.join(",", exp));
-        //System.out.println("Res: " + String.join(",", res));
         Assert.assertArrayEquals(exp, res);
     }
 
@@ -2227,8 +2239,6 @@ public class TestParameterizedSparqlString {
         String[] res = ParameterizedSparqlString.extractTargetVars(cmd, 
valueName);
         String[] exp = new String[]{};
 
-        //System.out.println("Exp: " + String.join(",", exp));
-        //System.out.println("Res: " + String.join(",", res));
         Assert.assertArrayEquals(exp, res);
     }
 
@@ -2240,8 +2250,6 @@ public class TestParameterizedSparqlString {
         String[] res = ParameterizedSparqlString.extractTargetVars(cmd, 
valueName);
         String[] exp = new String[]{};
 
-        //System.out.println("Exp: " + String.join(",", exp));
-        //System.out.println("Res: " + String.join(",", res));
         Assert.assertArrayEquals(exp, res);
     }
 
@@ -2253,8 +2261,6 @@ public class TestParameterizedSparqlString {
         String[] res = ParameterizedSparqlString.extractTargetVars(cmd, 
valueName);
         String[] exp = new String[]{};
 
-        //System.out.println("Exp: " + String.join(",", exp));
-        //System.out.println("Res: " + String.join(",", res));
         Assert.assertArrayEquals(exp, res);
     }
 
@@ -2266,8 +2272,6 @@ public class TestParameterizedSparqlString {
         String[] res = ParameterizedSparqlString.extractTargetVars(cmd, 
valueName);
         String[] exp = new String[]{};
 
-        //System.out.println("Exp: " + String.join(",", exp));
-        //System.out.println("Res: " + String.join(",", res));
         Assert.assertArrayEquals(exp, res);
     }
 
@@ -2279,8 +2283,6 @@ public class TestParameterizedSparqlString {
         String[] res = ParameterizedSparqlString.extractTargetVars(cmd, 
valueName);
         String[] exp = new String[]{};
 
-        //System.out.println("Exp: " + String.join(",", exp));
-        //System.out.println("Res: " + String.join(",", res));
         Assert.assertArrayEquals(exp, res);
     }
 
@@ -2292,9 +2294,6 @@ public class TestParameterizedSparqlString {
         String[] res = ParameterizedSparqlString.extractTargetVars(cmd, 
valueName);
         String[] exp = new String[]{};
 
-        //System.out.println("Exp: " + String.join(",", exp));
-        //System.out.println("Res: " + String.join(",", res));
         Assert.assertArrayEquals(exp, res);
     }
-
 }

Reply via email to