mirror of
https://github.com/github/codeql.git
synced 2025-12-24 12:46:34 +01:00
395 lines
12 KiB
Plaintext
395 lines
12 KiB
Plaintext
/**
|
|
* Contains predicates and classes relating to support methods for tests, such as the `source()` and `sink()`.
|
|
*/
|
|
|
|
import java
|
|
private import semmle.code.java.dataflow.internal.DataFlowUtil
|
|
private import semmle.code.java.dataflow.ExternalFlow
|
|
private import semmle.code.java.dataflow.FlowSummary
|
|
private import semmle.code.java.dataflow.internal.FlowSummaryImpl
|
|
private import FlowTestCaseUtils
|
|
private import FlowTestCase
|
|
|
|
/**
|
|
* Returns a valid Java token naming the field `fc`.
|
|
*/
|
|
private string getFieldToken(FieldContent fc) {
|
|
result =
|
|
fc.getField().getDeclaringType().getSourceDeclaration().getName() + "_" +
|
|
fc.getField().getName()
|
|
}
|
|
|
|
/**
|
|
* Returns a valid Java token naming the synthetic field `fc`,
|
|
* assuming that the name of that field consists only of characters valid in a Java identifier and `.`.
|
|
*/
|
|
private string getSyntheticFieldToken(SyntheticFieldContent fc) {
|
|
exists(string name, int parts |
|
|
name = fc.getField() and
|
|
parts = count(name.splitAt("."))
|
|
|
|
|
if parts = 1
|
|
then result = name
|
|
else result = name.splitAt(".", parts - 2) + "_" + name.splitAt(".", parts - 1)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Returns a token suitable for incorporation into a Java method name describing content `c`.
|
|
*/
|
|
private string contentToken(Content c) {
|
|
c instanceof ArrayContent and result = "ArrayElement"
|
|
or
|
|
c instanceof CollectionContent and result = "Element"
|
|
or
|
|
c instanceof MapKeyContent and result = "MapKey"
|
|
or
|
|
c instanceof MapValueContent and result = "MapValue"
|
|
or
|
|
result = getFieldToken(c)
|
|
or
|
|
result = getSyntheticFieldToken(c)
|
|
}
|
|
|
|
/**
|
|
* Returns the `content` wrapped by `component`, if any.
|
|
*/
|
|
private Content getContent(SummaryComponent component) {
|
|
component = SummaryComponent::content(result)
|
|
}
|
|
|
|
/** Contains utility predicates for getting relevant support methods. */
|
|
module SupportMethod {
|
|
/** Gets a generator method for the content type of the head of the component stack `c`. */
|
|
GenMethod genMethodForContent(SummaryComponentStack c) {
|
|
result = genMethodFor(any(VoidType v), c)
|
|
}
|
|
|
|
/** Gets a generator method for the type `t` and the content type of the head of the component stack `c`. */
|
|
GenMethod genMethodFor(Type t, SummaryComponentStack c) {
|
|
result =
|
|
min(GenMethod g |
|
|
g = min(GenMethod g1 | g1.appliesTo(t, getContent(c.head())) | g1 order by g1.getPriority())
|
|
)
|
|
}
|
|
|
|
/** Gets a getter method for the content type of the head of the component stack `c`. */
|
|
GetMethod getMethodForContent(SummaryComponentStack c) {
|
|
result = getMethodFor(any(VoidType v), c)
|
|
}
|
|
|
|
/** Gets a getter method for the type `t` and the content type of the head of the component stack `c`. */
|
|
GetMethod getMethodFor(Type t, SummaryComponentStack c) {
|
|
result =
|
|
min(GetMethod g |
|
|
g = min(GetMethod g1 | g1.appliesTo(t, getContent(c.head())) | g1 order by g1.getPriority())
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A support method for tests, such as `source()` or `sink()`.
|
|
*/
|
|
bindingset[this]
|
|
abstract class SupportMethod extends string {
|
|
/** Gets an import that is required for this support method. */
|
|
string getARequiredImport() { none() }
|
|
|
|
/** Gets the Java definition of this support method, if one is necessary. */
|
|
string getDefinition() { none() }
|
|
|
|
/** Gets the priority of this support method. Lower priorities are preferred when multiple support methods apply. */
|
|
bindingset[this]
|
|
int getPriority() { result = 50 }
|
|
|
|
/**
|
|
* Gets the CSV row describing this support method if it is needed to set up the output for this test.
|
|
*
|
|
* For example, `newWithMapValue` will propagate a value from `Argument[0]` to `MapValue of ReturnValue`, and `getMapValue`
|
|
* will do the opposite.
|
|
*/
|
|
string getCsvModel() { none() }
|
|
}
|
|
|
|
/**
|
|
* The method `source()` which is considered as the source for the flow test.
|
|
*/
|
|
class SourceMethod extends SupportMethod {
|
|
SourceMethod() { this = "source" }
|
|
|
|
override string getDefinition() { result = "Object source() { return null; }" }
|
|
}
|
|
|
|
/**
|
|
* The method `sink()` which is considered as the sink for the flow test.
|
|
*/
|
|
class SinkMethod extends SupportMethod {
|
|
SinkMethod() { this = "sink" }
|
|
|
|
override string getDefinition() { result = "void sink(Object o) { }" }
|
|
}
|
|
|
|
/**
|
|
* A method for getting content from a type.
|
|
*/
|
|
bindingset[this]
|
|
abstract class GetMethod extends SupportMethod {
|
|
/**
|
|
* Holds if this get method can be used to get the content `c` from the type `t`.
|
|
*/
|
|
abstract predicate appliesTo(Type t, Content c);
|
|
|
|
/**
|
|
* Gets the call to get the content from the argument `arg`.
|
|
*/
|
|
bindingset[this, arg]
|
|
abstract string getCall(string arg);
|
|
}
|
|
|
|
private class DefaultGetMethod extends GetMethod {
|
|
Content c;
|
|
|
|
DefaultGetMethod() { this = "DefaultGet" + contentToken(c) }
|
|
|
|
string getName() { result = "get" + contentToken(c) }
|
|
|
|
override int getPriority() { result = 999 }
|
|
|
|
override predicate appliesTo(Type t, Content c1) {
|
|
c = c1 and
|
|
// suppress unused variable warning
|
|
t = [any(TestCase tc).getOutputType(), any(VoidType v)]
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = this.getName() + "(" + arg + ")" }
|
|
|
|
override string getDefinition() {
|
|
result = "Object get" + contentToken(c) + "(Object container) { return null; }"
|
|
}
|
|
|
|
override string getCsvModel() {
|
|
result =
|
|
"generatedtest;Test;false;" + this.getName() + ";;;" +
|
|
getComponentSpec(SummaryComponent::content(c)) + " of Argument[0];ReturnValue;value"
|
|
}
|
|
}
|
|
|
|
private class ListGetMethod extends GetMethod {
|
|
ListGetMethod() { this = "listgetmethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
t.(RefType).getASourceSupertype*().hasQualifiedName("java.lang", "Iterable") and
|
|
c instanceof CollectionContent
|
|
}
|
|
|
|
override string getDefinition() {
|
|
result = "<T> T getElement(Iterable<T> it) { return it.iterator().next(); }"
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "getElement(" + arg + ")" }
|
|
}
|
|
|
|
private class IteratorGetMethod extends GetMethod {
|
|
IteratorGetMethod() { this = "iteratorgetmethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
t.(RefType).getASourceSupertype*().hasQualifiedName("java.util", "Iterator") and
|
|
c instanceof CollectionContent
|
|
}
|
|
|
|
override string getDefinition() {
|
|
result = "<T> T getElement(Iterator<T> it) { return it.next(); }"
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "getElement(" + arg + ")" }
|
|
}
|
|
|
|
private class OptionalGetMethod extends GetMethod {
|
|
OptionalGetMethod() { this = "optionalgetmethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
t.(RefType).getSourceDeclaration().hasQualifiedName("java.util", "Optional") and
|
|
c instanceof CollectionContent
|
|
}
|
|
|
|
override string getDefinition() { result = "<T> T getElement(Optional<T> o) { return o.get(); }" }
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "getElement(" + arg + ")" }
|
|
}
|
|
|
|
private class MapGetKeyMethod extends GetMethod {
|
|
MapGetKeyMethod() { this = "mapgetkeymethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
t.(RefType).getASourceSupertype*().hasQualifiedName("java.util", "Map") and
|
|
c instanceof MapKeyContent
|
|
}
|
|
|
|
override string getDefinition() {
|
|
result = "<K> K getMapKey(Map<K,?> map) { return map.keySet().iterator().next(); }"
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "getMapKey(" + arg + ")" }
|
|
}
|
|
|
|
private class MapValueGetMethod extends GetMethod {
|
|
MapValueGetMethod() { this = "MapValueGetMethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
t.(RefType).getASourceSupertype*().hasQualifiedName("java.util", "Map") and
|
|
c instanceof MapValueContent
|
|
}
|
|
|
|
override string getDefinition() {
|
|
result = "<V> V getMapValue(Map<?,V> map) { return map.get(null); }"
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "getMapValue(" + arg + ")" }
|
|
}
|
|
|
|
private class ArrayGetMethod extends GetMethod {
|
|
ArrayGetMethod() { this = "arraygetmethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
t instanceof Array and
|
|
c instanceof ArrayContent
|
|
}
|
|
|
|
override string getDefinition() {
|
|
result = "<T> T getArrayElement(T[] array) { return array[0]; }"
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "getArrayElement(" + arg + ")" }
|
|
}
|
|
|
|
/**
|
|
* A method for generating a type with content.
|
|
*/
|
|
bindingset[this]
|
|
abstract class GenMethod extends SupportMethod {
|
|
/**
|
|
* Holds if this generator method can be used to generate a new `t` that contains content `c`.
|
|
*/
|
|
abstract predicate appliesTo(Type t, Content c);
|
|
|
|
/**
|
|
* Gets the call to generate an object with content `arg`.
|
|
*/
|
|
bindingset[this, arg]
|
|
abstract string getCall(string arg);
|
|
}
|
|
|
|
private class DefaultGenMethod extends GenMethod {
|
|
Content c;
|
|
|
|
DefaultGenMethod() { this = "DefaultGen" + contentToken(c) }
|
|
|
|
string getName() { result = "newWith" + contentToken(c) }
|
|
|
|
override int getPriority() { result = 999 }
|
|
|
|
override predicate appliesTo(Type t, Content c1) {
|
|
c = c1 and
|
|
// suppress unused variable warning
|
|
t = [any(TestCase tc).getInputType(), any(VoidType v)]
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = this.getName() + "(" + arg + ")" }
|
|
|
|
override string getDefinition() {
|
|
result = "Object newWith" + contentToken(c) + "(Object element) { return null; }"
|
|
}
|
|
|
|
override string getCsvModel() {
|
|
result =
|
|
"generatedtest;Test;false;" + this.getName() + ";;;Argument[0];" +
|
|
getComponentSpec(SummaryComponent::content(c)) + " of ReturnValue;value"
|
|
}
|
|
}
|
|
|
|
private class ListGenMethod extends GenMethod {
|
|
ListGenMethod() { this = "listgenmethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
exists(GenericType list | list.hasQualifiedName("java.util", "List") |
|
|
t = list or list.getAParameterizedType().getASupertype*() = t
|
|
) and
|
|
c instanceof CollectionContent
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "List.of(" + arg + ")" }
|
|
}
|
|
|
|
private class OptionalGenMethod extends GenMethod {
|
|
OptionalGenMethod() { this = "optionalgenmethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
exists(GenericType list | list.hasQualifiedName("java.util", "List") |
|
|
list.getAParameterizedType().getASupertype*() = t
|
|
) and
|
|
c instanceof CollectionContent
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "Optional.of(" + arg + ")" }
|
|
}
|
|
|
|
private class MapGenKeyMethod extends GenMethod {
|
|
MapGenKeyMethod() { this = "mapkeygenmethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
exists(GenericType map | map.hasQualifiedName("java.util", "Map") |
|
|
map.getAParameterizedType().getASupertype*() = t
|
|
) and
|
|
c instanceof MapKeyContent
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "Map.of(" + arg + ", null)" }
|
|
}
|
|
|
|
private class MapGenValueMethod extends GenMethod {
|
|
MapGenValueMethod() { this = "mapvaluegenmethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
exists(GenericType map | map.hasQualifiedName("java.util", "Map") |
|
|
map.getAParameterizedType().getASupertype*() = t
|
|
) and
|
|
c instanceof MapValueContent
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) { result = "Map.of(null, " + arg + ")" }
|
|
}
|
|
|
|
string getConvertExprIfNotObject(RefType t) {
|
|
if t.hasQualifiedName("java.lang", "Object")
|
|
then result = ""
|
|
else result = "(" + getShortNameIfPossible(t) + ")"
|
|
}
|
|
|
|
private class ArrayGenMethod extends GenMethod {
|
|
Array type;
|
|
|
|
ArrayGenMethod() { this = type.getName() + "genmethod" }
|
|
|
|
override predicate appliesTo(Type t, Content c) {
|
|
t = type and
|
|
c instanceof ArrayContent
|
|
}
|
|
|
|
bindingset[arg]
|
|
override string getCall(string arg) {
|
|
result = "{" + getConvertExprIfNotObject(type.getElementType()) + arg + "}"
|
|
}
|
|
}
|