Merge branch 'main' into kaeluka/java-automodel-variadic-args

This commit is contained in:
Stephan Brandauer
2023-08-09 09:02:32 +02:00
committed by GitHub
117 changed files with 11828 additions and 1070 deletions

View File

@@ -0,0 +1,4 @@
---
category: majorAnalysis
---
* Improved support for flow through captured variables that properly adheres to inter-procedural control flow.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Fixed a typo in the `StdlibRandomSource` class in `RandomDataSource.qll`, which caused the class to improperly model calls to the `nextBytes` method. Queries relying on `StdlibRandomSource` may see an increase in results.

View File

@@ -176,7 +176,6 @@ extensions:
- ["java.lang", "Object", "getClass", "()", "summary", "manual"]
- ["java.lang", "Object", "hashCode", "()", "summary", "manual"]
- ["java.lang", "Object", "toString", "()", "summary", "manual"]
- ["java.lang", "Runnable", "run", "()", "summary", "manual"]
- ["java.lang", "Runtime", "getRuntime", "()", "summary", "manual"]
- ["java.lang", "String", "compareTo", "(String)", "summary", "manual"]
- ["java.lang", "String", "contains", "(CharSequence)", "summary", "manual"]

View File

@@ -17,11 +17,11 @@ extensions:
- ["java.nio.file", "Files", False, "createTempFile", "(Path,String,String,FileAttribute[])", "", "Argument[0]", "path-injection", "manual"]
- ["java.nio.file", "Files", False, "delete", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "deleteIfExists", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "deleteIfExists", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "getFileStore", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] # the FileStore class is unlikely to be used for later sanitization
- ["java.nio.file", "Files", False, "lines", "(Path,Charset)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "lines", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "move", "", "", "Argument[1]", "path-injection", "manual"]
- ["java.nio.file", "Files", False, "move", "(Path,Path,CopyOption[])", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "newBufferedReader", "(Path,Charset)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "newBufferedReader", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "newBufferedWriter", "", "", "Argument[0]", "path-injection", "manual"]
@@ -37,11 +37,6 @@ extensions:
- ["java.nio.file", "Files", False, "write", "", "", "Argument[1]", "file-content-store", "manual"]
- ["java.nio.file", "Files", False, "writeString", "", "", "Argument[0]", "path-injection", "manual"]
- ["java.nio.file", "Files", False, "writeString", "", "", "Argument[1]", "file-content-store", "manual"]
- ["java.nio.file", "Files", True, "move", "(Path,Path,CopyOption[])", "", "Argument[1]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", True, "move", "(Path,Path,CopyOption[])", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", True, "delete", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", True, "newInputStream", "(Path,OpenOption[])", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", True, "newOutputStream", "(Path,OpenOption[])", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "FileSystem", False, "getPath", "", "", "Argument[0..1]", "path-injection", "manual"] # old PathCreation
- ["java.nio.file", "FileSystems", False, "newFileSystem", "(URI,Map)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "FileSystems", False, "newFileSystem", "(URI,Map)", "", "Argument[0]", "request-forgery", "ai-manual"]

View File

@@ -0,0 +1,7 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: supportedThreatModels
data:
- ["default"] # The "default" threat model is always included.

View File

@@ -0,0 +1,23 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: threatModelGrouping
data:
# Default threat model
- ["remote", "default"]
- ["uri-path", "default"]
# Android threat models
- ["android-external-storage-dir", "android"]
- ["contentprovider", "android"]
# Remote threat models
- ["request", "remote"]
- ["response", "remote"]
# Local threat models
- ["database", "local"]
- ["cli", "local"]
- ["environment", "local"]
- ["file", "local"]

View File

@@ -16,4 +16,5 @@ dataExtensions:
- ext/*.model.yml
- ext/generated/*.model.yml
- ext/experimental/*.model.yml
- ext/threatmodels/*.model.yml
warnOnImplicitThis: true

View File

@@ -0,0 +1,31 @@
/**
* INTERNAL use only. This is an experimental API subject to change without notice.
*
* This module provides extensible predicates for configuring which kinds of MaD models
* are applicable to generic queries.
*/
private import ExternalFlowExtensions
/**
* Holds if the specified kind of source model is supported for the current query.
*/
extensible private predicate supportedThreatModels(string kind);
/**
* Holds if the specified kind of source model is containted within the specified group.
*/
extensible private predicate threatModelGrouping(string kind, string group);
/**
* Gets the threat models that are direct descendants of the specified kind/group.
*/
private string getChildThreatModel(string group) { threatModelGrouping(result, group) }
/**
* Holds if the source model kind `kind` is relevant for generic queries
* under the current threat model configuration.
*/
predicate sourceModelKindConfig(string kind) {
exists(string group | supportedThreatModels(group) and kind = getChildThreatModel*(group))
}

View File

@@ -101,6 +101,7 @@ abstract class SyntheticCallable extends string {
* A module for importing frameworks that define synthetic callables.
*/
private module SyntheticCallables {
private import semmle.code.java.dispatch.WrappedInvocation
private import semmle.code.java.frameworks.android.Intent
private import semmle.code.java.frameworks.Stream
}
@@ -170,6 +171,8 @@ class SummarizedCallableBase extends TSummarizedCallableBase {
}
}
class Provenance = Impl::Public::Provenance;
class SummarizedCallable = Impl::Public::SummarizedCallable;
class NeutralCallable = Impl::Public::NeutralCallable;

View File

@@ -1,3 +1,3 @@
private import DataFlowImplSpecific
private import codeql.dataflow.DataFlowImpl
private import codeql.dataflow.internal.DataFlowImpl
import MakeImpl<JavaDataFlow>

View File

@@ -1,3 +1,3 @@
private import DataFlowImplSpecific
private import codeql.dataflow.DataFlowImplCommon
private import codeql.dataflow.internal.DataFlowImplCommon
import MakeImplCommon<JavaDataFlow>

View File

@@ -2,7 +2,7 @@
* Provides Java-specific definitions for use in the data flow library.
*/
private import codeql.dataflow.DataFlowParameter
private import codeql.dataflow.DataFlow
module Private {
import DataFlowPrivate
@@ -13,7 +13,7 @@ module Public {
import DataFlowUtil
}
module JavaDataFlow implements DataFlowParameter {
module JavaDataFlow implements InputSig {
import Private
import Public

View File

@@ -55,7 +55,8 @@ private module Cached {
)
} or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
TFieldValueNode(Field f)
TFieldValueNode(Field f) or
TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn)
cached
newtype TContent =
@@ -64,6 +65,7 @@ private module Cached {
TCollectionContent() or
TMapKeyContent() or
TMapValueContent() or
TCapturedVariableContent(CapturedVariable v) or
TSyntheticFieldContent(SyntheticField s)
cached
@@ -73,6 +75,7 @@ private module Cached {
TCollectionContentApprox() or
TMapKeyContentApprox() or
TMapValueContentApprox() or
TCapturedVariableContentApprox(CapturedVariable v) or
TSyntheticFieldApproxContent()
}
@@ -127,6 +130,8 @@ module Public {
or
result = this.(ImplicitPostUpdateNode).getPreUpdateNode().getType()
or
result = this.(CaptureNode).getTypeImpl()
or
result = this.(FieldValueNode).getField().getType()
}
@@ -372,6 +377,7 @@ module Private {
result.asCallable() = n.(MallocNode).getClassInstanceExpr().getEnclosingCallable() or
result = nodeGetEnclosingCallable(n.(ImplicitPostUpdateNode).getPreUpdateNode()) or
result.asSummarizedCallable() = n.(FlowSummaryNode).getSummarizedCallable() or
result.asCallable() = n.(CaptureNode).getSynthesizedCaptureNode().getEnclosingCallable() or
result.asFieldScope() = n.(FieldValueNode).getField()
}
@@ -491,6 +497,28 @@ module Private {
c.asSummarizedCallable() = this.getSummarizedCallable() and pos = this.getPosition()
}
}
/**
* A synthesized data flow node representing a closure object that tracks
* captured variables.
*/
class CaptureNode extends Node, TCaptureNode {
private CaptureFlow::SynthesizedCaptureNode cn;
CaptureNode() { this = TCaptureNode(cn) }
CaptureFlow::SynthesizedCaptureNode getSynthesizedCaptureNode() { result = cn }
override Location getLocation() { result = cn.getLocation() }
override string toString() { result = cn.toString() }
Type getTypeImpl() {
exists(Variable v | cn.isVariableAccess(v) and result = v.getType())
or
cn.isInstanceAccess() and result = cn.getEnclosingCallable().getDeclaringType()
}
}
}
private import Private
@@ -520,3 +548,14 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNode {
override Node getPreUpdateNode() { result = pre }
}
private class CapturePostUpdateNode extends PostUpdateNode, CaptureNode {
private CaptureNode pre;
CapturePostUpdateNode() {
CaptureFlow::capturePostUpdateNode(this.getSynthesizedCaptureNode(),
pre.getSynthesizedCaptureNode())
}
override Node getPreUpdateNode() { result = pre }
}

View File

@@ -10,6 +10,7 @@ private import semmle.code.java.dataflow.FlowSummary
private import FlowSummaryImpl as FlowSummaryImpl
private import DataFlowImplConsistency
private import DataFlowNodes
private import codeql.dataflow.VariableCapture as VariableCapture
import DataFlowNodes::Private
private newtype TReturnKind = TNormalReturnKind()
@@ -51,26 +52,131 @@ private predicate fieldStep(Node node1, Node node2) {
)
}
/**
* Holds if data can flow from `node1` to `node2` through variable capture.
*/
private predicate variableCaptureStep(Node node1, ExprNode node2) {
exists(SsaImplicitInit closure, SsaVariable captured |
closure.captures(captured) and
node2.getExpr() = closure.getAFirstUse()
|
node1.asExpr() = captured.getAUse()
or
not exists(captured.getAUse()) and
exists(SsaVariable capturedDef | capturedDef = captured.getAnUltimateDefinition() |
capturedDef.(SsaImplicitInit).isParameterDefinition(node1.asParameter()) or
capturedDef.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() =
node1.asExpr() or
capturedDef.(SsaExplicitUpdate).getDefiningExpr().(AssignOp) = node1.asExpr()
)
private predicate closureFlowStep(Expr e1, Expr e2) {
simpleAstFlowStep(e1, e2)
or
exists(SsaVariable v |
v.getAUse() = e2 and
v.getAnUltimateDefinition().(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() =
e1
)
}
private module CaptureInput implements VariableCapture::InputSig {
private import java as J
class Location = J::Location;
class BasicBlock instanceof J::BasicBlock {
string toString() { result = super.toString() }
Callable getEnclosingCallable() { result = super.getEnclosingCallable() }
Location getLocation() { result = super.getLocation() }
}
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { bbIDominates(result, bb) }
BasicBlock getABasicBlockSuccessor(BasicBlock bb) {
result = bb.(J::BasicBlock).getABBSuccessor()
}
//TODO: support capture of `this` in lambdas
class CapturedVariable instanceof LocalScopeVariable {
CapturedVariable() {
2 <=
strictcount(J::Callable c |
c = this.getCallable() or c = this.getAnAccess().getEnclosingCallable()
)
}
string toString() { result = super.toString() }
Callable getCallable() { result = super.getCallable() }
Location getLocation() { result = super.getLocation() }
}
class CapturedParameter extends CapturedVariable instanceof Parameter { }
class Expr instanceof J::Expr {
string toString() { result = super.toString() }
Location getLocation() { result = super.getLocation() }
predicate hasCfgNode(BasicBlock bb, int i) { this = bb.(J::BasicBlock).getNode(i) }
}
class VariableWrite extends Expr instanceof VariableUpdate {
CapturedVariable v;
VariableWrite() { super.getDestVar() = v }
CapturedVariable getVariable() { result = v }
Expr getSource() {
result = this.(VariableAssign).getSource() or
result = this.(AssignOp)
}
}
class VariableRead extends Expr instanceof RValue {
CapturedVariable v;
VariableRead() { super.getVariable() = v }
CapturedVariable getVariable() { result = v }
}
class ClosureExpr extends Expr instanceof ClassInstanceExpr {
NestedClass nc;
ClosureExpr() {
nc.(AnonymousClass).getClassInstanceExpr() = this
or
nc instanceof LocalClass and
super.getConstructedType().getASourceSupertype*().getSourceDeclaration() = nc
}
predicate hasBody(Callable body) { nc.getACallable() = body }
predicate hasAliasedAccess(Expr f) { closureFlowStep+(this, f) and not closureFlowStep(f, _) }
}
class Callable extends J::Callable {
predicate isConstructor() { this instanceof Constructor }
}
}
class CapturedVariable = CaptureInput::CapturedVariable;
class CapturedParameter = CaptureInput::CapturedParameter;
module CaptureFlow = VariableCapture::Flow<CaptureInput>;
private CaptureFlow::ClosureNode asClosureNode(Node n) {
result = n.(CaptureNode).getSynthesizedCaptureNode() or
result.(CaptureFlow::ExprNode).getExpr() = n.asExpr() or
result.(CaptureFlow::ExprPostUpdateNode).getExpr() =
n.(PostUpdateNode).getPreUpdateNode().asExpr() or
result.(CaptureFlow::ParameterNode).getParameter() = n.asParameter() or
result.(CaptureFlow::ThisParameterNode).getCallable() = n.(InstanceParameterNode).getCallable() or
exprNode(result.(CaptureFlow::MallocNode).getClosureExpr()).(PostUpdateNode).getPreUpdateNode() =
n
}
private predicate captureStoreStep(Node node1, CapturedVariableContent c, Node node2) {
CaptureFlow::storeStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2))
}
private predicate captureReadStep(Node node1, CapturedVariableContent c, Node node2) {
CaptureFlow::readStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2))
}
predicate captureValueStep(Node node1, Node node2) {
CaptureFlow::localFlowStep(asClosureNode(node1), asClosureNode(node2))
}
/**
* Holds if data can flow from `node1` to `node2` through a field or
* variable capture.
@@ -78,10 +184,6 @@ private predicate variableCaptureStep(Node node1, ExprNode node2) {
predicate jumpStep(Node node1, Node node2) {
fieldStep(node1, node2)
or
variableCaptureStep(node1, node2)
or
variableCaptureStep(node1.(PostUpdateNode).getPreUpdateNode(), node2)
or
any(AdditionalValueStep a).step(node1, node2) and
node1.getEnclosingCallable() != node2.getEnclosingCallable()
or
@@ -117,6 +219,8 @@ predicate storeStep(Node node1, ContentSet f, Node node2) {
or
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), f,
node2.(FlowSummaryNode).getSummaryNode())
or
captureStoreStep(node1, f, node2)
}
/**
@@ -149,6 +253,8 @@ predicate readStep(Node node1, ContentSet f, Node node2) {
or
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), f,
node2.(FlowSummaryNode).getSummaryNode())
or
captureReadStep(node1, f, node2)
}
/**
@@ -231,19 +337,29 @@ private newtype TDataFlowCallable =
TSummarizedCallable(SummarizedCallable c) or
TFieldScope(Field f)
/**
* A callable or scope enclosing some number of data flow nodes. This can either
* be a source callable, a synthesized callable for which we have a summary
* model, or a synthetic scope for a field value node.
*/
class DataFlowCallable extends TDataFlowCallable {
/** Gets the source callable corresponding to this callable, if any. */
Callable asCallable() { this = TSrcCallable(result) }
/** Gets the summary model callable corresponding to this callable, if any. */
SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }
/** Gets the field corresponding to this callable, if it is a field value scope. */
Field asFieldScope() { this = TFieldScope(result) }
/** Gets a textual representation of this callable. */
string toString() {
result = this.asCallable().toString() or
result = "Synthetic: " + this.asSummarizedCallable().toString() or
result = "Field scope: " + this.asFieldScope().toString()
}
/** Gets the location of this callable. */
Location getLocation() {
result = this.asCallable().getLocation() or
result = this.asSummarizedCallable().getLocation() or
@@ -406,6 +522,8 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
*/
predicate allowParameterReturnInSelf(ParameterNode p) {
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
or
CaptureFlow::heuristicAllowInstanceParameterReturnInSelf(p.(InstanceParameterNode).getCallable())
}
/** An approximated `Content`. */
@@ -447,6 +565,10 @@ ContentApprox getContentApprox(Content c) {
or
c instanceof MapValueContent and result = TMapValueContentApprox()
or
exists(CapturedVariable v |
c = TCapturedVariableContent(v) and result = TCapturedVariableContentApprox(v)
)
or
c instanceof SyntheticFieldContent and result = TSyntheticFieldApproxContent()
}

View File

@@ -135,6 +135,30 @@ private module Cached {
import Cached
private predicate capturedVariableRead(Node n) {
n.asExpr().(RValue).getVariable() instanceof CapturedVariable
}
/**
* Holds if there is a data flow step from `e1` to `e2` that only steps from
* child to parent in the AST.
*/
predicate simpleAstFlowStep(Expr e1, Expr e2) {
e2.(CastingExpr).getExpr() = e1
or
e2.(ChooseExpr).getAResultExpr() = e1
or
e2.(AssignExpr).getSource() = e1
or
e2.(ArrayCreationExpr).getInit() = e1
or
e2 = any(StmtExpr stmtExpr | e1 = stmtExpr.getResultExpr())
or
e2 = any(NotNullExpr nne | e1 = nne.getExpr())
or
e2.(WhenExpr).getBranch(_).getAResult() = e1
}
private predicate simpleLocalFlowStep0(Node node1, Node node2) {
TaintTrackingUtil::forceCachingInSameStage() and
// Variable flow steps through adjacent def-use and use-use pairs.
@@ -142,39 +166,31 @@ private predicate simpleLocalFlowStep0(Node node1, Node node2) {
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
upd.getDefiningExpr().(AssignOp) = node1.asExpr()
|
node2.asExpr() = upd.getAFirstUse()
node2.asExpr() = upd.getAFirstUse() and
not capturedVariableRead(node2)
)
or
exists(SsaImplicitInit init |
init.isParameterDefinition(node1.asParameter()) and
node2.asExpr() = init.getAFirstUse()
node2.asExpr() = init.getAFirstUse() and
not capturedVariableRead(node2)
)
or
adjacentUseUse(node1.asExpr(), node2.asExpr()) and
not exists(FieldRead fr |
hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr()
) and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(node1, _)
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(node1, _) and
not capturedVariableRead(node2)
or
ThisFlow::adjacentThisRefs(node1, node2)
or
adjacentUseUse(node1.(PostUpdateNode).getPreUpdateNode().asExpr(), node2.asExpr())
adjacentUseUse(node1.(PostUpdateNode).getPreUpdateNode().asExpr(), node2.asExpr()) and
not capturedVariableRead(node2)
or
ThisFlow::adjacentThisRefs(node1.(PostUpdateNode).getPreUpdateNode(), node2)
or
node2.asExpr().(CastingExpr).getExpr() = node1.asExpr()
or
node2.asExpr().(ChooseExpr).getAResultExpr() = node1.asExpr()
or
node2.asExpr().(AssignExpr).getSource() = node1.asExpr()
or
node2.asExpr().(ArrayCreationExpr).getInit() = node1.asExpr()
or
node2.asExpr() = any(StmtExpr stmtExpr | node1.asExpr() = stmtExpr.getResultExpr())
or
node2.asExpr() = any(NotNullExpr nne | node1.asExpr() = nne.getExpr())
or
node2.asExpr().(WhenExpr).getBranch(_).getAResult() = node1.asExpr()
simpleAstFlowStep(node1.asExpr(), node2.asExpr())
or
exists(MethodAccess ma, ValuePreservingMethod m, int argNo |
ma.getCallee().getSourceDeclaration() = m and m.returnsValue(argNo)
@@ -185,6 +201,8 @@ private predicate simpleLocalFlowStep0(Node node1, Node node2) {
or
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1.(FlowSummaryNode).getSummaryNode(),
node2.(FlowSummaryNode).getSummaryNode(), true)
or
captureValueStep(node1, node2)
}
/**
@@ -256,6 +274,19 @@ class MapValueContent extends Content, TMapValueContent {
override string toString() { result = "<map.value>" }
}
/** A captured variable. */
class CapturedVariableContent extends Content, TCapturedVariableContent {
CapturedVariable v;
CapturedVariableContent() { this = TCapturedVariableContent(v) }
CapturedVariable getVariable() { result = v }
override DataFlowType getType() { result = getErasedRepr(v.(Variable).getType()) }
override string toString() { result = v.toString() }
}
/** A reference through a synthetic instance field. */
class SyntheticFieldContent extends Content, TSyntheticFieldContent {
SyntheticField s;

View File

@@ -58,3 +58,37 @@ Method getRunnerTarget(MethodAccess ma) {
result.overridesOrInstantiates*(runmethod)
)
}
import semmle.code.java.dataflow.FlowSummary
import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific as ImplSpecific
private predicate mayInvokeCallback(SrcMethod m, int n) {
m.getParameterType(n).(RefType).getSourceDeclaration() instanceof FunctionalInterface and
(not m.fromSource() or m.isNative() or m.getFile().getAbsolutePath().matches("%/test/stubs/%"))
}
private class SummarizedCallableWithCallback extends SummarizedCallable {
private int pos;
SummarizedCallableWithCallback() { mayInvokeCallback(this.asCallable(), pos) }
override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
input = SummaryComponentStack::argument(pos) and
output = SummaryComponentStack::push(SummaryComponent::parameter(-1), input) and
preservesValue = true
}
override predicate hasProvenance(Provenance provenance) { provenance = "hq-generated" }
}
private class RequiredComponentStackForCallback extends RequiredSummaryComponentStack {
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
exists(int pos |
mayInvokeCallback(_, pos) and
head = SummaryComponent::parameter(-1) and
tail = SummaryComponentStack::argument(pos)
)
}
}

View File

@@ -103,7 +103,7 @@ class StdlibRandomSource extends RandomDataSource {
}
override Expr getOutput() {
if m.hasName("getBytes") then result = this.getArgument(0) else result = this
if m.hasName("nextBytes") then result = this.getArgument(0) else result = this
}
}

View File

@@ -307,6 +307,7 @@ class TopJdkApi extends SummarizedCallableBase {
predicate hasManualMadModel() { this.hasManualSummary() or this.hasManualNeutral() }
/*
* Note: the following top JDK APIs are not modeled with MaD:
* `java.lang.Runnable#run()`: specialised lambda flow
* `java.lang.String#valueOf(Object)`: a complex case; an alias for `Object.toString`, except the dispatch is hidden
* `java.lang.System#getProperty(String)`: needs to be modeled by regular CodeQL matching the get and set keys to reduce FPs
* `java.lang.System#setProperty(String,String)`: needs to be modeled by regular CodeQL matching the get and set keys to reduce FPs

View File

@@ -69,6 +69,7 @@ where
// modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
not CharacteristicsImpl::isSink(endpoint, _, _) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
includeAutomodelCandidate(package, type, name, signature) and
// The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be
// a non-sink, and we surface only endpoints that have at least one such sink type.
message =

View File

@@ -0,0 +1,5 @@
extensions:
- addsTo:
pack: codeql/java-queries
extensible: automodelCandidateFilter
data: []

View File

@@ -30,6 +30,7 @@ where
// modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
not CharacteristicsImpl::isSink(endpoint, _, _) and
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, parameterName) and
includeAutomodelCandidate(package, type, name, signature) and
// The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be
// a non-sink, and we surface only endpoints that have at least one such sink type.
message =

View File

@@ -66,3 +66,24 @@ boolean considerSubtypes(Callable callable) {
then result = false
else result = true
}
/**
* Holds if the given package, type, name and signature is a candidate for automodeling.
*
* This predicate is extensible, so that different endpoints can be selected at runtime.
*/
extensible predicate automodelCandidateFilter(
string package, string type, string name, string signature
);
/**
* Holds if the given package, type, name and signature is a candidate for automodeling.
*
* This relies on an extensible predicate, and if that is not supplied then
* all endpoints are considered candidates.
*/
bindingset[package, type, name, signature]
predicate includeAutomodelCandidate(string package, string type, string name, string signature) {
not automodelCandidateFilter(_, _, _, _) or
automodelCandidateFilter(package, type, name, signature)
}

View File

@@ -12,4 +12,5 @@ dependencies:
codeql/util: ${workspace}
dataExtensions:
- Telemetry/ExtractorInformation.yml
- Telemetry/AutomodelCandidateFilter.yml
warnOnImplicitThis: true

View File

@@ -1,3 +1,4 @@
| java.lang.Runnable#run() | no manual model |
| java.lang.String#valueOf(Object) | no manual model |
| java.lang.System#getProperty(String) | no manual model |
| java.lang.System#setProperty(String,String) | no manual model |

View File

@@ -0,0 +1,251 @@
import java.util.*;
import java.util.function.*;
public class B {
static String source(String label) { return null; }
static void sink(String s) { }
static void test1() {
List<String> l1 = new ArrayList<>();
l1.add(source("L"));
List<String> l2 = new ArrayList<>();
l1.forEach(e -> l2.add(e));
sink(l2.get(0)); // $ hasValueFlow=L
}
String bf1;
String bf2;
void test2() {
B other = new B();
Consumer<String> f = x -> { this.bf1 = x; bf2 = x; other.bf1 = x; };
// no flow
sink(bf1);
sink(this.bf2);
sink(other.bf1);
sink(other.bf2);
f.accept(source("T"));
sink(bf1); // $ MISSING: hasValueFlow=T
sink(this.bf2); // $ MISSING: hasValueFlow=T
sink(other.bf1); // $ hasValueFlow=T
sink(other.bf2);
}
static void convert(Map<String, String> inp, Map<String, String> out) {
inp.forEach((key, value) -> { out.put(key, value); });
}
void test3() {
HashMap<String,String> m1 = new HashMap<>();
HashMap<String,String> m2 = new HashMap<>();
m1.put(source("Key"), source("Value"));
convert(m1, m2);
m2.forEach((k, v) -> {
sink(k); // $ hasValueFlow=Key
sink(v); // $ hasValueFlow=Value
});
}
String elem;
void testParamIn1() {
elem = source("pin.This.elem");
testParamIn2(source("pin.Arg"));
}
void testParamIn2(String param) {
Runnable r = () -> {
sink(elem); // $ MISSING: hasValueFlow=pin.This.elem
sink(this.elem); // $ MISSING: hasValueFlow=pin.This.elem
sink(param); // $ hasValueFlow=pin.Arg
};
r.run();
}
void testParamOut1() {
B other = new B();
testParamOut2(other);
sink(elem); // $ MISSING: hasValueFlow=pout.This.elem
sink(this.elem); // $ MISSING: hasValueFlow=pout.This.elem
sink(other.elem); // $ hasValueFlow=pout.param
}
void testParamOut2(B param) {
Runnable r = () -> {
this.elem = source("pout.This.elem");
param.elem = source("pout.param");
};
r.run();
}
void testCrossLambda() {
B b = new B();
Runnable sink1 = () -> { sink(b.elem); };
Runnable sink2 = () -> { sink(b.elem); }; // $ hasValueFlow=src
Runnable src = () -> { b.elem = source("src"); };
doRun(sink1);
doRun(src);
doRun(sink2);
}
void doRun(Runnable r) {
r.run();
}
void testNested() {
List<String> l1 = new ArrayList<>();
List<List<String>> l2 = new ArrayList<>();
l1.add(source("nest.out"));
l2.add(l1);
String s = source("nest.in");
List<String> out1 = new ArrayList<>();
List<String> out2 = new ArrayList<>();
l2.forEach(l -> l.forEach(x -> {
sink(s); // $ hasValueFlow=nest.in
out1.add(x);
out2.add(s);
}));
sink(out1.get(0)); // $ hasValueFlow=nest.out
sink(out2.get(0)); // $ hasValueFlow=nest.in
}
static interface TwoRuns {
void run1();
void run2();
}
void testAnonymousClass() {
List<String> l1 = new ArrayList<>();
List<String> l2 = new ArrayList<>();
TwoRuns r = new TwoRuns() {
@Override
public void run1() {
l1.add(source("run1"));
}
@Override
public void run2() {
l2.add(l1.get(0));
}
};
r.run2();
sink(l2.get(0));
r.run1();
r.run2();
sink(l2.get(0)); // $ hasValueFlow=run1
}
void testLocalClass1() {
String s = source("local1");
class MyLocal {
String f;
MyLocal() { this.f = s; }
String getF() { return this.f; }
}
MyLocal m = new MyLocal();
sink(m.getF()); // $ hasValueFlow=local1
}
void testLocalClass2() {
String s1 = source("s1");
String s2 = source("s2");
List<String> l = new ArrayList<>();
class MyLocal {
String f;
MyLocal() {
this.f = s1;
sink(s2); // $ hasValueFlow=s2
}
void test() {
sink(f); // $ hasValueFlow=s1
sink(s2); // $ hasValueFlow=s2
}
void add(String s) {
l.add(s);
}
String get() {
return l.get(0);
}
}
MyLocal m1 = new MyLocal();
MyLocal m2 = new MyLocal();
m1.test();
sink(m1.get());
m1.add(source("m1.add"));
sink(m2.get()); // $ hasValueFlow=m1.add
}
void testComplex() {
String s = source("complex");
class LocalComplex {
Supplier<StringBox> getBoxSupplier() {
return new Supplier<StringBox>() {
StringBox b = new StringBox();
@Override
public StringBox get() { return b; }
};
}
class StringBox {
String get() {
// capture through regular nested class inside local nested class
return s;
}
}
}
LocalComplex lc = new LocalComplex();
sink(lc.getBoxSupplier().get().get()); // $ MISSING: hasValueFlow=complex
}
void testCapturedLambda() {
String s = source("double.capture.in");
List<String> out = new ArrayList<>();
Runnable r1 = () -> {
sink(s); // $ hasValueFlow=double.capture.in
out.add(source("double.capture.out"));
};
Runnable r2 = () -> {
r1.run();
};
r2.run();
sink(out.get(0)); // $ MISSING: hasValueFlow=double.capture.out
}
void testEnhancedForStmtCapture() {
List<String> l = new ArrayList<>();
l.add(source("list"));
String[] a = new String[] { source("array") };
for (String x : l) {
Runnable r = () -> sink(x); // $ MISSING: hasValueFlow=list
r.run();
}
for (String x : a) {
Runnable r = () -> sink(x); // $ MISSING: hasValueFlow=array
r.run();
}
}
void testDoubleCall() {
String s = source("src");
List<String> l = new ArrayList<>();
List<String> l2 = new ArrayList<>();
class MyLocal2 {
MyLocal2() {
sink(l.get(0)); // no flow
sink(l2.get(0)); // no flow
l.add(s);
}
void run() {
l2.add(l.get(0));
}
}
// The ClassInstanceExpr has two calls in the same cfg node:
// First the constructor call for which it is the postupdate,
// and then as instance argument to the run call.
new MyLocal2().run();
sink(l.get(0)); // $ hasValueFlow=src
sink(l2.get(0)); // $ hasValueFlow=src
}
}

View File

@@ -0,0 +1,2 @@
failures
testFailures

View File

@@ -0,0 +1,2 @@
import TestUtilities.InlineFlowTest
import DefaultFlowTest

View File

@@ -1,26 +1,93 @@
| A.java:14:14:14:16 | "A" | A.java:14:14:14:16 | "A" |
| A.java:14:14:14:16 | "A" | A.java:15:16:15:22 | get(...) |
| A.java:14:14:14:16 | "A" | A.java:18:8:18:15 | p |
| A.java:14:14:14:16 | "A" | A.java:32:26:32:26 | p |
| A.java:21:11:21:13 | "B" | A.java:15:16:15:22 | get(...) |
| A.java:21:11:21:13 | "B" | A.java:21:7:21:13 | ...=... |
| A.java:21:11:21:13 | "B" | A.java:21:11:21:13 | "B" |
| A.java:21:11:21:13 | "B" | A.java:33:26:33:26 | s |
| A.java:23:11:23:13 | "C" | A.java:15:16:15:22 | get(...) |
| A.java:23:11:23:13 | "C" | A.java:23:7:23:13 | ...=... |
| A.java:23:11:23:13 | "C" | A.java:23:11:23:13 | "C" |
| A.java:23:11:23:13 | "C" | A.java:33:26:33:26 | s |
| A.java:25:22:25:24 | "D" | A.java:4:9:4:16 | e |
| A.java:25:22:25:24 | "D" | A.java:4:21:4:28 | ...=... |
| A.java:25:22:25:24 | "D" | A.java:4:28:4:28 | e |
| A.java:25:22:25:24 | "D" | A.java:6:31:6:34 | elem |
| A.java:25:22:25:24 | "D" | A.java:15:16:15:22 | get(...) |
| A.java:25:22:25:24 | "D" | A.java:25:22:25:24 | "D" |
| A.java:25:22:25:24 | "D" | A.java:34:26:34:37 | getElem(...) |
| A.java:27:16:27:18 | "E" | A.java:5:18:5:25 | e |
| A.java:27:16:27:18 | "E" | A.java:5:30:5:37 | ...=... |
| A.java:27:16:27:18 | "E" | A.java:5:37:5:37 | e |
| A.java:27:16:27:18 | "E" | A.java:6:31:6:34 | elem |
| A.java:27:16:27:18 | "E" | A.java:15:16:15:22 | get(...) |
| A.java:27:16:27:18 | "E" | A.java:27:16:27:18 | "E" |
| A.java:27:16:27:18 | "E" | A.java:35:26:35:37 | getElem(...) |
| A.java:14:14:14:16 | "A" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:15:16:15:16 | a : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:14:14:14:16 | "A" : String | A.java:18:8:18:15 | p : String |
| A.java:14:14:14:16 | "A" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:28:11:38:5 | p : String |
| A.java:14:14:14:16 | "A" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:31:17:31:17 | this : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:32:26:32:26 | p : String |
| A.java:14:14:14:16 | "A" : String | A.java:32:26:32:26 | this : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:33:26:33:26 | this : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:34:26:34:27 | this : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:35:26:35:27 | this : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:39:12:39:12 | a : new A(...) { ... } [p] |
| A.java:14:14:14:16 | "A" : String | A.java:39:12:39:12 | p : String |
| A.java:21:11:21:13 | "B" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:15:16:15:16 | a : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:21:11:21:13 | "B" : String | A.java:21:7:21:13 | ...=... : String |
| A.java:21:11:21:13 | "B" : String | A.java:25:5:25:26 | phi(String s) : String |
| A.java:21:11:21:13 | "B" : String | A.java:28:11:38:5 | String s : String |
| A.java:21:11:21:13 | "B" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:31:17:31:17 | this : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:32:26:32:26 | this : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:33:26:33:26 | s : String |
| A.java:21:11:21:13 | "B" : String | A.java:33:26:33:26 | this : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:34:26:34:27 | this : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:35:26:35:27 | this : new A(...) { ... } [String s] |
| A.java:21:11:21:13 | "B" : String | A.java:39:12:39:12 | String s : String |
| A.java:21:11:21:13 | "B" : String | A.java:39:12:39:12 | a : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:15:16:15:16 | a : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:23:11:23:13 | "C" : String | A.java:23:7:23:13 | ...=... : String |
| A.java:23:11:23:13 | "C" : String | A.java:25:5:25:26 | phi(String s) : String |
| A.java:23:11:23:13 | "C" : String | A.java:28:11:38:5 | String s : String |
| A.java:23:11:23:13 | "C" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:31:17:31:17 | this : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:32:26:32:26 | this : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:33:26:33:26 | s : String |
| A.java:23:11:23:13 | "C" : String | A.java:33:26:33:26 | this : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:34:26:34:27 | this : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:35:26:35:27 | this : new A(...) { ... } [String s] |
| A.java:23:11:23:13 | "C" : String | A.java:39:12:39:12 | String s : String |
| A.java:23:11:23:13 | "C" : String | A.java:39:12:39:12 | a : new A(...) { ... } [String s] |
| A.java:25:22:25:24 | "D" : String | A.java:4:9:4:16 | e : String |
| A.java:25:22:25:24 | "D" : String | A.java:4:21:4:24 | this <.field> [post update] : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:4:21:4:28 | ...=... : String |
| A.java:25:22:25:24 | "D" : String | A.java:4:28:4:28 | e : String |
| A.java:25:22:25:24 | "D" : String | A.java:6:12:6:18 | parameter this : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:6:31:6:34 | elem : String |
| A.java:25:22:25:24 | "D" : String | A.java:6:31:6:34 | this <.field> : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:15:16:15:16 | a : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:25:22:25:24 | "D" : String | A.java:25:14:25:25 | new Box(...) : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:28:11:38:5 | Box b1 : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:31:17:31:17 | this : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:32:26:32:26 | this : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:33:26:33:26 | this : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:34:26:34:27 | b1 : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:34:26:34:27 | this : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:34:26:34:37 | getElem(...) : String |
| A.java:25:22:25:24 | "D" : String | A.java:35:26:35:27 | this : new A(...) { ... } [Box b1, ... (2)] |
| A.java:25:22:25:24 | "D" : String | A.java:39:12:39:12 | Box b1 : Box [elem] |
| A.java:25:22:25:24 | "D" : String | A.java:39:12:39:12 | a : new A(...) { ... } [Box b1, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:5:18:5:25 | e : String |
| A.java:27:16:27:18 | "E" : String | A.java:5:30:5:33 | this <.field> [post update] : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:5:30:5:37 | ...=... : String |
| A.java:27:16:27:18 | "E" : String | A.java:5:37:5:37 | e : String |
| A.java:27:16:27:18 | "E" : String | A.java:6:12:6:18 | parameter this : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:6:31:6:34 | elem : String |
| A.java:27:16:27:18 | "E" : String | A.java:6:31:6:34 | this <.field> : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:14:11:14:20 | f2(...) : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:15:16:15:16 | a : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:15:16:15:22 | get(...) : String |
| A.java:27:16:27:18 | "E" : String | A.java:27:5:27:6 | b2 [post update] : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:28:11:38:5 | Box b2 : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:28:11:38:5 | new (...) : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:30:14:30:16 | parameter this : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:31:17:31:17 | this : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:32:26:32:26 | this : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:33:26:33:26 | this : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:34:26:34:27 | this : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:35:26:35:27 | b2 : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:35:26:35:27 | this : new A(...) { ... } [Box b2, ... (2)] |
| A.java:27:16:27:18 | "E" : String | A.java:35:26:35:37 | getElem(...) : String |
| A.java:27:16:27:18 | "E" : String | A.java:39:12:39:12 | Box b2 : Box [elem] |
| A.java:27:16:27:18 | "E" : String | A.java:39:12:39:12 | a : new A(...) { ... } [Box b2, ... (2)] |

View File

@@ -1,16 +1,23 @@
import java
import semmle.code.java.dataflow.DataFlow
StringLiteral src() { result.getCompilationUnit().fromSource() }
StringLiteral src() {
result.getCompilationUnit().fromSource() and
result.getFile().toString() = "A"
}
module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node n) { n.asExpr() = src() }
predicate isSink(DataFlow::Node n) { any() }
predicate isSink(DataFlow::Node n) { none() }
}
module Flow = DataFlow::Global<Config>;
from DataFlow::Node src, DataFlow::Node sink
where Flow::flow(src, sink)
int explorationLimit() { result = 100 }
module PartialFlow = Flow::FlowExploration<explorationLimit/0>;
from PartialFlow::PartialPathNode src, PartialFlow::PartialPathNode sink
where PartialFlow::partialFlow(src, sink, _)
select src, sink

View File

@@ -99,7 +99,7 @@ public class A {
}
public static void testWrapCall() {
sink(wrapStream(null)); // $ SPURIOUS: hasTaintFlow
sink(wrapStream(null)); // no flow
sink(wrapStream(source())); // $ hasTaintFlow
}

View File

@@ -0,0 +1 @@
class Empty { }

View File

@@ -0,0 +1,5 @@
| default |
| remote |
| request |
| response |
| uri-path |

View File

@@ -0,0 +1,5 @@
import semmle.code.java.dataflow.ExternalFlowConfiguration as ExternalFlowConfiguration
query predicate supportedThreatModels(string kind) {
ExternalFlowConfiguration::sourceModelKindConfig(kind)
}

View File

@@ -0,0 +1,10 @@
| cli |
| database |
| default |
| environment |
| file |
| local |
| remote |
| request |
| response |
| uri-path |

View File

@@ -0,0 +1,7 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: supportedThreatModels
data:
- ["local"] # Add the "local" group threat model.

View File

@@ -0,0 +1,5 @@
import semmle.code.java.dataflow.ExternalFlowConfiguration as ExternalFlowConfiguration
query predicate supportedThreatModels(string kind) {
ExternalFlowConfiguration::sourceModelKindConfig(kind)
}

View File

@@ -107,13 +107,13 @@ class IntegrationTest {
filterAndMerge_2(pojoForm, mergedParams, name -> false);
return mergedParams;
}).then(pojoMap -> {
sink(pojoMap.keySet().iterator().next()); //TODO:$hasTaintFlow
sink(pojoMap.get("value")); //TODO:$hasTaintFlow
sink(pojoMap.keySet().iterator().next()); //$hasTaintFlow
sink(pojoMap.get("value")); //$hasTaintFlow
pojoMap.forEach((key, value) -> {
sink(key); //TODO:$hasTaintFlow
sink(value); //TODO:$hasTaintFlow
sink(key); //$hasTaintFlow
sink(value); //$hasTaintFlow
List<Object> values = (List<Object>) value;
sink(values.get(0)); //TODO:$hasTaintFlow
sink(values.get(0)); //$hasTaintFlow
});
});
}