mirror of
https://github.com/github/codeql.git
synced 2026-02-23 10:23:41 +01:00
Java: Use FlowSummaryImpl from dataflow pack
This commit is contained in:
@@ -89,12 +89,13 @@
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||
private import FlowSummary as FlowSummary
|
||||
private import internal.DataFlowPrivate
|
||||
private import internal.FlowSummaryImpl
|
||||
private import internal.FlowSummaryImpl::Public
|
||||
private import internal.FlowSummaryImpl::Private
|
||||
private import internal.FlowSummaryImpl::Private::External
|
||||
private import internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
||||
private import internal.AccessPathSyntax
|
||||
private import internal.ExternalFlowExtensions as Extensions
|
||||
private import FlowSummary
|
||||
private import codeql.mad.ModelValidation as SharedModelVal
|
||||
|
||||
/**
|
||||
@@ -234,6 +235,21 @@ predicate modelCoverage(string package, int pkgs, string kind, string part, int
|
||||
|
||||
/** Provides a query predicate to check the MaD models for validation errors. */
|
||||
module ModelValidation {
|
||||
private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax
|
||||
|
||||
private predicate getRelevantAccessPath(string path) {
|
||||
summaryModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
summaryModel(_, _, _, _, _, _, _, path, _, _) or
|
||||
sinkModel(_, _, _, _, _, _, path, _, _) or
|
||||
sourceModel(_, _, _, _, _, _, path, _, _)
|
||||
}
|
||||
|
||||
private module MkAccessPath = AccessPathSyntax::AccessPath<getRelevantAccessPath/1>;
|
||||
|
||||
class AccessPath = MkAccessPath::AccessPath;
|
||||
|
||||
class AccessPathToken = MkAccessPath::AccessPathToken;
|
||||
|
||||
private string getInvalidModelInput() {
|
||||
exists(string pred, AccessPath input, AccessPathToken part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||
@@ -478,7 +494,9 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate sourceNode(Node node, string kind) {
|
||||
exists(FlowSummaryImplSpecific::InterpretNode n | isSourceNode(n, kind) and n.asNode() = node)
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSourceNode(n, kind) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -487,8 +505,54 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate sinkNode(Node node, string kind) {
|
||||
exists(FlowSummaryImplSpecific::InterpretNode n | isSinkNode(n, kind) and n.asNode() = node)
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isSinkNode(n, kind) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
|
||||
private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||
SummarizedCallableAdapter() { summaryElement(this, _, _, _, _) }
|
||||
|
||||
private predicate relevantSummaryElementManual(string input, string output, string kind) {
|
||||
exists(Provenance provenance |
|
||||
summaryElement(this, input, output, kind, provenance) and
|
||||
provenance.isManual()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
|
||||
exists(Provenance provenance |
|
||||
summaryElement(this, input, output, kind, provenance) and
|
||||
provenance.isGenerated()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
exists(string kind |
|
||||
this.relevantSummaryElementManual(input, output, kind)
|
||||
or
|
||||
not this.relevantSummaryElementManual(_, _, _) and
|
||||
this.relevantSummaryElementGenerated(input, output, kind)
|
||||
|
|
||||
if kind = "value" then preservesValue = true else preservesValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasProvenance(Provenance provenance) {
|
||||
summaryElement(this, _, _, _, provenance)
|
||||
}
|
||||
}
|
||||
|
||||
private class NeutralCallableAdapter extends NeutralCallable {
|
||||
string kind;
|
||||
string provenance_;
|
||||
|
||||
NeutralCallableAdapter() { neutralElement(this, kind, provenance_) }
|
||||
|
||||
override string getKind() { result = kind }
|
||||
|
||||
override predicate hasProvenance(Provenance provenance) { provenance = provenance_ }
|
||||
}
|
||||
|
||||
@@ -6,63 +6,13 @@ import java
|
||||
private import internal.FlowSummaryImpl as Impl
|
||||
private import internal.DataFlowUtil
|
||||
|
||||
class SummaryComponent = Impl::Public::SummaryComponent;
|
||||
deprecated class SummaryComponent = Impl::Private::SummaryComponent;
|
||||
|
||||
/** Provides predicates for constructing summary components. */
|
||||
module SummaryComponent {
|
||||
import Impl::Public::SummaryComponent
|
||||
deprecated module SummaryComponent = Impl::Private::SummaryComponent;
|
||||
|
||||
/** Gets a summary component that represents a qualifier. */
|
||||
SummaryComponent qualifier() { result = argument(-1) }
|
||||
deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||
|
||||
/** Gets a summary component for field `f`. */
|
||||
SummaryComponent field(Field f) { result = content(any(FieldContent c | c.getField() = f)) }
|
||||
|
||||
/** Gets a summary component for `Element`. */
|
||||
SummaryComponent element() { result = content(any(CollectionContent c)) }
|
||||
|
||||
/** Gets a summary component for `ArrayElement`. */
|
||||
SummaryComponent arrayElement() { result = content(any(ArrayContent c)) }
|
||||
|
||||
/** Gets a summary component for `MapValue`. */
|
||||
SummaryComponent mapValue() { result = content(any(MapValueContent c)) }
|
||||
|
||||
/** Gets a summary component that represents the return value of a call. */
|
||||
SummaryComponent return() { result = return(_) }
|
||||
}
|
||||
|
||||
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
|
||||
|
||||
/** Provides predicates for constructing stacks of summary components. */
|
||||
module SummaryComponentStack {
|
||||
import Impl::Public::SummaryComponentStack
|
||||
|
||||
/** Gets a singleton stack representing a qualifier. */
|
||||
SummaryComponentStack qualifier() { result = singleton(SummaryComponent::qualifier()) }
|
||||
|
||||
/** Gets a stack representing a field `f` of `object`. */
|
||||
SummaryComponentStack fieldOf(Field f, SummaryComponentStack object) {
|
||||
result = push(SummaryComponent::field(f), object)
|
||||
}
|
||||
|
||||
/** Gets a stack representing `Element` of `object`. */
|
||||
SummaryComponentStack elementOf(SummaryComponentStack object) {
|
||||
result = push(SummaryComponent::element(), object)
|
||||
}
|
||||
|
||||
/** Gets a stack representing `ArrayElement` of `object`. */
|
||||
SummaryComponentStack arrayElementOf(SummaryComponentStack object) {
|
||||
result = push(SummaryComponent::arrayElement(), object)
|
||||
}
|
||||
|
||||
/** Gets a stack representing `MapValue` of `object`. */
|
||||
SummaryComponentStack mapValueOf(SummaryComponentStack object) {
|
||||
result = push(SummaryComponent::mapValue(), object)
|
||||
}
|
||||
|
||||
/** Gets a singleton stack representing a (normal) return. */
|
||||
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
|
||||
}
|
||||
deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||
|
||||
/** A synthetic callable with a set of concrete call sites and a flow summary. */
|
||||
abstract class SyntheticCallable extends string {
|
||||
@@ -77,11 +27,7 @@ abstract class SyntheticCallable extends string {
|
||||
*
|
||||
* See `SummarizedCallable::propagatesFlow` for details.
|
||||
*/
|
||||
predicate propagatesFlow(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
none()
|
||||
}
|
||||
abstract predicate propagatesFlow(string input, string output, boolean preservesValue);
|
||||
|
||||
/**
|
||||
* Gets the type of the parameter at the specified position with -1 indicating
|
||||
@@ -180,11 +126,9 @@ class SummarizedCallable = Impl::Public::SummarizedCallable;
|
||||
* to `SummarizedCallable`.
|
||||
*/
|
||||
private class SummarizedSyntheticCallableAdapter extends SummarizedCallable, TSyntheticCallable {
|
||||
override predicate propagatesFlow(
|
||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
||||
) {
|
||||
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||
this.asSyntheticCallable().propagatesFlow(input, output, preservesValue)
|
||||
}
|
||||
}
|
||||
|
||||
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
|
||||
deprecated class RequiredSummaryComponentStack = Impl::Private::RequiredSummaryComponentStack;
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
/**
|
||||
* Module for parsing access paths from MaD models, both the identifying access path used
|
||||
* by dynamic languages, and the input/output specifications for summary steps.
|
||||
*
|
||||
* This file is used by the shared data flow library and by the JavaScript libraries
|
||||
* (which does not use the shared data flow libraries).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convenience-predicate for extracting two capture groups at once.
|
||||
*/
|
||||
bindingset[input, regexp]
|
||||
private predicate regexpCaptureTwo(string input, string regexp, string capture1, string capture2) {
|
||||
capture1 = input.regexpCapture(regexp, 1) and
|
||||
capture2 = input.regexpCapture(regexp, 2)
|
||||
}
|
||||
|
||||
/** Companion module to the `AccessPath` class. */
|
||||
module AccessPath {
|
||||
/** A string that should be parsed as an access path. */
|
||||
abstract class Range extends string {
|
||||
bindingset[this]
|
||||
Range() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an integer constant `n` or interval `n1..n2` (inclusive) and gets the value
|
||||
* of the constant or any value contained in the interval.
|
||||
*/
|
||||
bindingset[arg]
|
||||
int parseInt(string arg) {
|
||||
result = arg.toInt()
|
||||
or
|
||||
// Match "n1..n2"
|
||||
exists(string lo, string hi |
|
||||
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.(-?\\d+)", lo, hi) and
|
||||
result = [lo.toInt() .. hi.toInt()]
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a lower-bounded interval `n..` and gets the lower bound.
|
||||
*/
|
||||
bindingset[arg]
|
||||
int parseLowerBound(string arg) { result = arg.regexpCapture("(-?\\d+)\\.\\.", 1).toInt() }
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) that explicitly
|
||||
* references the arity, such as `N-1` or `N-3..N-1`.
|
||||
*
|
||||
* Note that expressions of form `N-x` will never resolve to a negative index,
|
||||
* even if `N` is zero (it will have no result in that case).
|
||||
*/
|
||||
bindingset[arg, arity]
|
||||
private int parseIntWithExplicitArity(string arg, int arity) {
|
||||
result >= 0 and // do not allow N-1 to resolve to a negative index
|
||||
exists(string lo |
|
||||
// N-x
|
||||
lo = arg.regexpCapture("N-(\\d+)", 1) and
|
||||
result = arity - lo.toInt()
|
||||
or
|
||||
// N-x..
|
||||
lo = arg.regexpCapture("N-(\\d+)\\.\\.", 1) and
|
||||
result = [arity - lo.toInt(), arity - 1]
|
||||
)
|
||||
or
|
||||
exists(string lo, string hi |
|
||||
// x..N-y
|
||||
regexpCaptureTwo(arg, "(-?\\d+)\\.\\.N-(\\d+)", lo, hi) and
|
||||
result = [lo.toInt() .. arity - hi.toInt()]
|
||||
or
|
||||
// N-x..N-y
|
||||
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.N-(\\d+)", lo, hi) and
|
||||
result = [arity - lo.toInt() .. arity - hi.toInt()] and
|
||||
result >= 0
|
||||
or
|
||||
// N-x..y
|
||||
regexpCaptureTwo(arg, "N-(\\d+)\\.\\.(\\d+)", lo, hi) and
|
||||
result = [arity - lo.toInt() .. hi.toInt()] and
|
||||
result >= 0
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) and gets any
|
||||
* of the integers contained within (of which there may be infinitely many).
|
||||
*
|
||||
* Has no result for arguments involving an explicit arity, such as `N-1`.
|
||||
*/
|
||||
bindingset[arg, result]
|
||||
int parseIntUnbounded(string arg) {
|
||||
result = parseInt(arg)
|
||||
or
|
||||
result >= parseLowerBound(arg)
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an integer constant or interval (bounded or unbounded) that
|
||||
* may reference the arity of a call, such as `N-1` or `N-3..N-1`.
|
||||
*
|
||||
* Note that expressions of form `N-x` will never resolve to a negative index,
|
||||
* even if `N` is zero (it will have no result in that case).
|
||||
*/
|
||||
bindingset[arg, arity]
|
||||
int parseIntWithArity(string arg, int arity) {
|
||||
result = parseInt(arg)
|
||||
or
|
||||
result in [parseLowerBound(arg) .. arity - 1]
|
||||
or
|
||||
result = parseIntWithExplicitArity(arg, arity)
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the `n`th token on the access path as a string. */
|
||||
private string getRawToken(AccessPath path, int n) {
|
||||
// Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`.
|
||||
// Instead use regexpFind to match valid tokens, and supplement with a final length
|
||||
// check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token.
|
||||
result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* A string that occurs as an access path (either identifying or input/output spec)
|
||||
* which might be relevant for this database.
|
||||
*/
|
||||
class AccessPath extends string instanceof AccessPath::Range {
|
||||
/** Holds if this string is not a syntactically valid access path. */
|
||||
predicate hasSyntaxError() {
|
||||
// If the lengths match, all characters must haven been included in a token
|
||||
// or seen by the `.` lookahead pattern.
|
||||
this != "" and
|
||||
not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1
|
||||
}
|
||||
|
||||
/** Gets the `n`th token on the access path (if there are no syntax errors). */
|
||||
AccessPathToken getToken(int n) {
|
||||
result = getRawToken(this, n) and
|
||||
not this.hasSyntaxError()
|
||||
}
|
||||
|
||||
/** Gets the number of tokens on the path (if there are no syntax errors). */
|
||||
int getNumToken() {
|
||||
result = count(int n | exists(getRawToken(this, n))) and
|
||||
not this.hasSyntaxError()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
|
||||
*/
|
||||
class AccessPathToken extends string {
|
||||
AccessPathToken() { this = getRawToken(_, _) }
|
||||
|
||||
private string getPart(int part) {
|
||||
result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part)
|
||||
}
|
||||
|
||||
/** Gets the name of the token, such as `Member` from `Member[x]` */
|
||||
string getName() { result = this.getPart(1) }
|
||||
|
||||
/**
|
||||
* Gets the argument list, such as `1,2` from `Member[1,2]`,
|
||||
* or has no result if there are no arguments.
|
||||
*/
|
||||
string getArgumentList() { result = this.getPart(2) }
|
||||
|
||||
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
|
||||
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
|
||||
|
||||
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
|
||||
pragma[nomagic]
|
||||
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
|
||||
|
||||
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
|
||||
string getAnArgument() { result = this.getArgument(_) }
|
||||
|
||||
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
|
||||
string getAnArgument(string name) { result = this.getArgument(name, _) }
|
||||
|
||||
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
|
||||
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
|
||||
}
|
||||
@@ -491,16 +491,16 @@ module Private {
|
||||
override string toString() { result = this.getSummaryNode().toString() }
|
||||
|
||||
/** Holds if this summary node is the `i`th argument of `call`. */
|
||||
predicate isArgumentOf(DataFlowCall call, int i) {
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(call, this.getSummaryNode(), i)
|
||||
predicate isArgumentOf(SummaryCall call, int i) {
|
||||
FlowSummaryImpl::Private::summaryArgumentNode(call.getReceiver(), this.getSummaryNode(), i)
|
||||
}
|
||||
|
||||
/** Holds if this summary node is a return node. */
|
||||
predicate isReturn() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), _) }
|
||||
|
||||
/** Holds if this summary node is an out node for `call`. */
|
||||
predicate isOut(DataFlowCall call) {
|
||||
FlowSummaryImpl::Private::summaryOutNode(call, this.getSummaryNode(), _)
|
||||
predicate isOut(SummaryCall call) {
|
||||
FlowSummaryImpl::Private::summaryOutNode(call.getReceiver(), this.getSummaryNode(), _)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -578,7 +578,10 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||
* by default as a heuristic.
|
||||
*/
|
||||
predicate allowParameterReturnInSelf(ParameterNode p) {
|
||||
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
|
||||
exists(DataFlowCallable c, ParameterPosition pos |
|
||||
parameterNode(p, c, pos) and
|
||||
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asSummarizedCallable(), pos)
|
||||
)
|
||||
or
|
||||
CaptureFlow::heuristicAllowInstanceParameterReturnInSelf(p.(InstanceParameterNode).getCallable())
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,354 +0,0 @@
|
||||
/**
|
||||
* Provides Java specific classes and predicates for defining flow summaries.
|
||||
*/
|
||||
|
||||
private import java
|
||||
private import DataFlowDispatch
|
||||
private import DataFlowPrivate
|
||||
private import DataFlowUtil
|
||||
private import FlowSummaryImpl::Private
|
||||
private import FlowSummaryImpl::Public
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSummary as FlowSummary
|
||||
private import semmle.code.java.dataflow.internal.AccessPathSyntax as AccessPathSyntax
|
||||
|
||||
class SummarizedCallableBase = FlowSummary::SummarizedCallableBase;
|
||||
|
||||
/**
|
||||
* A class of callables that are candidates for neutral modeling.
|
||||
*/
|
||||
class NeutralCallableBase extends Callable {
|
||||
NeutralCallableBase() { this.isSourceDeclaration() }
|
||||
|
||||
/** Gets a call that targets this neutral. */
|
||||
Call getACall() { result.getCallee().getSourceDeclaration() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
* A module for importing frameworks that define synthetic globals.
|
||||
*/
|
||||
private module SyntheticGlobals {
|
||||
private import semmle.code.java.frameworks.android.Intent
|
||||
}
|
||||
|
||||
DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c }
|
||||
|
||||
/** Gets the parameter position of the instance parameter. */
|
||||
ArgumentPosition callbackSelfParameterPosition() { result = -1 }
|
||||
|
||||
/** Gets the synthesized data-flow call for `receiver`. */
|
||||
SummaryCall summaryDataFlowCall(SummaryNode receiver) { result.getReceiver() = receiver }
|
||||
|
||||
/** Gets the type of content `c`. */
|
||||
DataFlowType getContentType(Content c) { result = c.getType() }
|
||||
|
||||
/** Gets the type of the parameter at the given position. */
|
||||
DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) {
|
||||
result = getErasedRepr(c.getParameterType(pos))
|
||||
}
|
||||
|
||||
/** Gets the return type of kind `rk` for callable `c`. */
|
||||
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) {
|
||||
result = getErasedRepr(c.getReturnType()) and
|
||||
exists(rk)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the `i`th parameter in a synthesized call that targets a
|
||||
* callback of type `t`.
|
||||
*/
|
||||
DataFlowType getCallbackParameterType(DataFlowType t, int i) {
|
||||
result = getErasedRepr(t.(FunctionalInterface).getRunMethod().getParameterType(i))
|
||||
or
|
||||
result = getErasedRepr(t.(FunctionalInterface)) and i = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the return type of kind `rk` in a synthesized call that targets a
|
||||
* callback of type `t`.
|
||||
*/
|
||||
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
|
||||
result = getErasedRepr(t.(FunctionalInterface).getRunMethod().getReturnType()) and
|
||||
exists(rk)
|
||||
}
|
||||
|
||||
/** Gets the type of synthetic global `sg`. */
|
||||
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) {
|
||||
exists(sg) and
|
||||
result instanceof TypeObject
|
||||
}
|
||||
|
||||
private predicate relatedArgSpec(Callable c, string spec) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, spec, _, _) or
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, spec, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, spec, _, _)
|
||||
|
|
||||
c = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `defaultsCallable` is a Kotlin default-parameter proxy for `originalCallable`, and
|
||||
* `originalCallable` has a model, and `defaultsArgSpec` is `originalArgSpec` adjusted to account
|
||||
* for the additional dispatch receiver parameter that occurs in the default-parameter proxy's argument
|
||||
* list. When no adjustment is required (e.g. for constructors, or non-argument-based specs), `defaultArgsSpec`
|
||||
* equals `originalArgSpec`.
|
||||
*
|
||||
* Note in the case where `originalArgSpec` uses an integer range, like `Argument[1..3]...`, this will produce multiple
|
||||
* results for `defaultsArgSpec`, like `{Argument[2]..., Argument[3]..., Argument[4]...}`.
|
||||
*/
|
||||
private predicate correspondingKotlinParameterDefaultsArgSpec(
|
||||
Callable originalCallable, Callable defaultsCallable, string originalArgSpec,
|
||||
string defaultsArgSpec
|
||||
) {
|
||||
relatedArgSpec(originalCallable, originalArgSpec) and
|
||||
defaultsCallable = originalCallable.getKotlinParameterDefaultsProxy() and
|
||||
(
|
||||
originalCallable instanceof Constructor and originalArgSpec = defaultsArgSpec
|
||||
or
|
||||
originalCallable instanceof Method and
|
||||
exists(string regex |
|
||||
// Note I use a regex and not AccessPathToken because this feeds summaryElement et al,
|
||||
// which would introduce mutual recursion with the definition of AccessPathToken.
|
||||
regex = "Argument\\[([0-9,\\. ]+)\\](.*)" and
|
||||
(
|
||||
exists(string oldArgNumber, string rest, int paramOffset |
|
||||
oldArgNumber = originalArgSpec.regexpCapture(regex, 1) and
|
||||
rest = originalArgSpec.regexpCapture(regex, 2) and
|
||||
paramOffset =
|
||||
defaultsCallable.getNumberOfParameters() -
|
||||
(originalCallable.getNumberOfParameters() + 2) and
|
||||
exists(int oldArgParsed |
|
||||
oldArgParsed = AccessPathSyntax::AccessPath::parseInt(oldArgNumber.splitAt(",").trim())
|
||||
|
|
||||
if
|
||||
ktExtensionFunctions(originalCallable, _, _) and
|
||||
ktExtensionFunctions(defaultsCallable, _, _) and
|
||||
oldArgParsed = 0
|
||||
then defaultsArgSpec = "Argument[" + paramOffset + "]" // 1 if dispatch receiver is present, 0 otherwise.
|
||||
else defaultsArgSpec = "Argument[" + (oldArgParsed + paramOffset) + "]" + rest
|
||||
)
|
||||
)
|
||||
or
|
||||
not originalArgSpec.regexpMatch(regex) and
|
||||
defaultsArgSpec = originalArgSpec
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external flow summary exists for `c` with input specification
|
||||
* `input`, output specification `output`, kind `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate summaryElement(
|
||||
SummarizedCallableBase c, string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string originalInput, string originalOutput, Callable baseCallable
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, originalInput, originalOutput,
|
||||
kind, provenance) and
|
||||
baseCallable = interpretElement(namespace, type, subtypes, name, signature, ext) and
|
||||
(
|
||||
c.asCallable() = baseCallable and input = originalInput and output = originalOutput
|
||||
or
|
||||
correspondingKotlinParameterDefaultsArgSpec(baseCallable, c.asCallable(), originalInput, input) and
|
||||
correspondingKotlinParameterDefaultsArgSpec(baseCallable, c.asCallable(), originalOutput,
|
||||
output)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a neutral model exists for `c` of kind `kind`
|
||||
* and with provenance `provenance`.
|
||||
*/
|
||||
predicate neutralElement(NeutralCallableBase c, string kind, string provenance) {
|
||||
exists(string namespace, string type, string name, string signature |
|
||||
neutralModel(namespace, type, name, signature, kind, provenance) and
|
||||
c = interpretElement(namespace, type, false, name, signature, "")
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the summary component for specification component `c`, if any. */
|
||||
bindingset[c]
|
||||
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
||||
exists(Content content | parseContent(c, content) and result = SummaryComponent::content(content))
|
||||
or
|
||||
c = "WithoutElement" and result = SummaryComponent::withoutContent(any(CollectionContent cc))
|
||||
or
|
||||
c = "WithElement" and result = SummaryComponent::withContent(any(CollectionContent cc))
|
||||
}
|
||||
|
||||
/** Gets the summary component for specification component `c`, if any. */
|
||||
private string getContentSpecific(Content c) {
|
||||
exists(Field f, string package, string className, string fieldName |
|
||||
f = c.(FieldContent).getField() and
|
||||
f.hasQualifiedName(package, className, fieldName) and
|
||||
result = "Field[" + package + "." + className + "." + fieldName + "]"
|
||||
)
|
||||
or
|
||||
exists(SyntheticField f |
|
||||
f = c.(SyntheticFieldContent).getField() and result = "SyntheticField[" + f + "]"
|
||||
)
|
||||
or
|
||||
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"
|
||||
}
|
||||
|
||||
/** Gets the textual representation of the content in the format used for MaD models. */
|
||||
string getMadRepresentationSpecific(SummaryComponent sc) {
|
||||
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c))
|
||||
or
|
||||
sc = TWithoutContentSummaryComponent(_) and result = "WithoutElement"
|
||||
or
|
||||
sc = TWithContentSummaryComponent(_) and result = "WithElement"
|
||||
}
|
||||
|
||||
bindingset[pos]
|
||||
private string positionToString(int pos) {
|
||||
if pos = -1 then result = "this" else result = pos.toString()
|
||||
}
|
||||
|
||||
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
|
||||
string getParameterPosition(ParameterPosition pos) { result = positionToString(pos) }
|
||||
|
||||
/** Gets the textual representation of an argument position in the format used for flow summaries. */
|
||||
string getArgumentPosition(ArgumentPosition pos) { result = positionToString(pos) }
|
||||
|
||||
/** Holds if input specification component `c` needs a reference. */
|
||||
predicate inputNeedsReferenceSpecific(string c) { none() }
|
||||
|
||||
/** Holds if output specification component `c` needs a reference. */
|
||||
predicate outputNeedsReferenceSpecific(string c) { none() }
|
||||
|
||||
class SourceOrSinkElement = Top;
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output`, kind `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate sourceElement(SourceOrSinkElement e, string output, string kind, string provenance) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseSource, string originalOutput
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, originalOutput, kind, provenance) and
|
||||
baseSource = interpretElement(namespace, type, subtypes, name, signature, ext) and
|
||||
(
|
||||
e = baseSource and output = originalOutput
|
||||
or
|
||||
correspondingKotlinParameterDefaultsArgSpec(baseSource, e, originalOutput, output)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external sink specification exists for `e` with input specification
|
||||
* `input`, kind `kind` and provenance `provenance`.
|
||||
*/
|
||||
predicate sinkElement(SourceOrSinkElement e, string input, string kind, string provenance) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseSink, string originalInput
|
||||
|
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, originalInput, kind, provenance) and
|
||||
baseSink = interpretElement(namespace, type, subtypes, name, signature, ext) and
|
||||
(
|
||||
e = baseSink and originalInput = input
|
||||
or
|
||||
correspondingKotlinParameterDefaultsArgSpec(baseSink, e, originalInput, input)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
|
||||
ReturnKind getReturnValueKind() { any() }
|
||||
|
||||
private newtype TInterpretNode =
|
||||
TElement(SourceOrSinkElement n) or
|
||||
TNode(Node n)
|
||||
|
||||
/** An entity used to interpret a source/sink specification. */
|
||||
class InterpretNode extends TInterpretNode {
|
||||
/** Gets the element that this node corresponds to, if any. */
|
||||
SourceOrSinkElement asElement() { this = TElement(result) }
|
||||
|
||||
/** Gets the data-flow node that this node corresponds to, if any. */
|
||||
Node asNode() { this = TNode(result) }
|
||||
|
||||
/** Gets the call that this node corresponds to, if any. */
|
||||
DataFlowCall asCall() { result.asCall() = this.asElement() }
|
||||
|
||||
/** Gets the callable that this node corresponds to, if any. */
|
||||
DataFlowCallable asCallable() { result.asCallable() = this.asElement() }
|
||||
|
||||
/** Gets the target of this call, if any. */
|
||||
Callable getCallTarget() { result = this.asCall().asCall().getCallee().getSourceDeclaration() }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
string toString() {
|
||||
result = this.asElement().toString()
|
||||
or
|
||||
result = this.asNode().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this node. */
|
||||
Location getLocation() {
|
||||
result = this.asElement().getLocation()
|
||||
or
|
||||
result = this.asNode().getLocation()
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides additional sink specification logic required for annotations. */
|
||||
pragma[inline]
|
||||
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
|
||||
exists(Node n, Top ast |
|
||||
n = node.asNode() and
|
||||
ast = mid.asElement()
|
||||
|
|
||||
(c = "Parameter" or c = "") and
|
||||
node.asNode().asParameter() = mid.asElement()
|
||||
or
|
||||
c = "" and
|
||||
n.asExpr().(FieldRead).getField() = ast
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides additional source specification logic required for annotations. */
|
||||
pragma[inline]
|
||||
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) {
|
||||
exists(FieldWrite fw |
|
||||
c = "" and
|
||||
fw.getField() = mid.asElement() and
|
||||
n.asNode().asExpr() = fw.getASource()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
|
||||
bindingset[s]
|
||||
ArgumentPosition parseParamBody(string s) {
|
||||
result = AccessPath::parseInt(s)
|
||||
or
|
||||
s = "this" and result = -1
|
||||
}
|
||||
|
||||
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
|
||||
bindingset[s]
|
||||
ParameterPosition parseArgBody(string s) {
|
||||
result = AccessPath::parseInt(s)
|
||||
or
|
||||
s = "this" and result = -1
|
||||
}
|
||||
Reference in New Issue
Block a user