Skip to content

Commit 9fd1606

Browse files
committed
Model java.util.Optional
1 parent d719a1e commit 9fd1606

File tree

5 files changed

+183
-0
lines changed

5 files changed

+183
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ private module Frameworks {
8282
private import semmle.code.java.frameworks.guava.Guava
8383
private import semmle.code.java.frameworks.jackson.JacksonSerializability
8484
private import semmle.code.java.frameworks.JaxWS
85+
private import semmle.code.java.frameworks.Optional
8586
private import semmle.code.java.frameworks.spring.SpringHttp
8687
private import semmle.code.java.frameworks.spring.SpringWebClient
8788
private import semmle.code.java.security.ResponseSplitting
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/** Definitions related to `java.util.Optional`. */
2+
3+
import semmle.code.java.dataflow.ExternalFlow
4+
5+
private class OptionalModel extends SummaryModelCsv {
6+
override predicate row(string s) {
7+
s =
8+
[
9+
"java.util;Optional;false;filter;;;Element of Argument[-1];Element of ReturnValue;value",
10+
"java.util;Optional;false;get;;;Element of Argument[-1];ReturnValue;value",
11+
"java.util;Optional;false;of;;;Argument[0];Element of ReturnValue;value",
12+
"java.util;Optional;false;ofNullable;;;Argument[0];Element of ReturnValue;value",
13+
"java.util;Optional;false;or;;;Element of Argument[-1];Element of ReturnValue;value",
14+
"java.util;Optional;false;orElse;;;Element of Argument[-1];ReturnValue;value",
15+
"java.util;Optional;false;orElse;;;Argument[0];ReturnValue;value",
16+
"java.util;Optional;false;orElseGet;;;Element of Argument[-1];ReturnValue;value",
17+
"java.util;Optional;false;orElseThrow;;;Element of Argument[-1];ReturnValue;value",
18+
"java.util;Optional;false;stream;;;Element of Argument[-1];Element of ReturnValue;value"
19+
]
20+
}
21+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package generatedtest;
2+
3+
import java.util.Optional;
4+
import java.util.stream.Stream;
5+
6+
// Test case generated by GenerateFlowTestCase.ql
7+
public class Test {
8+
9+
Object getElement(Optional<Object> container) { return container.get(); }
10+
Object getStreamElement(Stream<Object> container) { return null; /* Modelled in .ql file */ }
11+
Optional<Object> newWithElement(Object element) { return Optional.of(element); }
12+
Object source() { return null; }
13+
void sink(Object o) { }
14+
15+
public void test() {
16+
17+
{
18+
// "java.util;Optional;false;filter;;;Element of Argument[-1];Element of ReturnValue;value"
19+
Optional<Object> out = null;
20+
Optional<Object> in = newWithElement(source());
21+
out = in.filter(null);
22+
sink(getElement(out)); // $hasValueFlow
23+
}
24+
{
25+
// "java.util;Optional;false;get;;;Element of Argument[-1];ReturnValue;value"
26+
Object out = null;
27+
Optional<Object> in = newWithElement(source());
28+
out = in.get();
29+
sink(out); // $hasValueFlow
30+
}
31+
{
32+
// "java.util;Optional;false;of;;;Argument[0];Element of ReturnValue;value"
33+
Optional<Object> out = null;
34+
Object in = (Object)source();
35+
out = Optional.of(in);
36+
sink(getElement(out)); // $hasValueFlow
37+
}
38+
{
39+
// "java.util;Optional;false;ofNullable;;;Argument[0];Element of ReturnValue;value"
40+
Optional<Object> out = null;
41+
Object in = (Object)source();
42+
out = Optional.ofNullable(in);
43+
sink(getElement(out)); // $hasValueFlow
44+
}
45+
{
46+
// "java.util;Optional;false;or;;;Element of Argument[-1];Element of ReturnValue;value"
47+
Optional<Object> out = null;
48+
Optional<Object> in = newWithElement(source());
49+
out = in.or(null);
50+
sink(getElement(out)); // $hasValueFlow
51+
}
52+
{
53+
// "java.util;Optional;false;orElse;;;Argument[0];ReturnValue;value"
54+
Object out = null;
55+
Object in = (Object)source();
56+
Optional<Object> instance = null;
57+
out = instance.orElse(in);
58+
sink(out); // $hasValueFlow
59+
}
60+
{
61+
// "java.util;Optional;false;orElse;;;Element of Argument[-1];ReturnValue;value"
62+
Object out = null;
63+
Optional<Object> in = newWithElement(source());
64+
out = in.orElse(null);
65+
sink(out); // $hasValueFlow
66+
}
67+
{
68+
// "java.util;Optional;false;orElseGet;;;Element of Argument[-1];ReturnValue;value"
69+
Object out = null;
70+
Optional<Object> in = newWithElement(source());
71+
out = in.orElseGet(null);
72+
sink(out); // $hasValueFlow
73+
}
74+
{
75+
// "java.util;Optional;false;orElseThrow;;;Element of Argument[-1];ReturnValue;value"
76+
Object out = null;
77+
Optional<Object> in = newWithElement(source());
78+
out = in.orElseThrow(null);
79+
sink(out); // $hasValueFlow
80+
}
81+
{
82+
// "java.util;Optional;false;orElseThrow;;;Element of Argument[-1];ReturnValue;value"
83+
Object out = null;
84+
Optional<Object> in = newWithElement(source());
85+
out = in.orElseThrow();
86+
sink(out); // $hasValueFlow
87+
}
88+
{
89+
// "java.util;Optional;false;stream;;;Element of Argument[-1];Element of ReturnValue;value"
90+
Stream<Object> out = null;
91+
Optional<Object> in = newWithElement(source());
92+
out = in.stream();
93+
sink(getStreamElement(out)); // $hasValueFlow
94+
}
95+
96+
}
97+
98+
}

java/ql/test/library-tests/optional/test.expected

Whitespace-only changes.
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import java
2+
import semmle.code.java.dataflow.DataFlow
3+
import semmle.code.java.dataflow.ExternalFlow
4+
import semmle.code.java.dataflow.TaintTracking
5+
import TestUtilities.InlineExpectationsTest
6+
7+
class SummaryModelTest extends SummaryModelCsv {
8+
override predicate row(string row) {
9+
row =
10+
[
11+
//"package;type;overrides;name;signature;ext;inputspec;outputspec;kind",
12+
"generatedtest;Test;false;getStreamElement;;;Element of Argument[0];ReturnValue;value"
13+
]
14+
}
15+
}
16+
17+
class ValueFlowConf extends DataFlow::Configuration {
18+
ValueFlowConf() { this = "qltest:valueFlowConf" }
19+
20+
override predicate isSource(DataFlow::Node n) {
21+
n.asExpr().(MethodAccess).getMethod().hasName("source")
22+
}
23+
24+
override predicate isSink(DataFlow::Node n) {
25+
n.asExpr().(Argument).getCall().getCallee().hasName("sink")
26+
}
27+
}
28+
29+
class TaintFlowConf extends TaintTracking::Configuration {
30+
TaintFlowConf() { this = "qltest:taintFlowConf" }
31+
32+
override predicate isSource(DataFlow::Node n) {
33+
n.asExpr().(MethodAccess).getMethod().hasName("source")
34+
}
35+
36+
override predicate isSink(DataFlow::Node n) {
37+
n.asExpr().(Argument).getCall().getCallee().hasName("sink")
38+
}
39+
}
40+
41+
class HasFlowTest extends InlineExpectationsTest {
42+
HasFlowTest() { this = "HasFlowTest" }
43+
44+
override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] }
45+
46+
override predicate hasActualResult(Location location, string element, string tag, string value) {
47+
tag = "hasValueFlow" and
48+
exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) |
49+
sink.getLocation() = location and
50+
element = sink.toString() and
51+
value = ""
52+
)
53+
or
54+
tag = "hasTaintFlow" and
55+
exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf |
56+
conf.hasFlow(src, sink) and not any(ValueFlowConf c).hasFlow(src, sink)
57+
|
58+
sink.getLocation() = location and
59+
element = sink.toString() and
60+
value = ""
61+
)
62+
}
63+
}

0 commit comments

Comments
 (0)