Skip to content

Commit d7feaf4

Browse files
authored
Merge pull request #12685 from atorralba/atorralba/java/command-injection-mad
Java: Add command-injection sink kind and refactor command injection queries
2 parents 2d2d32a + 3102199 commit d7feaf4

File tree

7 files changed

+102
-73
lines changed

7 files changed

+102
-73
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* A new models as data sink kind `command-injection` has been added.
5+
* The queries `java/command-line-injection` and `java/concatenated-command-line` now can be extended using the `command-injection` models as data sink kind.

java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ module ModelValidation {
272272
"groovy", "xss", "ognl-injection", "intent-start", "pending-intent-sent",
273273
"url-open-stream", "url-redirect", "create-file", "read-file", "write-file",
274274
"set-hostname-verifier", "header-splitting", "information-leak", "xslt", "jexl",
275-
"bean-validation", "ssti", "fragment-injection"
275+
"bean-validation", "ssti", "fragment-injection", "command-injection"
276276
] and
277277
not kind.matches("regex-use%") and
278278
not kind.matches("qltest%") and

java/ql/lib/semmle/code/java/security/CommandLineQuery.qll

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,44 @@
77
* query.
88
*/
99

10-
import semmle.code.java.dataflow.FlowSources
11-
import semmle.code.java.security.ExternalProcess
12-
import semmle.code.java.security.CommandArguments
10+
import java
11+
private import semmle.code.java.dataflow.FlowSources
12+
private import semmle.code.java.dataflow.ExternalFlow
13+
private import semmle.code.java.security.ExternalProcess
14+
private import semmle.code.java.security.CommandArguments
15+
16+
/** A sink for command injection vulnerabilities. */
17+
abstract class CommandInjectionSink extends DataFlow::Node { }
18+
19+
/** A sanitizer for command injection vulnerabilities. */
20+
abstract class CommandInjectionSanitizer extends DataFlow::Node { }
1321

1422
/**
15-
* DEPRECATED: Use `RemoteUserInputToArgumentToExecFlow` instead.
23+
* A unit class for adding additional taint steps.
1624
*
17-
* A taint-tracking configuration for unvalidated user input that is used to run an external process.
25+
* Extend this class to add additional taint steps that should apply to configurations related to command injection.
1826
*/
19-
deprecated class RemoteUserInputToArgumentToExecFlowConfig extends TaintTracking::Configuration {
20-
RemoteUserInputToArgumentToExecFlowConfig() {
21-
this = "ExecCommon::RemoteUserInputToArgumentToExecFlowConfig"
22-
}
23-
24-
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
27+
class CommandInjectionAdditionalTaintStep extends Unit {
28+
/**
29+
* Holds if the step from `node1` to `node2` should be considered a taint
30+
* step for configurations related to command injection.
31+
*/
32+
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
33+
}
2534

26-
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec }
35+
private class DefaultCommandInjectionSink extends CommandInjectionSink {
36+
DefaultCommandInjectionSink() {
37+
this.asExpr() instanceof ArgumentToExec or sinkNode(this, "command-injection")
38+
}
39+
}
2740

28-
override predicate isSanitizer(DataFlow::Node node) {
29-
node.getType() instanceof PrimitiveType
41+
private class DefaultCommandInjectionSanitizer extends CommandInjectionSanitizer {
42+
DefaultCommandInjectionSanitizer() {
43+
this.getType() instanceof PrimitiveType
3044
or
31-
node.getType() instanceof BoxedType
45+
this.getType() instanceof BoxedType
3246
or
33-
isSafeCommandArgument(node.asExpr())
47+
isSafeCommandArgument(this.asExpr())
3448
}
3549
}
3650

@@ -40,14 +54,12 @@ deprecated class RemoteUserInputToArgumentToExecFlowConfig extends TaintTracking
4054
module RemoteUserInputToArgumentToExecFlowConfig implements DataFlow::ConfigSig {
4155
predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
4256

43-
predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec }
57+
predicate isSink(DataFlow::Node sink) { sink instanceof CommandInjectionSink }
4458

45-
predicate isBarrier(DataFlow::Node node) {
46-
node.getType() instanceof PrimitiveType
47-
or
48-
node.getType() instanceof BoxedType
49-
or
50-
isSafeCommandArgument(node.asExpr())
59+
predicate isBarrier(DataFlow::Node node) { node instanceof CommandInjectionSanitizer }
60+
61+
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
62+
any(CommandInjectionAdditionalTaintStep s).step(n1, n2)
5163
}
5264
}
5365

