mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Merge pull request #6137 from smowton/smowton/feature/java-util-optional
Java: Model java.util.Optional
This commit is contained in:
2
java/change-notes/2021-06-22-util-optional.md
Normal file
2
java/change-notes/2021-06-22-util-optional.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
lgtm,codescanning
|
||||||
|
* Models for java.util.Optional added. This may lead to more results whenever a data-flow path involves this type.
|
||||||
@@ -82,6 +82,7 @@ private module Frameworks {
|
|||||||
private import semmle.code.java.frameworks.guava.Guava
|
private import semmle.code.java.frameworks.guava.Guava
|
||||||
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
private import semmle.code.java.frameworks.jackson.JacksonSerializability
|
||||||
private import semmle.code.java.frameworks.JaxWS
|
private import semmle.code.java.frameworks.JaxWS
|
||||||
|
private import semmle.code.java.frameworks.Optional
|
||||||
private import semmle.code.java.frameworks.spring.SpringHttp
|
private import semmle.code.java.frameworks.spring.SpringHttp
|
||||||
private import semmle.code.java.frameworks.spring.SpringWebClient
|
private import semmle.code.java.frameworks.spring.SpringWebClient
|
||||||
private import semmle.code.java.security.ResponseSplitting
|
private import semmle.code.java.security.ResponseSplitting
|
||||||
|
|||||||
21
java/ql/src/semmle/code/java/frameworks/Optional.qll
Normal file
21
java/ql/src/semmle/code/java/frameworks/Optional.qll
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/** Definitions related to `java.util.Optional`. */
|
||||||
|
|
||||||
|
import semmle.code.java.dataflow.ExternalFlow
|
||||||
|
|
||||||
|
private class OptionalModel extends SummaryModelCsv {
|
||||||
|
override predicate row(string s) {
|
||||||
|
s =
|
||||||
|
[
|
||||||
|
"java.util;Optional;false;filter;;;Element of Argument[-1];Element of ReturnValue;value",
|
||||||
|
"java.util;Optional;false;get;;;Element of Argument[-1];ReturnValue;value",
|
||||||
|
"java.util;Optional;false;of;;;Argument[0];Element of ReturnValue;value",
|
||||||
|
"java.util;Optional;false;ofNullable;;;Argument[0];Element of ReturnValue;value",
|
||||||
|
"java.util;Optional;false;or;;;Element of Argument[-1];Element of ReturnValue;value",
|
||||||
|
"java.util;Optional;false;orElse;;;Element of Argument[-1];ReturnValue;value",
|
||||||
|
"java.util;Optional;false;orElse;;;Argument[0];ReturnValue;value",
|
||||||
|
"java.util;Optional;false;orElseGet;;;Element of Argument[-1];ReturnValue;value",
|
||||||
|
"java.util;Optional;false;orElseThrow;;;Element of Argument[-1];ReturnValue;value",
|
||||||
|
"java.util;Optional;false;stream;;;Element of Argument[-1];Element of ReturnValue;value"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
98
java/ql/test/library-tests/optional/Test.java
Normal file
98
java/ql/test/library-tests/optional/Test.java
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package generatedtest;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
// Test case generated by GenerateFlowTestCase.ql
|
||||||
|
public class Test {
|
||||||
|
|
||||||
|
Object getElement(Optional<Object> container) { return container.get(); }
|
||||||
|
Object getStreamElement(Stream<Object> container) { return null; /* Modelled in .ql file */ }
|
||||||
|
Optional<Object> newWithElement(Object element) { return Optional.of(element); }
|
||||||
|
Object source() { return null; }
|
||||||
|
void sink(Object o) { }
|
||||||
|
|
||||||
|
public void test() {
|
||||||
|
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;filter;;;Element of Argument[-1];Element of ReturnValue;value"
|
||||||
|
Optional<Object> out = null;
|
||||||
|
Optional<Object> in = newWithElement(source());
|
||||||
|
out = in.filter(null);
|
||||||
|
sink(getElement(out)); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;get;;;Element of Argument[-1];ReturnValue;value"
|
||||||
|
Object out = null;
|
||||||
|
Optional<Object> in = newWithElement(source());
|
||||||
|
out = in.get();
|
||||||
|
sink(out); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;of;;;Argument[0];Element of ReturnValue;value"
|
||||||
|
Optional<Object> out = null;
|
||||||
|
Object in = (Object)source();
|
||||||
|
out = Optional.of(in);
|
||||||
|
sink(getElement(out)); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;ofNullable;;;Argument[0];Element of ReturnValue;value"
|
||||||
|
Optional<Object> out = null;
|
||||||
|
Object in = (Object)source();
|
||||||
|
out = Optional.ofNullable(in);
|
||||||
|
sink(getElement(out)); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;or;;;Element of Argument[-1];Element of ReturnValue;value"
|
||||||
|
Optional<Object> out = null;
|
||||||
|
Optional<Object> in = newWithElement(source());
|
||||||
|
out = in.or(null);
|
||||||
|
sink(getElement(out)); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;orElse;;;Argument[0];ReturnValue;value"
|
||||||
|
Object out = null;
|
||||||
|
Object in = (Object)source();
|
||||||
|
Optional<Object> instance = null;
|
||||||
|
out = instance.orElse(in);
|
||||||
|
sink(out); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;orElse;;;Element of Argument[-1];ReturnValue;value"
|
||||||
|
Object out = null;
|
||||||
|
Optional<Object> in = newWithElement(source());
|
||||||
|
out = in.orElse(null);
|
||||||
|
sink(out); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;orElseGet;;;Element of Argument[-1];ReturnValue;value"
|
||||||
|
Object out = null;
|
||||||
|
Optional<Object> in = newWithElement(source());
|
||||||
|
out = in.orElseGet(null);
|
||||||
|
sink(out); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;orElseThrow;;;Element of Argument[-1];ReturnValue;value"
|
||||||
|
Object out = null;
|
||||||
|
Optional<Object> in = newWithElement(source());
|
||||||
|
out = in.orElseThrow(null);
|
||||||
|
sink(out); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;orElseThrow;;;Element of Argument[-1];ReturnValue;value"
|
||||||
|
Object out = null;
|
||||||
|
Optional<Object> in = newWithElement(source());
|
||||||
|
out = in.orElseThrow();
|
||||||
|
sink(out); // $hasValueFlow
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// "java.util;Optional;false;stream;;;Element of Argument[-1];Element of ReturnValue;value"
|
||||||
|
Stream<Object> out = null;
|
||||||
|
Optional<Object> in = newWithElement(source());
|
||||||
|
out = in.stream();
|
||||||
|
sink(getStreamElement(out)); // $hasValueFlow
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
0
java/ql/test/library-tests/optional/test.expected
Normal file
0
java/ql/test/library-tests/optional/test.expected
Normal file
63
java/ql/test/library-tests/optional/test.ql
Normal file
63
java/ql/test/library-tests/optional/test.ql
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import java
|
||||||
|
import semmle.code.java.dataflow.DataFlow
|
||||||
|
import semmle.code.java.dataflow.ExternalFlow
|
||||||
|
import semmle.code.java.dataflow.TaintTracking
|
||||||
|
import TestUtilities.InlineExpectationsTest
|
||||||
|
|
||||||
|
class SummaryModelTest extends SummaryModelCsv {
|
||||||
|
override predicate row(string row) {
|
||||||
|
row =
|
||||||
|
[
|
||||||
|
//"package;type;overrides;name;signature;ext;inputspec;outputspec;kind",
|
||||||
|
"generatedtest;Test;false;getStreamElement;;;Element of Argument[0];ReturnValue;value"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ValueFlowConf extends DataFlow::Configuration {
|
||||||
|
ValueFlowConf() { this = "qltest:valueFlowConf" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node n) {
|
||||||
|
n.asExpr().(MethodAccess).getMethod().hasName("source")
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node n) {
|
||||||
|
n.asExpr().(Argument).getCall().getCallee().hasName("sink")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TaintFlowConf extends TaintTracking::Configuration {
|
||||||
|
TaintFlowConf() { this = "qltest:taintFlowConf" }
|
||||||
|
|
||||||
|
override predicate isSource(DataFlow::Node n) {
|
||||||
|
n.asExpr().(MethodAccess).getMethod().hasName("source")
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate isSink(DataFlow::Node n) {
|
||||||
|
n.asExpr().(Argument).getCall().getCallee().hasName("sink")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HasFlowTest extends InlineExpectationsTest {
|
||||||
|
HasFlowTest() { this = "HasFlowTest" }
|
||||||
|
|
||||||
|
override string getARelevantTag() { result = ["hasValueFlow", "hasTaintFlow"] }
|
||||||
|
|
||||||
|
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||||
|
tag = "hasValueFlow" and
|
||||||
|
exists(DataFlow::Node src, DataFlow::Node sink, ValueFlowConf conf | conf.hasFlow(src, sink) |
|
||||||
|
sink.getLocation() = location and
|
||||||
|
element = sink.toString() and
|
||||||
|
value = ""
|
||||||
|
)
|
||||||
|
or
|
||||||
|
tag = "hasTaintFlow" and
|
||||||
|
exists(DataFlow::Node src, DataFlow::Node sink, TaintFlowConf conf |
|
||||||
|
conf.hasFlow(src, sink) and not any(ValueFlowConf c).hasFlow(src, sink)
|
||||||
|
|
|
||||||
|
sink.getLocation() = location and
|
||||||
|
element = sink.toString() and
|
||||||
|
value = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user