Skip to content

Commit 42f55e1

Browse files
authored
Merge pull request #1 from smowton/smowton/admin/rewrite-xquery
Rewrite XQuery injection to use an additional taint step instead of multiple configurations
2 parents 16308fe + d34233b commit 42f55e1

File tree

2 files changed

+26
-85
lines changed

2 files changed

+26
-85
lines changed

java/ql/src/Security/CWE/CWE-652/XQueryInjection.ql

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,21 @@ import semmle.code.java.dataflow.FlowSources
1515
import XQueryInjectionLib
1616
import DataFlow::PathGraph
1717

18-
class XQueryInjectionConfig extends DataFlow::Configuration {
18+
class XQueryInjectionConfig extends TaintTracking::Configuration {
1919
XQueryInjectionConfig() { this = "XQueryInjectionConfig" }
2020

21-
override predicate isSource(DataFlow::Node source) { source instanceof XQueryInjectionSource }
21+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
2222

23-
override predicate isSink(DataFlow::Node sink) { sink instanceof XQueryInjectionSink }
23+
override predicate isSink(DataFlow::Node sink) {
24+
sink.asExpr() = any(XQueryExecuteCall execute).getPreparedExpression()
25+
}
2426

25-
override predicate isBarrier(DataFlow::Node node) {
26-
exists(MethodAccess ma, Method m, BindParameterRemoteFlowConf conf, DataFlow::Node node1 |
27-
m = ma.getMethod()
28-
|
29-
node.asExpr() = ma and
30-
m.hasName("bindString") and
31-
m.getDeclaringType()
32-
.getASourceSupertype*()
33-
.hasQualifiedName("javax.xml.xquery", "XQDynamicContext") and
34-
ma.getArgument(1) = node1.asExpr() and
35-
conf.hasFlowTo(node1)
36-
)
27+
/**
28+
* Conveys taint from the input to a `prepareExpression` call to the returned prepared expression.
29+
*/
30+
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
31+
exists(XQueryParserCall parser |
32+
pred.asExpr() = parser.getInput() and succ.asExpr() = parser)
3733
}
3834
}
3935

Lines changed: 15 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
import java
2-
import semmle.code.java.dataflow.FlowSources
3-
import semmle.code.java.dataflow.TaintTracking2
4-
import DataFlow::PathGraph
52

63
/** A call to `XQConnection.prepareExpression`. */
74
class XQueryParserCall extends MethodAccess {
@@ -14,77 +11,25 @@ class XQueryParserCall extends MethodAccess {
1411
m.hasName("prepareExpression")
1512
)
1613
}
17-
/** Returns the first parameter of the `bindString` method. */
18-
Expr getInput() { result = this.getArgument(0) }
19-
}
20-
21-
/** A call to `XQDynamicContext.bindString`. */
22-
class XQueryBindStringCall extends MethodAccess {
23-
XQueryBindStringCall() {
24-
exists(Method m |
25-
this.getMethod() = m and
26-
m.getDeclaringType()
27-
.getASourceSupertype*()
28-
.hasQualifiedName("javax.xml.xquery", "XQDynamicContext") and
29-
m.hasName("bindString")
30-
)
31-
}
32-
/** Returns the second parameter of the `bindString` method. */
33-
Expr getInput() { result = this.getArgument(1) }
34-
}
35-
36-
/** Used to determine whether to call the `prepareExpression` method, and the first parameter value can be remotely controlled. */
37-
class ParserParameterRemoteFlowConf extends TaintTracking2::Configuration {
38-
ParserParameterRemoteFlowConf() { this = "ParserParameterRemoteFlowConf" }
3914

40-
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
41-
42-
override predicate isSink(DataFlow::Node sink) {
43-
exists(XQueryParserCall xqpc | xqpc.getSink() = sink.asExpr())
44-
}
45-
}
46-
47-
/** Used to determine whether to call the `bindString` method, and the second parameter value can be controlled remotely. */
48-
class BindParameterRemoteFlowConf extends TaintTracking2::Configuration {
49-
BindParameterRemoteFlowConf() { this = "BindParameterRemoteFlowConf" }
50-
51-
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
52-
53-
override predicate isSink(DataFlow::Node sink) {
54-
exists(XQueryBindStringCall xqbsc | xqbsc.getSink() = sink.asExpr())
55-
}
15+
/**
16+
* Returns the first parameter of the `prepareExpression` method, which provides
17+
* the string, stream or reader to be compiled into a prepared expression.
18+
*/
19+
Expr getInput() { result = this.getArgument(0) }
5620
}
5721

58-
/**
59-
* A data flow source for XQuery injection vulnerability.
60-
* 1. `prepareExpression` call as sink.
61-
* 2. Determine whether the `var1` parameter of `prepareExpression` method can be controlled remotely.
62-
*/
63-
class XQueryInjectionSource extends DataFlow::ExprNode {
64-
XQueryInjectionSource() {
65-
exists(MethodAccess ma, Method m, ParserParameterRemoteFlowConf conf, DataFlow::Node node |
66-
m = ma.getMethod()
67-
|
68-
m.hasName("prepareExpression") and
69-
m.getDeclaringType()
70-
.getASourceSupertype*()
71-
.hasQualifiedName("javax.xml.xquery", "XQConnection") and
72-
asExpr() = ma and
73-
node.asExpr() = ma.getArgument(0) and
74-
conf.hasFlowTo(node)
22+
/** A call to `XQPreparedExpression.executeQuery`. */
23+
class XQueryExecuteCall extends MethodAccess {
24+
XQueryExecuteCall() {
25+
exists(Method m | this.getMethod() = m and
26+
m.hasName("executeQuery") and
27+
m.getDeclaringType()
28+
.getASourceSupertype*()
29+
.hasQualifiedName("javax.xml.xquery", "XQPreparedExpression")
7530
)
7631
}
77-
}
7832

79-
/** A data flow sink for XQuery injection vulnerability. */
80-
class XQueryInjectionSink extends DataFlow::Node {
81-
XQueryInjectionSink() {
82-
exists(MethodAccess ma, Method m | m = ma.getMethod() |
83-
m.hasName("executeQuery") and
84-
m.getDeclaringType()
85-
.getASourceSupertype*()
86-
.hasQualifiedName("javax.xml.xquery", "XQPreparedExpression") and
87-
asExpr() = ma.getQualifier()
88-
)
89-
}
33+
/** Return this prepared expression. */
34+
Expr getPreparedExpression() { result = this.getQualifier() }
9035
}

0 commit comments

Comments
 (0)