@@ -58,29 +70,69 @@ module RemoteUserInputToArgumentToExecFlow =
5870
TaintTracking::Global<RemoteUserInputToArgumentToExecFlowConfig>;
5971

6072
/**
61-
* DEPRECATED: Use `execIsTainted` instead.
62-
*
63-
* Implementation of `ExecTainted.ql`. It is extracted to a QLL
64-
* so that it can be excluded from `ExecUnescaped.ql` to avoid
65-
* reporting overlapping results.
73+
* A taint-tracking configuration for unvalidated local user input that is used to run an external process.
6674
*/
67-
deprecated predicate execTainted(
68-
DataFlow::PathNode source, DataFlow::PathNode sink, ArgumentToExec execArg
69-
) {
70-
exists(RemoteUserInputToArgumentToExecFlowConfig conf |
71-
conf.hasFlowPath(source, sink) and sink.getNode() = DataFlow::exprNode(execArg)
72-
)
75+
module LocalUserInputToArgumentToExecFlowConfig implements DataFlow::ConfigSig {
76+
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
77+
78+
predicate isSink(DataFlow::Node sink) { sink instanceof CommandInjectionSink }
79+
80+
predicate isBarrier(DataFlow::Node node) { node instanceof CommandInjectionSanitizer }
81+
82+
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
83+
any(CommandInjectionAdditionalTaintStep s).step(n1, n2)
84+
}
7385
}
7486

87+
/**
88+
* Taint-tracking flow for unvalidated local user input that is used to run an external process.
89+
*/
90+
module LocalUserInputToArgumentToExecFlow =
91+
TaintTracking::Global<LocalUserInputToArgumentToExecFlowConfig>;
92+
7593
/**
7694
* Implementation of `ExecTainted.ql`. It is extracted to a QLL
7795
* so that it can be excluded from `ExecUnescaped.ql` to avoid
7896
* reporting overlapping results.
7997
*/
8098
predicate execIsTainted(
8199
RemoteUserInputToArgumentToExecFlow::PathNode source,
82-
RemoteUserInputToArgumentToExecFlow::PathNode sink, ArgumentToExec execArg
100+
RemoteUserInputToArgumentToExecFlow::PathNode sink, Expr execArg
83101
) {
84102
RemoteUserInputToArgumentToExecFlow::flowPath(source, sink) and
85-
sink.getNode() = DataFlow::exprNode(execArg)
103+
sink.getNode().asExpr() = execArg
104+
}
105+
106+
/**
107+
* DEPRECATED: Use `execIsTainted` instead.
108+
*
109+
* Implementation of `ExecTainted.ql`. It is extracted to a QLL
110+
* so that it can be excluded from `ExecUnescaped.ql` to avoid
111+
* reporting overlapping results.
112+
*/
113+
deprecated predicate execTainted(DataFlow::PathNode source, DataFlow::PathNode sink, Expr execArg) {
114+
exists(RemoteUserInputToArgumentToExecFlowConfig conf |
115+
conf.hasFlowPath(source, sink) and sink.getNode().asExpr() = execArg
116+
)
117+
}
118+
119+
/**
120+
* DEPRECATED: Use `RemoteUserInputToArgumentToExecFlow` instead.
121+
*
122+
* A taint-tracking configuration for unvalidated user input that is used to run an external process.
123+
*/
124+
deprecated class RemoteUserInputToArgumentToExecFlowConfig extends TaintTracking::Configuration {
125+
RemoteUserInputToArgumentToExecFlowConfig() {
126+
this = "ExecCommon::RemoteUserInputToArgumentToExecFlowConfig"
127+
}
128+
129+
override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
130+
131+
override predicate isSink(DataFlow::Node sink) { sink instanceof CommandInjectionSink }
132+
133+
override predicate isSanitizer(DataFlow::Node node) { node instanceof CommandInjectionSanitizer }
134+
135+
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
136+
any(CommandInjectionAdditionalTaintStep s).step(n1, n2)
137+
}
86138
}

