Skip to content

Commit a56dd60

Browse files
committed
*)add CWE-652 XQueryInjection detection
1 parent 9cfbe6f commit a56dd60

File tree

4 files changed

+125
-0
lines changed

4 files changed

+125
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import javax.servlet.http.HttpServletRequest;
2+
import javax.xml.namespace.QName;
3+
import javax.xml.xquery.XQConnection;
4+
import javax.xml.xquery.XQDataSource;
5+
import javax.xml.xquery.XQException;
6+
import javax.xml.xquery.XQItemType;
7+
import javax.xml.xquery.XQPreparedExpression;
8+
import javax.xml.xquery.XQResultSequence;
9+
import net.sf.saxon.xqj.SaxonXQDataSource;
10+
11+
public void bad(HttpServletRequest request) throws XQException {
12+
String name = request.getParameter("name");
13+
XQDataSource ds = new SaxonXQDataSource();
14+
XQConnection conn = ds.getConnection();
15+
String query = "for $user in doc(\"users.xml\")/Users/User[name='" + name + "'] return $user/password";
16+
XQPreparedExpression xqpe = conn.prepareExpression(query);
17+
XQResultSequence result = xqpe.executeQuery();
18+
while (result.next()){
19+
System.out.println(result.getItemAsString(null));
20+
}
21+
}
22+
23+
public void good(HttpServletRequest request) throws XQException {
24+
String name = request.getParameter("name");
25+
XQDataSource ds = new SaxonXQDataSource();
26+
XQConnection conn = ds.getConnection();
27+
String query = "declare variable $name as xs:string external;"
28+
+ " for $user in doc(\"users.xml\")/Users/User[name=$name] return $user/password";
29+
XQPreparedExpression xqpe = conn.prepareExpression(query);
30+
xqpe.bindString(new QName("name"), name, conn.createAtomicType(XQItemType.XQBASETYPE_STRING));
31+
XQResultSequence result = xqpe.executeQuery();
32+
while (result.next()){
33+
System.out.println(result.getItemAsString(null));
34+
}
35+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>The software uses external input to dynamically construct an XQuery expression used to retrieve data from an XML database, but it does not neutralize or incorrectly neutralizes that input.
7+
This allows an attacker to control the structure of the query.</p>
8+
9+
</overview>
10+
<recommendation>
11+
12+
<p>Use parameterized queries. This will help ensure separation between data plane and control plane.</p>
13+
14+
</recommendation>
15+
<example>
16+
17+
<p>This example is a comparison of unused parameterized query and using parameterized query.
18+
Parameterized query through <code>bindString</code>.</p>
19+
20+
<sample src="XQueryInjection.java" />
21+
22+
</example>
23+
<references>
24+
25+
<li>cwe description:
26+
<a href="https://cwe.mitre.org/data/definitions/652.html">XQuery Injection</a>.</li>
27+
28+
29+
30+
<!-- LocalWords: CWE
31+
-->
32+
33+
</references>
34+
</qhelp>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* @name XQuery query built from user-controlled sources
3+
* @description Building an XQuery query from user-controlled sources is vulnerable to insertion of
4+
* malicious XQuery code by the user.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @precision high
8+
* @id java/XQuery-injection
9+
* @tags security
10+
* external/cwe/cwe-652
11+
*/
12+
13+
import java
14+
import semmle.code.java.dataflow.FlowSources
15+
import XQueryInjectionLib
16+
import DataFlow::PathGraph
17+
18+
from DataFlow::PathNode source, DataFlow::PathNode sink, XQueryInjectionConfig conf
19+
where conf.hasFlowPath(source, sink)
20+
select sink.getNode(), source, sink, "XQuery query might include code from $@.", source.getNode(),
21+
"this user input"
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import java
2+
import semmle.code.java.dataflow.FlowSources
3+
4+
class XQueryInjectionConfig extends TaintTracking::Configuration {
5+
XQueryInjectionConfig() { this = "XQueryInjectionConfig" }
6+
7+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
8+
9+
override predicate isSink(DataFlow::Node sink) { sink instanceof XQueryInjectionSink }
10+
}
11+
12+
/*Find if the executeQuery method is finally called.*/
13+
predicate executeQuery(MethodAccess ma) {
14+
exists(LocalVariableDeclExpr lvd, MethodAccess ma1, Method m | lvd.getAChildExpr() = ma |
15+
m = ma1.getMethod() and
16+
m.hasName("executeQuery") and
17+
m.getDeclaringType()
18+
.getASourceSupertype*()
19+
.hasQualifiedName("javax.xml.xquery", "XQPreparedExpression") and
20+
ma1.getQualifier() = lvd.getAnAccess()
21+
)
22+
}
23+
24+
class XQueryInjectionSink extends DataFlow::ExprNode {
25+
XQueryInjectionSink() {
26+
exists(MethodAccess ma, Method m | m = ma.getMethod() |
27+
m.hasName("prepareExpression") and
28+
m.getDeclaringType()
29+
.getASourceSupertype*()
30+
.hasQualifiedName("javax.xml.xquery", "XQConnection") and
31+
executeQuery(ma) and
32+
asExpr() = ma.getArgument(0)
33+
)
34+
}
35+
}

0 commit comments

Comments
 (0)