You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by rv...@apache.org on 2013/04/10 01:30:18 UTC
svn commit: r1466288 - in /jena/trunk/jena-arq/src:
main/java/com/hp/hpl/jena/query/ParameterizedSparqlString.java
test/java/com/hp/hpl/jena/query/TestParameterizedSparqlString.java
Author: rvesse
Date: Tue Apr 9 23:30:18 2013
New Revision: 1466288
URL: http://svn.apache.org/r1466288
Log:
Add some light delimiter parsing to ParameterizedSparqlString which allows us to detect and prevent all the previously identified undetectable injection attacks
Modified:
jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/query/ParameterizedSparqlString.java
jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/query/TestParameterizedSparqlString.java
Modified: jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/query/ParameterizedSparqlString.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/query/ParameterizedSparqlString.java?rev=1466288&r1=1466287&r2=1466288&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/query/ParameterizedSparqlString.java (original)
+++ jena/trunk/jena-arq/src/main/java/com/hp/hpl/jena/query/ParameterizedSparqlString.java Tue Apr 9 23:30:18 2013
@@ -19,15 +19,18 @@
package com.hp.hpl.jena.query;
import java.net.URL;
+import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.apache.jena.atlas.lib.Pair;
import org.apache.jena.iri.IRI;
import com.hp.hpl.jena.datatypes.RDFDatatype;
@@ -122,19 +125,23 @@ import com.hp.hpl.jena.update.UpdateRequ
* cannot prevent. In particular you should never surround a variable which you
* intend to replace with double quotes e.g.
* </p>
+ *
* <pre>
* String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> \"?var\" }";
* ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
* </pre>
+ *
* <p>
* While the class will recognize and prevent this as an error this protection
* is trivially defeated by placing some white space around the variable
* definition e.g
* </p>
+ *
* <pre>
* String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> \" ?var \" }";
* ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
* </pre>
+ *
* <p>
* This latter case cannot be easily detected and prevented because we can't
* easily distinguish between a possible injection vulnerability and a variable
@@ -143,7 +150,7 @@ import com.hp.hpl.jena.update.UpdateRequ
* <p>
* Therefore we <strong>strongly</strong> recommend that users concerned about
* SPARQL Injection attacks perform their own validation on provided parameters
- * and test their use of this class to avoid known attack vectors. We also
+ * and test their use of this class to avoid known attack vectors. We also
* recommend that users do not use easily guessable variable names for their
* parameters as these can allow a chained injection attack.
* </p>
@@ -601,7 +608,7 @@ public class ParameterizedSparqlString i
protected void validateParameterValue(Node n) {
if (n.isURI()) {
if (n.getURI().contains(">"))
- throw new ARQException("Value for the parameter attempts SQL injection");
+ throw new ARQException("Value for the parameter contains a SPARQL injection risk");
}
}
@@ -1147,19 +1154,27 @@ public class ParameterizedSparqlString i
this.params.clear();
this.positionalParams.clear();
}
-
+
/**
- * Helper method which checks whether it is safe to inject to a variable parameter the given value
- * @param command Current command string
- * @param var Variable
- * @param n Value to inject
- * @throws ARQException Thrown if not safe to inject, error message will describe why it is unsafe to inject
+ * Helper method which checks whether it is safe to inject to a variable
+ * parameter the given value
+ *
+ * @param command
+ * Current command string
+ * @param var
+ * Variable
+ * @param n
+ * Value to inject
+ * @throws ARQException
+ * Thrown if not safe to inject, error message will describe why
+ * it is unsafe to inject
*/
protected void validateSafeToInject(String command, String var, Node n) throws ARQException {
- // Looks for the known injection attack vectors and throws an error if any
- // are encountered
-
- // A ?var surrounded by " or ' where the variable is a literal is an attack vector
+ // Looks for the known injection attack vectors and throws an error if
+ // any are encountered
+
+ // A ?var surrounded by " or ' where the variable is a literal is an
+ // attack vector
Pattern p = Pattern.compile("\"[?$]" + var + "\"|'[?$]" + var + "'");
if (p.matcher(command).find() && n.isLiteral()) {
@@ -1168,14 +1183,58 @@ public class ParameterizedSparqlString i
+ var
+ " appears surrounded directly by quotes and is bound to a literal which provides a SPARQL injection attack vector");
}
+
+ // Parse out delimiter info
+ DelimiterInfo delims = this.findDelimiters(command);
+
+ // Check each occurrence of the variable for safety
+ p = Pattern.compile("([?$]" + var + ")([^\\w]|$)");
+ Matcher matcher = p.matcher(command);
+ while (matcher.find()) {
+ MatchResult posMatch = matcher.toMatchResult();
+
+ if (n.isLiteral()) {
+ if (delims.isInsideLiteral(posMatch.start(1), posMatch.end(1))) {
+ throw new ARQException(
+ "Command string is vunerable to injection attack, variable ?"
+ + var
+ + " appears inside of a literal and is bound to a literal which provides a SPARQL injection attack vector");
+ }
+ }
+ }
}
/**
+ * Helper method which does light parsing on the command string to find the
+ * position of all relevant delimiters
+ *
+ * @param command
+ * Command String
+ * @return
+ */
+ protected final DelimiterInfo findDelimiters(String command) {
+ DelimiterInfo delims = new DelimiterInfo();
+ delims.parseFrom(command);
+ return delims;
+ }
+
+ protected final String stringForNode(Node n, SerializationContext context) {
+ String str = FmtUtils.stringForNode(n, context);
+ if (str.contains("'")) {
+ // Should escape ' to avoid a possible injection vulnerability
+ str = str.replace("'", "\\'");
+ }
+ return str;
+ }
+
+ /**
+ * <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
* 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.
+ * </p>
* <p>
* This class makes no guarantees about the validity of the returned string
* for use as a SPARQL Query or Update, for example if a variable parameter
@@ -1183,6 +1242,11 @@ public class ParameterizedSparqlString i
* a syntax error when you try to parse the query. If you run into issues
* like this try using a mixture of variable and positional parameters.
* </p>
+ *
+ * @throws ARQException
+ * May be thrown if the code detects a SPARQL Injection
+ * vulnerability because of the interaction of the command
+ * string and the injected variables
*/
@Override
public String toString() {
@@ -1196,12 +1260,12 @@ public class ParameterizedSparqlString i
while (vars.hasNext()) {
String var = vars.next();
Node n = this.params.get(var);
- if (n == null) continue;
+ if (n == null)
+ continue;
this.validateSafeToInject(command, var, n);
p = Pattern.compile("([?$]" + var + ")([^\\w]|$)");
- command = p.matcher(command).replaceAll(
- Matcher.quoteReplacement(FmtUtils.stringForNode(n, context)) + "$2");
+ command = p.matcher(command).replaceAll(Matcher.quoteReplacement(this.stringForNode(n, context)) + "$2");
}
// Then inject Positional Parameters
@@ -1218,7 +1282,7 @@ public class ParameterizedSparqlString i
if (n == null)
continue;
- String nodeStr = FmtUtils.stringForNode(n, context);
+ String nodeStr = this.stringForNode(n, context);
command = command.substring(0, posMatch.start() + adj) + nodeStr + command.substring(posMatch.start() + adj + 1);
// Because we are using a matcher over the string state prior to
// starting replacements we need to
@@ -1388,4 +1452,188 @@ public class ParameterizedSparqlString i
return this.prefixes.samePrefixMappingAs(other);
}
+ private class DelimiterInfo {
+ private List<Pair<Integer, String>> starts = new ArrayList<Pair<Integer, String>>();
+ private Map<Integer, Integer> stops = new HashMap<Integer, Integer>();
+
+ public void parseFrom(String command) {
+ this.starts.clear();
+ this.stops.clear();
+
+ char[] cs = command.toCharArray();
+ for (int i = 0; i < cs.length; i++) {
+ switch (cs[i]) {
+ case '"':
+ // Start of a Literal
+ // Is it a long literal?
+ if (i < cs.length - 2 && cs[i + 1] == '"' && cs[i + 2] == '"') {
+ this.addStart(i, "\"\"\"");
+ for (int j = i + 3; j < cs.length - 2; j++) {
+ if (cs[j] == '"' && cs[j + 1] == '"' && cs[j + 2] == '"') {
+ this.addStop(i, j + 2);
+ i = j + 2;
+ }
+ }
+ // Was unterminated
+ } else {
+ // Normal literal, scan till we see a " which is not
+ // preceded by a \
+ this.addStart(i, "\"");
+ for (int j = i + 1; j < cs.length; j++) {
+ if (cs[j] == '"' && cs[j - 1] != '\\') {
+ this.addStop(i, j);
+ i = j;
+ continue;
+ }
+ }
+ // Was unterminated
+ }
+ break;
+ case '<':
+ // Start of a URI
+ this.addStart(i, "<");
+ for (int j = i + 1; j < cs.length; j++) {
+ if (cs[j] == '>' && cs[j - 1] != '\\') {
+ this.addStop(i, j);
+ i = j;
+ continue;
+ }
+ }
+ // Was unterminated
+ break;
+ case '\'':
+ // Start of alternative literal form
+ // Start of a Literal
+ // Is it a long literal?
+ if (i < cs.length - 2 && cs[i + 1] == '\'' && cs[i + 2] == '\'') {
+ this.addStart(i, "'''");
+ for (int j = i + 3; j < cs.length - 2; j++) {
+ if (cs[j] == '\'' && cs[j + 1] == '\'' && cs[j + 2] == '\'') {
+ this.addStop(i, j + 2);
+ i = j + 2;
+ }
+ }
+ // Was unterminated
+ } else {
+ // Normal literal, scan till we see a ' which is not
+ // preceded by a \
+ this.addStart(i, "'");
+ for (int j = i + 1; j < cs.length; j++) {
+ if (cs[j] == '\'' && cs[j - 1] != '\\') {
+ this.addStop(i, j);
+ i = j;
+ continue;
+ }
+ }
+ // Was unterminated
+ }
+ break;
+ case '#':
+ // Start of a comment
+ // Scan to next newline
+ this.addStart(i, "#");
+ for (int j = i + 1; j < cs.length; j++) {
+ if (cs[j] == '\n' || cs[j] == '\r') {
+ this.addStop(i, j);
+ i = j;
+ continue;
+ }
+ }
+ this.addStop(i, cs.length - 1);
+ break;
+ case '\n':
+ case '\r':
+ case '.':
+ case ',':
+ case ';':
+ case '(':
+ case ')':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ // Treat various punctuation as delimiters
+ this.addStart(i, new String(new char[] { cs[i] }));
+ this.addStop(i, i);
+ break;
+ }
+ }
+ }
+
+ public void addStart(int index, String delim) {
+ this.starts.add(new Pair<Integer, String>(index, delim));
+ }
+
+ public void addStop(int start, int stop) {
+ this.stops.put(start, stop);
+ }
+
+ public Pair<Integer, String> findBefore(int index) {
+ Pair<Integer, String> found = null;
+ for (Pair<Integer, String> pair : this.starts) {
+ if (pair.getLeft() < index)
+ found = pair;
+ if (pair.getLeft() >= index)
+ break;
+ }
+ return found;
+ }
+
+ public Pair<Integer, String> findAfter(int index) {
+ for (Pair<Integer, String> pair : this.starts) {
+ if (pair.getLeft() > index)
+ return pair;
+ }
+ return null;
+ }
+
+ public boolean isInsideLiteral(int start, int stop) {
+ Pair<Integer, String> pair = this.findBefore(start);
+ if (pair == null)
+ return false;
+ if (pair.getRight().equals("\"")) {
+ Integer nearestStop = this.stops.get(pair.getLeft());
+ if (nearestStop == null)
+ return true; // Inside unterminated literal
+ return (nearestStop > stop); // May be inside a literal
+ } else {
+ // Not inside a literal
+ return false;
+ }
+ }
+
+ public boolean isInsideAltLiteral(int start, int stop) {
+ Pair<Integer, String> pair = this.findBefore(start);
+ if (pair == null)
+ return false;
+ if (pair.getRight().equals("'")) {
+ Integer nearestStop = this.stops.get(pair.getLeft());
+ if (nearestStop == null)
+ return true; // Inside unterminated literal
+ return (nearestStop > stop); // May be inside a literal
+ } else {
+ // Not inside a literal
+ return false;
+ }
+ }
+
+ public boolean isBetweenLiterals(int start, int stop) {
+ Pair<Integer, String> pairBefore = this.findBefore(start);
+ if (pairBefore == null)
+ return false;
+ if (pairBefore.getRight().equals("\"")) {
+ Integer stopBefore = this.stops.get(pairBefore.getLeft());
+ if (stopBefore == null)
+ return false; // Inside unterminated literal
+
+ // We occur after a literal, is there a subsequent literal?
+ Pair<Integer, String> pairAfter = this.findAfter(stop);
+ return pairAfter != null && pairAfter.getRight().equals("\"");
+ } else {
+ // Previous deliminator is not that of a literal
+ return false;
+ }
+ }
+
+ }
}
Modified: jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/query/TestParameterizedSparqlString.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/query/TestParameterizedSparqlString.java?rev=1466288&r1=1466287&r2=1466288&view=diff
==============================================================================
--- jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/query/TestParameterizedSparqlString.java (original)
+++ jena/trunk/jena-arq/src/test/java/com/hp/hpl/jena/query/TestParameterizedSparqlString.java Tue Apr 9 23:30:18 2013
@@ -18,26 +18,26 @@
package com.hp.hpl.jena.query;
-import java.util.Calendar ;
-import java.util.TimeZone ;
+import java.util.Calendar;
+import java.util.TimeZone;
-import org.apache.jena.iri.IRIFactory ;
-import org.junit.Assert ;
-import org.junit.Test ;
-
-import com.hp.hpl.jena.datatypes.TypeMapper ;
-import com.hp.hpl.jena.graph.Node ;
-import com.hp.hpl.jena.graph.NodeFactory ;
-import com.hp.hpl.jena.rdf.model.Literal ;
-import com.hp.hpl.jena.rdf.model.Resource ;
-import com.hp.hpl.jena.rdf.model.ResourceFactory ;
-import com.hp.hpl.jena.shared.impl.PrefixMappingImpl ;
-import com.hp.hpl.jena.sparql.ARQException ;
-import com.hp.hpl.jena.sparql.syntax.Element ;
-import com.hp.hpl.jena.sparql.syntax.ElementGroup ;
-import com.hp.hpl.jena.sparql.syntax.ElementTriplesBlock ;
-import com.hp.hpl.jena.update.UpdateRequest ;
-import com.hp.hpl.jena.vocabulary.XSD ;
+import org.apache.jena.iri.IRIFactory;
+import org.junit.Assert;
+import org.junit.Test;
+
+import com.hp.hpl.jena.datatypes.TypeMapper;
+import com.hp.hpl.jena.graph.Node;
+import com.hp.hpl.jena.graph.NodeFactory;
+import com.hp.hpl.jena.rdf.model.Literal;
+import com.hp.hpl.jena.rdf.model.Resource;
+import com.hp.hpl.jena.rdf.model.ResourceFactory;
+import com.hp.hpl.jena.shared.impl.PrefixMappingImpl;
+import com.hp.hpl.jena.sparql.ARQException;
+import com.hp.hpl.jena.sparql.syntax.Element;
+import com.hp.hpl.jena.sparql.syntax.ElementGroup;
+import com.hp.hpl.jena.sparql.syntax.ElementTriplesBlock;
+import com.hp.hpl.jena.update.UpdateRequest;
+import com.hp.hpl.jena.vocabulary.XSD;
public class TestParameterizedSparqlString {
@@ -292,7 +292,7 @@ public class TestParameterizedSparqlStri
query.setIri("p", "http://predicate");
query.setLiteral("o", "A test's test");
- Assert.assertEquals("SELECT * WHERE { <http://example.org> <http://predicate> \"A test's test\" . }", query.toString());
+ Assert.assertEquals("SELECT * WHERE { <http://example.org> <http://predicate> \"A test\\'s test\" . }", query.toString());
}
@Test
@@ -1333,8 +1333,8 @@ public class TestParameterizedSparqlStri
@Test(expected = ARQException.class)
public void test_param_string_injection_06() {
- // This injection attempt is prevented by forbidding injection to a variable
- // parameter immediatedly surrounded by quotes
+ // This injection attempt is prevented by forbidding injection to a
+ // variable parameter immediately surrounded by quotes
String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> '?var' }";
ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
pss.setLiteral("var", "hello' . } ; DROP ALL ; INSERT DATA { <s> <p> \"goodbye");
@@ -1354,11 +1354,12 @@ public class TestParameterizedSparqlStri
UpdateRequest updates = pss.asUpdate();
Assert.fail("Attempt to do SPARQL injection should result in an exception");
}
-
+
@Test(expected = ARQException.class)
public void test_param_string_injection_08() {
- // This injection attempt results in an invalid SPARQL update because you end up with
- // a double quoted literal inside a single quoted literal
+ // This injection attempt results in an invalid SPARQL update because
+ // you end up with a double quoted literal inside a single quoted
+ // literal
String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> '?var' }";
ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
pss.setLiteral("var", "' . } ; DROP ALL ; INSERT DATA { <s> <p> <o> }#");
@@ -1369,9 +1370,9 @@ public class TestParameterizedSparqlStri
@Test
public void test_param_string_injection_09() {
- // This injection attempt using comments results in a valid SPARQL update but a failed
- // injection because the attempt to use comments ends up being a valid string
- // literal within quotes
+ // This injection attempt using comments results in a valid SPARQL
+ // update but a failed injection because the attempt to use comments
+ // ends up being a valid string literal within quotes
String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> ?var }";
ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
pss.setLiteral("var", "\" . } ; DROP ALL ; INSERT DATA { <s> <p> <o> }#");
@@ -1379,97 +1380,116 @@ public class TestParameterizedSparqlStri
UpdateRequest updates = pss.asUpdate();
Assert.assertEquals(1, updates.getOperations().size());
}
-
- @Test(expected=ARQException.class)
+
+ @Test(expected = ARQException.class)
public void test_param_string_injection_10() {
- // This injection attempt tries to chain together injections to achieve an attack, the first
- // injection appears innocuous and is an attempt to set up an actual injection vector
- // The injection is prevented because a ?var directly surrounded by quotes is always flagged as
- // subject to injection because pre-injection validation happens before each variable is injected
+ // This injection attempt tries to chain together injections to achieve
+ // an attack, the first
+ // injection appears innocuous and is an attempt to set up an actual
+ // injection vector
+ // The injection is prevented because a ?var directly surrounded by
+ // quotes is always flagged as
+ // subject to injection because pre-injection validation happens before
+ // each variable is injected
String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> ?var }";
ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
pss.setLiteral("var", "a");
pss.setLiteral("var2", "b");
-
+
// Figure out which variable will be injected first
String first = pss.getVars().next();
String second = first.equals("var") ? "var2" : "var";
-
+
pss.setLiteral(first, "?" + second);
pss.setLiteral(second, " . } ; DROP ALL ; INSERT DATA { <s> <p> ");
UpdateRequest updates = pss.asUpdate();
Assert.fail("Attempt to do SPARQL injection should result in an exception");
}
-
- @Test
- public void test_param_string_injection_permitted_01() {
- // This is a case where we cannot detect the different between a valid
- // parameterized string and one that is subject to injection
+
+ @Test(expected = ARQException.class)
+ public void test_param_string_injection_11() {
+ // This is a variant on placing a variable bound to a literal inside a
+ // literal resulting in an injection, we are now able to detect and
+ // prevent this
String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> \" ?var \" }";
ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
pss.setLiteral("var", " . } ; DROP ALL ; INSERT DATA { <s> <p> ");
UpdateRequest updates = pss.asUpdate();
- Assert.assertEquals(3, updates.getOperations().size());
+ Assert.fail("Attempt to do SPARQL injection should result in an exception");
}
- @Test
- public void test_param_string_injection_permitted_02() {
- // This is a case where we cannot detect the different between a valid
- // parameterized
- // string and one that is subject to injection
+ @Test(expected = ARQException.class)
+ public void test_param_string_injection_12() {
+ // This is a variant on placing a variable bound to a literal inside a
+ // literal resulting in an injection, we are now able to detect and
+ // prevent this
String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> \"some text ?var other text\" }";
ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
pss.setLiteral("var", " . } ; DROP ALL ; INSERT DATA { <s> <p> ");
UpdateRequest updates = pss.asUpdate();
- Assert.assertEquals(3, updates.getOperations().size());
+ Assert.fail("Attempt to do SPARQL injection should result in an exception");
}
-
+
@Test
- public void test_param_string_injection_permitted_03() {
- // This is a case where we cannot detect the different between a valid
- // parameterized string and one that is subject to injection
+ public void test_param_string_injection_13() {
+ // This is a variant on placing a variable bound to a literal inside a
+ // literal resulting in an injection, we now escape ' so prevent this
String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> ' ?var ' }";
ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
pss.setLiteral("var", "' . } ; DROP ALL ; INSERT DATA { <s> <p> <o> }#");
UpdateRequest updates = pss.asUpdate();
- Assert.assertEquals(3, updates.getOperations().size());
+ Assert.assertEquals(1, updates.getOperations().size());
}
@Test
- public void test_param_string_injection_permitted_04() {
- // This is a case where we cannot detect the different between a valid
- // parameterized string and one that is subject to injection
+ public void test_param_string_injection_14() {
+ // This is a variant on placing a variable bound to a literal inside a
+ // literal resulting in an injection, we now escape ' so prevent this
String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> 'some text ?var other text' }";
ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
pss.setLiteral("var", "' . } ; DROP ALL ; INSERT DATA { <s> <p> <o> }#");
UpdateRequest updates = pss.asUpdate();
- Assert.assertEquals(3, updates.getOperations().size());
+ Assert.assertEquals(1, updates.getOperations().size());
}
-
- @Test
- public void test_param_string_injection_permitted_05() {
- // This injection attempt tries to chain together injections to achieve an attack, the first
- // injection appears innocuous and is an attempt to set up an actual injection vector
- // The injection is prevented because a ?var directly surrounded by quotes is always flagged as
- // subject to injection because pre-injection validation happens before each variable is injected
+
+ @Test(expected = ARQException.class)
+ public void test_param_string_injection_15() {
+ // This injection attempt tries to chain together injections to achieve
+ // an attack, the first injection appears innocuous and is an attempt to
+ // set up an actual injection vector
+ // Since we not check out delimiters we are not able to detect and
+ // prevent this
String str = "PREFIX : <http://example/>\nINSERT DATA { <s> <p> ?var }";
ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
pss.setLiteral("var", "a");
pss.setLiteral("var2", "b");
-
+
// Figure out which variable will be injected first
String first = pss.getVars().next();
String second = first.equals("var") ? "var2" : "var";
-
+
pss.setLiteral(first, " ?" + second + " ");
pss.setLiteral(second, " . } ; DROP ALL ; INSERT DATA { <s> <p> ");
UpdateRequest updates = pss.asUpdate();
- Assert.assertEquals(3, updates.getOperations().size());
+ Assert.fail("Attempt to do SPARQL injection should result in an exception");
}
+
+ @Test
+ public void test_param_string_non_injection_01() {
+ // This test checks that a legitimate injection of a literal to a
+ // variable that occurs between two other literals is permitted
+ // Btw this is not a valid query but it serves to illustrate the case
+ String str = "SELECT * { \"subject\" ?var \"object\" . }";
+ ParameterizedSparqlString pss = new ParameterizedSparqlString(str);
+ pss.setLiteral("var", "predicate");
+
+ pss.toString();
+ }
+
}