java/ql/src/Security/CWE/CWE-078/ExecTainted.ql

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@
1313
*/
1414

1515
import java
16-
import semmle.code.java.dataflow.FlowSources
17-
import semmle.code.java.security.ExternalProcess
1816
import semmle.code.java.security.CommandLineQuery
1917
import RemoteUserInputToArgumentToExecFlow::PathGraph
2018

2119
from
2220
RemoteUserInputToArgumentToExecFlow::PathNode source,
23-
RemoteUserInputToArgumentToExecFlow::PathNode sink, ArgumentToExec execArg
21+
RemoteUserInputToArgumentToExecFlow::PathNode sink, Expr execArg
2422
where execIsTainted(source, sink, execArg)
2523
select execArg, source, sink, "This command line depends on a $@.", source.getNode(),
2624
"user-provided value"

java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,12 @@
1212
* external/cwe/cwe-088
1313
*/
1414

15-
import semmle.code.java.Expr
16-
import semmle.code.java.dataflow.FlowSources
17-
import semmle.code.java.security.ExternalProcess
18-
import semmle.code.java.security.CommandArguments
19-
20-
module LocalUserInputToArgumentToExecFlowConfig implements DataFlow::ConfigSig {
21-
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
22-
23-
predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ArgumentToExec }
24-
25-
predicate isBarrier(DataFlow::Node node) {
26-
node.getType() instanceof PrimitiveType
27-
or
28-
node.getType() instanceof BoxedType
29-
or
30-
isSafeCommandArgument(node.asExpr())
31-
}
32-
}
33-
34-
module LocalUserInputToArgumentToExecFlow =
35-
TaintTracking::Global<LocalUserInputToArgumentToExecFlowConfig>;
36-
15+
import semmle.code.java.security.CommandLineQuery
3716
import LocalUserInputToArgumentToExecFlow::PathGraph
3817

3918
from
4019
LocalUserInputToArgumentToExecFlow::PathNode source,
41-
LocalUserInputToArgumentToExecFlow::PathNode sink, ArgumentToExec execArg
42-
where
43-
LocalUserInputToArgumentToExecFlow::flowPath(source, sink) and
44-
sink.getNode().asExpr() = execArg
45-
select execArg, source, sink, "This command line depends on a $@.", source.getNode(),
46-
"user-provided value"
20+
LocalUserInputToArgumentToExecFlow::PathNode sink
21+
where LocalUserInputToArgumentToExecFlow::flowPath(source, sink)
22+
select sink.getNode().asExpr(), source, sink, "This command line depends on a $@.",
23+
source.getNode(), "user-provided value"

java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
*/
1414

1515
import java
16-
import semmle.code.java.security.ExternalProcess
1716
import semmle.code.java.security.CommandLineQuery
1817

1918
/**

java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,14 @@
1313
*/
1414

1515
import java
16-
import semmle.code.java.dataflow.FlowSources
17-
import semmle.code.java.security.ExternalProcess
1816
import semmle.code.java.security.CommandLineQuery
19-
import JSchOSInjection
2017
import RemoteUserInputToArgumentToExecFlow::PathGraph
18+
import JSchOSInjection
2119

2220
// This is a clone of query `java/command-line-injection` that also includes experimental sinks.
2321
from
2422
RemoteUserInputToArgumentToExecFlow::PathNode source,
25-
RemoteUserInputToArgumentToExecFlow::PathNode sink, ArgumentToExec execArg
23+
RemoteUserInputToArgumentToExecFlow::PathNode sink, Expr execArg
2624
where execIsTainted(source, sink, execArg)
2725
select execArg, source, sink, "This command line depends on a $@.", source.getNode(),
2826
"user-provided value"

0 commit comments

Comments
 (0)