mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
C#: Use FlowSummaryImpl from dataflow pack
This commit is contained in:
@@ -55,7 +55,6 @@
|
|||||||
],
|
],
|
||||||
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
|
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
|
||||||
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
|
||||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
|
|
||||||
"go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll",
|
"go/ql/lib/semmle/go/dataflow/internal/FlowSummaryImpl.qll",
|
||||||
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
|
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
|
||||||
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
|
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
|
||||||
@@ -466,7 +465,6 @@
|
|||||||
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
|
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
|
||||||
],
|
],
|
||||||
"AccessPathSyntax": [
|
"AccessPathSyntax": [
|
||||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
|
|
||||||
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
|
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
|
||||||
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
|
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
|
||||||
"python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll",
|
"python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll",
|
||||||
|
|||||||
@@ -1,199 +1,23 @@
|
|||||||
/** Provides classes and predicates for defining flow summaries. */
|
/** Provides classes and predicates for defining flow summaries. */
|
||||||
|
|
||||||
import csharp
|
import csharp
|
||||||
private import dotnet
|
|
||||||
private import internal.FlowSummaryImpl as Impl
|
private import internal.FlowSummaryImpl as Impl
|
||||||
private import internal.DataFlowDispatch as DataFlowDispatch
|
private import internal.DataFlowDispatch as DataFlowDispatch
|
||||||
private import Impl::Public::SummaryComponent as SummaryComponentInternal
|
|
||||||
|
|
||||||
class ParameterPosition = DataFlowDispatch::ParameterPosition;
|
deprecated class ParameterPosition = DataFlowDispatch::ParameterPosition;
|
||||||
|
|
||||||
class ArgumentPosition = DataFlowDispatch::ArgumentPosition;
|
deprecated class ArgumentPosition = DataFlowDispatch::ArgumentPosition;
|
||||||
|
|
||||||
// import all instances below
|
deprecated class SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
private module Summaries {
|
|
||||||
private import semmle.code.csharp.frameworks.EntityFramework
|
|
||||||
}
|
|
||||||
|
|
||||||
class SummaryComponent = Impl::Public::SummaryComponent;
|
deprecated module SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
|
|
||||||
/** Provides predicates for constructing summary components. */
|
deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
module SummaryComponent {
|
|
||||||
predicate content = SummaryComponentInternal::content/1;
|
|
||||||
|
|
||||||
/** Gets a summary component for parameter `i`. */
|
deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
SummaryComponent parameter(int i) {
|
|
||||||
exists(ArgumentPosition pos |
|
|
||||||
result = SummaryComponentInternal::parameter(pos) and
|
|
||||||
i = pos.getPosition()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a summary component for argument `i`. */
|
deprecated class RequiredSummaryComponentStack = Impl::Private::RequiredSummaryComponentStack;
|
||||||
SummaryComponent argument(int i) {
|
|
||||||
exists(ParameterPosition pos |
|
|
||||||
result = SummaryComponentInternal::argument(pos) and
|
|
||||||
i = pos.getPosition()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate return = SummaryComponentInternal::return/1;
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a qualifier. */
|
|
||||||
SummaryComponent qualifier() {
|
|
||||||
exists(ParameterPosition pos |
|
|
||||||
result = SummaryComponentInternal::argument(pos) and
|
|
||||||
pos.isThisParameter()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a summary component that represents an element in a collection. */
|
|
||||||
SummaryComponent element() { result = content(any(DataFlow::ElementContent c)) }
|
|
||||||
|
|
||||||
/** Gets a summary component for property `p`. */
|
|
||||||
SummaryComponent property(Property p) {
|
|
||||||
result = content(any(DataFlow::PropertyContent c | c.getProperty() = p.getUnboundDeclaration()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a summary component for field `f`. */
|
|
||||||
SummaryComponent field(Field f) {
|
|
||||||
result = content(any(DataFlow::FieldContent c | c.getField() = f.getUnboundDeclaration()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a summary component that represents the return value of a call. */
|
|
||||||
SummaryComponent return() { result = return(any(DataFlowDispatch::NormalReturnKind rk)) }
|
|
||||||
|
|
||||||
predicate syntheticGlobal = SummaryComponentInternal::syntheticGlobal/1;
|
|
||||||
|
|
||||||
class SyntheticGlobal = SummaryComponentInternal::SyntheticGlobal;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
|
|
||||||
|
|
||||||
/** Provides predicates for constructing stacks of summary components. */
|
|
||||||
module SummaryComponentStack {
|
|
||||||
private import Impl::Public::SummaryComponentStack as SummaryComponentStackInternal
|
|
||||||
|
|
||||||
predicate singleton = SummaryComponentStackInternal::singleton/1;
|
|
||||||
|
|
||||||
predicate push = SummaryComponentStackInternal::push/2;
|
|
||||||
|
|
||||||
/** Gets a singleton stack for argument `i`. */
|
|
||||||
SummaryComponentStack argument(int i) { result = singleton(SummaryComponent::argument(i)) }
|
|
||||||
|
|
||||||
predicate return = SummaryComponentStackInternal::return/1;
|
|
||||||
|
|
||||||
/** Gets a singleton stack representing a qualifier. */
|
|
||||||
SummaryComponentStack qualifier() { result = singleton(SummaryComponent::qualifier()) }
|
|
||||||
|
|
||||||
/** Gets a stack representing an element of `container`. */
|
|
||||||
SummaryComponentStack elementOf(SummaryComponentStack container) {
|
|
||||||
result = push(SummaryComponent::element(), container)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a stack representing a property `p` of `object`. */
|
|
||||||
SummaryComponentStack propertyOf(Property p, SummaryComponentStack object) {
|
|
||||||
result = push(SummaryComponent::property(p), object)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a stack representing a field `f` of `object`. */
|
|
||||||
SummaryComponentStack fieldOf(Field f, SummaryComponentStack object) {
|
|
||||||
result = push(SummaryComponent::field(f), object)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a singleton stack representing the return value of a call. */
|
|
||||||
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
|
|
||||||
|
|
||||||
/** Gets a singleton stack representing a synthetic global with name `name`. */
|
|
||||||
SummaryComponentStack syntheticGlobal(string synthetic) {
|
|
||||||
result = singleton(SummaryComponent::syntheticGlobal(synthetic))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DEPRECATED: Use the member predicate `getMadRepresentation` instead.
|
|
||||||
*
|
|
||||||
* Gets a textual representation of this stack used for flow summaries.
|
|
||||||
*/
|
|
||||||
deprecated string getComponentStack(SummaryComponentStack s) { result = s.getMadRepresentation() }
|
|
||||||
}
|
|
||||||
|
|
||||||
class SummarizedCallable = Impl::Public::SummarizedCallable;
|
class SummarizedCallable = Impl::Public::SummarizedCallable;
|
||||||
|
|
||||||
private predicate recordConstructorFlow(Constructor c, int i, Property p) {
|
|
||||||
c = any(RecordType r).getAMember() and
|
|
||||||
exists(string name |
|
|
||||||
c.getParameter(i).getName() = name and
|
|
||||||
c.getDeclaringType().getAMember(name) = p
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RecordConstructorFlow extends SummarizedCallable {
|
|
||||||
RecordConstructorFlow() { recordConstructorFlow(this, _, _) }
|
|
||||||
|
|
||||||
override predicate propagatesFlow(
|
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
|
||||||
) {
|
|
||||||
exists(int i, Property p |
|
|
||||||
recordConstructorFlow(this, i, p) and
|
|
||||||
input = SummaryComponentStack::argument(i) and
|
|
||||||
output = SummaryComponentStack::propertyOf(p, SummaryComponentStack::return()) and
|
|
||||||
preservesValue = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
|
|
||||||
|
|
||||||
private class RecordConstructorFlowRequiredSummaryComponentStack extends RequiredSummaryComponentStack
|
|
||||||
{
|
|
||||||
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
|
|
||||||
exists(Property p |
|
|
||||||
recordConstructorFlow(_, _, p) and
|
|
||||||
head = SummaryComponent::property(p) and
|
|
||||||
tail = SummaryComponentStack::return()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Provenance = Impl::Public::Provenance;
|
class Provenance = Impl::Public::Provenance;
|
||||||
|
|
||||||
private import semmle.code.csharp.frameworks.system.linq.Expressions
|
|
||||||
|
|
||||||
private SummaryComponent delegateSelf() {
|
|
||||||
exists(ArgumentPosition pos |
|
|
||||||
result = SummaryComponentInternal::parameter(pos) and
|
|
||||||
pos.isDelegateSelf()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate mayInvokeCallback(Callable c, int n) {
|
|
||||||
c.getParameter(n).getType() instanceof SystemLinqExpressions::DelegateExtType and
|
|
||||||
not c.hasBody() and
|
|
||||||
(if c instanceof Accessor then not c.fromSource() else any())
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SummarizedCallableWithCallback extends SummarizedCallable {
|
|
||||||
private int pos;
|
|
||||||
|
|
||||||
SummarizedCallableWithCallback() { mayInvokeCallback(this, pos) }
|
|
||||||
|
|
||||||
override predicate propagatesFlow(
|
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
|
||||||
) {
|
|
||||||
input = SummaryComponentStack::argument(pos) and
|
|
||||||
output = SummaryComponentStack::push(delegateSelf(), 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 = delegateSelf() and
|
|
||||||
tail = SummaryComponentStack::argument(pos)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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))) }
|
|
||||||
}
|
|
||||||
@@ -116,9 +116,7 @@ private module Cached {
|
|||||||
// No need to include calls that are compiled from source
|
// No need to include calls that are compiled from source
|
||||||
not call.getImplementation().getMethod().compiledFromSource()
|
not call.getImplementation().getMethod().compiledFromSource()
|
||||||
} or
|
} or
|
||||||
TSummaryCall(
|
TSummaryCall(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver) {
|
||||||
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
|
|
||||||
) {
|
|
||||||
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,7 +444,7 @@ class CilDataFlowCall extends DataFlowCall, TCilCall {
|
|||||||
* the method `Select`.
|
* the method `Select`.
|
||||||
*/
|
*/
|
||||||
class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
|
class SummaryCall extends DelegateDataFlowCall, TSummaryCall {
|
||||||
private FlowSummaryImpl::Public::SummarizedCallable c;
|
private FlowSummary::SummarizedCallable c;
|
||||||
private FlowSummaryImpl::Private::SummaryNode receiver;
|
private FlowSummaryImpl::Private::SummaryNode receiver;
|
||||||
|
|
||||||
SummaryCall() { this = TSummaryCall(c, receiver) }
|
SummaryCall() { this = TSummaryCall(c, receiver) }
|
||||||
|
|||||||
@@ -676,11 +676,11 @@ private predicate fieldOrPropertyStore(Expr e, Content c, Expr src, Expr q, bool
|
|||||||
f instanceof InstanceFieldOrProperty
|
f instanceof InstanceFieldOrProperty
|
||||||
or
|
or
|
||||||
exists(
|
exists(
|
||||||
FlowSummaryImpl::Public::SummarizedCallable sc,
|
FlowSummaryImpl::Private::SummarizedCallableImpl sc,
|
||||||
FlowSummaryImpl::Public::SummaryComponentStack input
|
FlowSummaryImpl::Private::SummaryComponentStack input
|
||||||
|
|
|
|
||||||
sc.propagatesFlow(input, _, _) and
|
sc.propagatesFlow(input, _, _) and
|
||||||
input.contains(FlowSummary::SummaryComponent::content(f.getContent()))
|
input.contains(FlowSummaryImpl::Private::SummaryComponent::content(f.getContent()))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
@@ -1393,11 +1393,11 @@ private module ArgumentNodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNodeImpl {
|
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNodeImpl {
|
||||||
private DataFlowCall call_;
|
private SummaryCall call_;
|
||||||
private ArgumentPosition pos_;
|
private ArgumentPosition pos_;
|
||||||
|
|
||||||
SummaryArgumentNode() {
|
SummaryArgumentNode() {
|
||||||
FlowSummaryImpl::Private::summaryArgumentNode(call_, this.getSummaryNode(), pos_)
|
FlowSummaryImpl::Private::summaryArgumentNode(call_.getReceiver(), this.getSummaryNode(), pos_)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||||
@@ -1683,11 +1683,11 @@ private module OutNodes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class SummaryOutNode extends FlowSummaryNode, OutNode {
|
private class SummaryOutNode extends FlowSummaryNode, OutNode {
|
||||||
private DataFlowCall call;
|
private SummaryCall call;
|
||||||
private ReturnKind kind_;
|
private ReturnKind kind_;
|
||||||
|
|
||||||
SummaryOutNode() {
|
SummaryOutNode() {
|
||||||
FlowSummaryImpl::Private::summaryOutNode(call, this.getSummaryNode(), kind_)
|
FlowSummaryImpl::Private::summaryOutNode(call.getReceiver(), this.getSummaryNode(), kind_)
|
||||||
}
|
}
|
||||||
|
|
||||||
override DataFlowCall getCall(ReturnKind kind) { result = call and kind = kind_ }
|
override DataFlowCall getCall(ReturnKind kind) { result = call and kind = kind_ }
|
||||||
@@ -1700,7 +1700,7 @@ import OutNodes
|
|||||||
class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
|
class FlowSummaryNode extends NodeImpl, TFlowSummaryNode {
|
||||||
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
|
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
|
||||||
|
|
||||||
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() {
|
FlowSummary::SummarizedCallable getSummarizedCallable() {
|
||||||
result = this.getSummaryNode().getSummarizedCallable()
|
result = this.getSummaryNode().getSummarizedCallable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2424,7 +2424,10 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
|||||||
* by default as a heuristic.
|
* by default as a heuristic.
|
||||||
*/
|
*/
|
||||||
predicate allowParameterReturnInSelf(ParameterNode p) {
|
predicate allowParameterReturnInSelf(ParameterNode p) {
|
||||||
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
|
exists(DataFlowCallable c, ParameterPosition pos |
|
||||||
|
parameterNode(p, c, pos) and
|
||||||
|
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asSummarizedCallable(), pos)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An approximated `Content`. */
|
/** An approximated `Content`. */
|
||||||
|
|||||||
@@ -88,13 +88,13 @@
|
|||||||
|
|
||||||
import csharp
|
import csharp
|
||||||
import ExternalFlowExtensions
|
import ExternalFlowExtensions
|
||||||
private import AccessPathSyntax
|
|
||||||
private import DataFlowDispatch
|
private import DataFlowDispatch
|
||||||
private import DataFlowPrivate
|
private import DataFlowPrivate
|
||||||
private import DataFlowPublic
|
private import DataFlowPublic
|
||||||
|
private import FlowSummaryImpl
|
||||||
private import FlowSummaryImpl::Public
|
private import FlowSummaryImpl::Public
|
||||||
|
private import FlowSummaryImpl::Private
|
||||||
private import FlowSummaryImpl::Private::External
|
private import FlowSummaryImpl::Private::External
|
||||||
private import FlowSummaryImplSpecific
|
|
||||||
private import semmle.code.csharp.commons.QualifiedName
|
private import semmle.code.csharp.commons.QualifiedName
|
||||||
private import codeql.mad.ModelValidation as SharedModelVal
|
private import codeql.mad.ModelValidation as SharedModelVal
|
||||||
|
|
||||||
@@ -154,6 +154,21 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
|
|||||||
|
|
||||||
/** Provides a query predicate to check the MaD models for validation errors. */
|
/** Provides a query predicate to check the MaD models for validation errors. */
|
||||||
module ModelValidation {
|
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() {
|
private string getInvalidModelInput() {
|
||||||
exists(string pred, AccessPath input, AccessPathToken part |
|
exists(string pred, AccessPath input, AccessPathToken part |
|
||||||
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
sinkModel(_, _, _, _, _, _, input, _, _) and pred = "sink"
|
||||||
@@ -380,14 +395,14 @@ Declaration interpretElement(
|
|||||||
* A callable where there exists a MaD sink model that applies to it.
|
* A callable where there exists a MaD sink model that applies to it.
|
||||||
*/
|
*/
|
||||||
class SinkCallable extends Callable {
|
class SinkCallable extends Callable {
|
||||||
SinkCallable() { sinkElement(this, _, _, _) }
|
SinkCallable() { SourceSinkInterpretationInput::sinkElement(this, _, _) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A callable where there exists a MaD source model that applies to it.
|
* A callable where there exists a MaD source model that applies to it.
|
||||||
*/
|
*/
|
||||||
class SourceCallable extends Callable {
|
class SourceCallable extends Callable {
|
||||||
SourceCallable() { sourceElement(this, _, _, _) }
|
SourceCallable() { SourceSinkInterpretationInput::sourceElement(this, _, _) }
|
||||||
}
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
@@ -398,7 +413,9 @@ private module Cached {
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate sourceNode(Node node, string kind) {
|
predicate sourceNode(Node node, string kind) {
|
||||||
exists(InterpretNode n | isSourceNode(n, kind) and n.asNode() = node)
|
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||||
|
isSourceNode(n, kind) and n.asNode() = node
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -407,7 +424,9 @@ private module Cached {
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate sinkNode(Node node, string kind) {
|
predicate sinkNode(Node node, string kind) {
|
||||||
exists(InterpretNode n | isSinkNode(n, kind) and n.asNode() = node)
|
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||||
|
isSinkNode(n, kind) and n.asNode() = node
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,3 +504,63 @@ string asPartialNeutralModel(UnboundCallable c) {
|
|||||||
+ parameters + ";" //
|
+ parameters + ";" //
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private predicate interpretSummary(
|
||||||
|
UnboundCallable c, string input, string output, string kind, string provenance
|
||||||
|
) {
|
||||||
|
exists(
|
||||||
|
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||||
|
|
|
||||||
|
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
|
||||||
|
c = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||||
|
SummarizedCallableAdapter() { interpretSummary(this, _, _, _, _) }
|
||||||
|
|
||||||
|
private predicate relevantSummaryElementManual(string input, string output, string kind) {
|
||||||
|
exists(Provenance provenance |
|
||||||
|
interpretSummary(this, input, output, kind, provenance) and
|
||||||
|
provenance.isManual()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate relevantSummaryElementGenerated(string input, string output, string kind) {
|
||||||
|
exists(Provenance provenance |
|
||||||
|
interpretSummary(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) {
|
||||||
|
interpretSummary(this, _, _, _, provenance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NeutralCallableAdapter extends NeutralCallable {
|
||||||
|
string kind;
|
||||||
|
string provenance_;
|
||||||
|
|
||||||
|
NeutralCallableAdapter() {
|
||||||
|
exists(string namespace, string type, string name, string signature |
|
||||||
|
neutralModel(namespace, type, name, signature, kind, provenance_) and
|
||||||
|
this = interpretElement(namespace, type, false, name, signature, "")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getKind() { result = kind }
|
||||||
|
|
||||||
|
override predicate hasProvenance(Provenance provenance) { provenance = provenance_ }
|
||||||
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,340 +0,0 @@
|
|||||||
/**
|
|
||||||
* Provides C# specific classes and predicates for defining flow summaries.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private import csharp
|
|
||||||
private import dotnet
|
|
||||||
private import semmle.code.csharp.frameworks.system.linq.Expressions
|
|
||||||
private import DataFlowDispatch
|
|
||||||
private import DataFlowPrivate
|
|
||||||
private import DataFlowPublic
|
|
||||||
private import DataFlowImplCommon
|
|
||||||
private import FlowSummaryImpl::Private
|
|
||||||
private import FlowSummaryImpl::Public
|
|
||||||
private import semmle.code.csharp.Unification
|
|
||||||
private import ExternalFlow
|
|
||||||
private import semmle.code.csharp.dataflow.FlowSummary as FlowSummary
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class of callables that are candidates for flow summary modeling.
|
|
||||||
*/
|
|
||||||
class SummarizedCallableBase = UnboundCallable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class of callables that are candidates for neutral modeling.
|
|
||||||
*/
|
|
||||||
class NeutralCallableBase = UnboundCallable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A module for importing frameworks that define synthetic globals.
|
|
||||||
*/
|
|
||||||
private module SyntheticGlobals {
|
|
||||||
private import semmle.code.csharp.frameworks.EntityFramework
|
|
||||||
}
|
|
||||||
|
|
||||||
DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c }
|
|
||||||
|
|
||||||
/** Gets the parameter position of the instance parameter. */
|
|
||||||
ArgumentPosition callbackSelfParameterPosition() { result.isDelegateSelf() }
|
|
||||||
|
|
||||||
/** Gets the synthesized data-flow call for `receiver`. */
|
|
||||||
SummaryCall summaryDataFlowCall(SummaryNode receiver) { receiver = result.getReceiver() }
|
|
||||||
|
|
||||||
/** Gets the type of content `c`. */
|
|
||||||
DataFlowType getContentType(Content c) {
|
|
||||||
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
|
|
||||||
t = c.(FieldContent).getField().getType()
|
|
||||||
or
|
|
||||||
t = c.(PropertyContent).getProperty().getType()
|
|
||||||
or
|
|
||||||
t = c.(SyntheticFieldContent).getField().getType()
|
|
||||||
or
|
|
||||||
c instanceof ElementContent and
|
|
||||||
t instanceof ObjectType // we don't know what the actual element type is
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the type of the parameter at the given position. */
|
|
||||||
DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) {
|
|
||||||
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
|
|
||||||
exists(int i |
|
|
||||||
pos.getPosition() = i and
|
|
||||||
t = c.getParameter(i).getType()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
pos.isThisParameter() and
|
|
||||||
t = c.getDeclaringType()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the return type of kind `rk` for callable `c`. */
|
|
||||||
DataFlowType getReturnType(DotNet::Callable c, ReturnKind rk) {
|
|
||||||
exists(Type t | result.asGvnType() = Gvn::getGlobalValueNumber(t) |
|
|
||||||
rk instanceof NormalReturnKind and
|
|
||||||
(
|
|
||||||
t = c.(Constructor).getDeclaringType()
|
|
||||||
or
|
|
||||||
not c instanceof Constructor and
|
|
||||||
t = c.getReturnType()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
t = c.getParameter(rk.(OutRefReturnKind).getPosition()).getType()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the type of the parameter matching arguments at position `pos` in a
|
|
||||||
* synthesized call that targets a callback of type `t`.
|
|
||||||
*/
|
|
||||||
DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) {
|
|
||||||
exists(SystemLinqExpressions::DelegateExtType dt |
|
|
||||||
t.asGvnType() = Gvn::getGlobalValueNumber(dt) and
|
|
||||||
result.asGvnType() =
|
|
||||||
Gvn::getGlobalValueNumber(dt.getDelegateType().getParameter(pos.getPosition()).getType())
|
|
||||||
)
|
|
||||||
or
|
|
||||||
pos.isDelegateSelf() and
|
|
||||||
result = t
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the return type of kind `rk` in a synthesized call that targets a
|
|
||||||
* callback of type `t`.
|
|
||||||
*/
|
|
||||||
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
|
|
||||||
rk instanceof NormalReturnKind and
|
|
||||||
exists(SystemLinqExpressions::DelegateExtType dt |
|
|
||||||
t.asGvnType() = Gvn::getGlobalValueNumber(dt) and
|
|
||||||
result.asGvnType() = Gvn::getGlobalValueNumber(dt.getDelegateType().getReturnType())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the type of synthetic global `sg`. */
|
|
||||||
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) {
|
|
||||||
exists(sg) and
|
|
||||||
result.asGvnType() = Gvn::getGlobalValueNumber(any(ObjectType t))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if an external flow summary exists for `c` with input specification
|
|
||||||
* `input`, output specification `output`, kind `kind`, and provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate summaryElement(Callable c, string input, string output, string kind, string provenance) {
|
|
||||||
exists(
|
|
||||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
|
||||||
|
|
|
||||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
|
|
||||||
c = interpretElement(namespace, type, subtypes, name, signature, ext)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if a neutral model exists for `c` of kind `kind`
|
|
||||||
* and with provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate neutralElement(Callable 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, "")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if an external source specification exists for `e` with output specification
|
|
||||||
* `output`, kind `kind`, and provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate sourceElement(Element e, string output, string kind, string provenance) {
|
|
||||||
exists(
|
|
||||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
|
||||||
|
|
|
||||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
|
|
||||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if an external sink specification exists for `e` with input specification
|
|
||||||
* `input`, kind `kind` and provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate sinkElement(Element e, string input, string kind, string provenance) {
|
|
||||||
exists(
|
|
||||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
|
||||||
|
|
|
||||||
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance) and
|
|
||||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the summary component for specification component `c`, if any. */
|
|
||||||
bindingset[c]
|
|
||||||
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
|
||||||
c = "Element" and result = SummaryComponent::content(any(ElementContent ec))
|
|
||||||
or
|
|
||||||
c = "WithoutElement" and result = SummaryComponent::withoutContent(any(ElementContent ec))
|
|
||||||
or
|
|
||||||
c = "WithElement" and result = SummaryComponent::withContent(any(ElementContent ec))
|
|
||||||
or
|
|
||||||
// Qualified names may contain commas,such as in `Tuple<,>`, so get the entire argument list
|
|
||||||
// rather than an individual argument.
|
|
||||||
exists(Field f |
|
|
||||||
c.getName() = "Field" and
|
|
||||||
c.getArgumentList() = f.getFullyQualifiedName() and
|
|
||||||
result = SummaryComponent::content(any(FieldContent fc | fc.getField() = f))
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(Property p |
|
|
||||||
c.getName() = "Property" and
|
|
||||||
c.getArgumentList() = p.getFullyQualifiedName() and
|
|
||||||
result = SummaryComponent::content(any(PropertyContent pc | pc.getProperty() = p))
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(SyntheticField f |
|
|
||||||
c.getAnArgument("SyntheticField") = f and
|
|
||||||
result = SummaryComponent::content(any(SyntheticFieldContent sfc | sfc.getField() = f))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the textual representation of the content in the format used for MaD models. */
|
|
||||||
private string getContentSpecific(Content c) {
|
|
||||||
c = TElementContent() and result = "Element"
|
|
||||||
or
|
|
||||||
exists(Field f | c = TFieldContent(f) and result = "Field[" + f.getFullyQualifiedName() + "]")
|
|
||||||
or
|
|
||||||
exists(Property p |
|
|
||||||
c = TPropertyContent(p) and result = "Property[" + p.getFullyQualifiedName() + "]"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(SyntheticField f | c = TSyntheticFieldContent(f) and result = "SyntheticField[" + f + "]")
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the textual representation of a summary component 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"
|
|
||||||
or
|
|
||||||
exists(OutRefReturnKind rk |
|
|
||||||
sc = TReturnSummaryComponent(rk) and
|
|
||||||
result = "Argument[" + rk.getPosition() + "]"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
|
|
||||||
string getParameterPosition(ParameterPosition pos) {
|
|
||||||
result = pos.getPosition().toString()
|
|
||||||
or
|
|
||||||
pos.isThisParameter() and
|
|
||||||
result = "this"
|
|
||||||
or
|
|
||||||
pos.isDelegateSelf() and
|
|
||||||
result = "delegate-self"
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the textual representation of an argument position in the format used for flow summaries. */
|
|
||||||
string getArgumentPosition(ArgumentPosition pos) {
|
|
||||||
result = pos.getPosition().toString()
|
|
||||||
or
|
|
||||||
pos.isQualifier() and
|
|
||||||
result = "this"
|
|
||||||
or
|
|
||||||
pos.isDelegateSelf() and
|
|
||||||
result = "delegate-self"
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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 = Element;
|
|
||||||
|
|
||||||
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
|
|
||||||
NormalReturnKind getReturnValueKind() { any() }
|
|
||||||
|
|
||||||
private newtype TInterpretNode =
|
|
||||||
TElement_(Element n) or
|
|
||||||
TNode_(Node n) or
|
|
||||||
TDataFlowCall_(DataFlowCall c)
|
|
||||||
|
|
||||||
/** 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() { this = TDataFlowCall_(result) }
|
|
||||||
|
|
||||||
/** Gets the callable that this node corresponds to, if any. */
|
|
||||||
DataFlowCallable asCallable() { result.getUnderlyingCallable() = this.asElement() }
|
|
||||||
|
|
||||||
/** Gets the target of this call, if any. */
|
|
||||||
Callable getCallTarget() { result = this.asCall().(NonDelegateDataFlowCall).getATarget(_) }
|
|
||||||
|
|
||||||
/** Gets a textual representation of this node. */
|
|
||||||
string toString() {
|
|
||||||
result = this.asElement().toString()
|
|
||||||
or
|
|
||||||
result = this.asNode().toString()
|
|
||||||
or
|
|
||||||
result = this.asCall().toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the location of this node. */
|
|
||||||
Location getLocation() {
|
|
||||||
result = this.asElement().getLocation()
|
|
||||||
or
|
|
||||||
result = this.asNode().getLocation()
|
|
||||||
or
|
|
||||||
result = this.asCall().getLocation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Provides additional sink specification logic required for attributes. */
|
|
||||||
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
|
|
||||||
exists(Node n | n = node.asNode() |
|
|
||||||
(c = "Parameter" or c = "") and
|
|
||||||
n.asParameter() = mid.asElement()
|
|
||||||
or
|
|
||||||
c = "" and
|
|
||||||
n.asExpr().(AssignableRead).getTarget().getUnboundDeclaration() = mid.asElement()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Provides additional sink specification logic required for attributes. */
|
|
||||||
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) {
|
|
||||||
c = "" and
|
|
||||||
exists(Assignable a |
|
|
||||||
n.asNode().asExpr() = a.getAnAssignedValue() and
|
|
||||||
a.getUnboundDeclaration() = mid.asElement()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
|
|
||||||
bindingset[s]
|
|
||||||
ArgumentPosition parseParamBody(string s) {
|
|
||||||
result.getPosition() = AccessPath::parseInt(s)
|
|
||||||
or
|
|
||||||
s = "this" and
|
|
||||||
result.isQualifier()
|
|
||||||
or
|
|
||||||
s = "delegate-self" and
|
|
||||||
result.isDelegateSelf()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
|
|
||||||
bindingset[s]
|
|
||||||
ParameterPosition parseArgBody(string s) {
|
|
||||||
result.getPosition() = AccessPath::parseInt(s)
|
|
||||||
or
|
|
||||||
s = "this" and
|
|
||||||
result.isThisParameter()
|
|
||||||
or
|
|
||||||
s = "delegate-self" and
|
|
||||||
result.isDelegateSelf()
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,8 @@ private import semmle.code.csharp.frameworks.System
|
|||||||
private import semmle.code.csharp.frameworks.system.data.Entity
|
private import semmle.code.csharp.frameworks.system.data.Entity
|
||||||
private import semmle.code.csharp.frameworks.system.collections.Generic
|
private import semmle.code.csharp.frameworks.system.collections.Generic
|
||||||
private import semmle.code.csharp.frameworks.Sql
|
private import semmle.code.csharp.frameworks.Sql
|
||||||
private import semmle.code.csharp.dataflow.FlowSummary
|
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Public
|
||||||
|
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private
|
||||||
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,9 +86,23 @@ module EntityFramework {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A flow summary for EntityFramework. */
|
/** A flow summary for EntityFramework. */
|
||||||
abstract class EFSummarizedCallable extends SummarizedCallable {
|
abstract class EFSummarizedCallable extends SummarizedCallableImpl {
|
||||||
bindingset[this]
|
bindingset[this]
|
||||||
EFSummarizedCallable() { any() }
|
EFSummarizedCallable() { any() }
|
||||||
|
|
||||||
|
override predicate hasProvenance(Provenance provenance) { provenance = "manual" }
|
||||||
|
}
|
||||||
|
|
||||||
|
// see `SummarizedCallableImpl` qldoc
|
||||||
|
private class EFSummarizedCallableAdapter extends SummarizedCallable instanceof EFSummarizedCallable
|
||||||
|
{
|
||||||
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasProvenance(Provenance provenance) {
|
||||||
|
EFSummarizedCallable.super.hasProvenance(provenance)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The class ``Microsoft.EntityFrameworkCore.DbQuery`1`` or ``System.Data.Entity.DbQuery`1``. */
|
/** The class ``Microsoft.EntityFrameworkCore.DbQuery`1`` or ``System.Data.Entity.DbQuery`1``. */
|
||||||
|
|||||||
@@ -1,15 +1,2 @@
|
|||||||
import csharp
|
import csharp
|
||||||
import semmle.code.csharp.dataflow.internal.ExternalFlow
|
import semmle.code.csharp.dataflow.internal.ExternalFlow::ModelValidation
|
||||||
import semmle.code.csharp.dataflow.internal.AccessPathSyntax
|
|
||||||
import ModelValidation
|
|
||||||
|
|
||||||
private predicate getRelevantAccessPath(string path) {
|
|
||||||
summaryModel(_, _, _, _, _, _, path, _, _, _) or
|
|
||||||
summaryModel(_, _, _, _, _, _, _, path, _, _) or
|
|
||||||
sinkModel(_, _, _, _, _, _, path, _, _) or
|
|
||||||
sourceModel(_, _, _, _, _, _, path, _, _)
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AccessPathsExternal extends AccessPath::Range {
|
|
||||||
AccessPathsExternal() { getRelevantAccessPath(this) }
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
|
||||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
|
||||||
import shared.FlowSummaries
|
import shared.FlowSummaries
|
||||||
|
import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||||
|
|
||||||
private class IncludeAllSummarizedCallable extends IncludeSummarizedCallable {
|
private class IncludeAllSummarizedCallable extends IncludeSummarizedCallable {
|
||||||
IncludeAllSummarizedCallable() { exists(this) }
|
IncludeAllSummarizedCallable() { exists(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class IncludeNeutralSummarizedCallable extends RelevantNeutralCallable instanceof FlowSummaryImpl::Public::NeutralSummaryCallable
|
private class IncludeNeutralSummarizedCallable extends RelevantNeutralCallable {
|
||||||
{
|
|
||||||
/** Gets a string representing the callable in semi-colon separated format for use in flow summaries. */
|
/** Gets a string representing the callable in semi-colon separated format for use in flow summaries. */
|
||||||
final override string getCallableCsv() { result = asPartialNeutralModel(this) }
|
final override string getCallableCsv() { result = asPartialNeutralModel(this) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import shared.FlowSummaries
|
import shared.FlowSummaries
|
||||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||||
|
|
||||||
class IncludeFilteredSummarizedCallable extends IncludeSummarizedCallable instanceof SummarizedCallable
|
class IncludeFilteredSummarizedCallable extends IncludeSummarizedCallable {
|
||||||
{
|
IncludeFilteredSummarizedCallable() { exists(this) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if flow is propagated between `input` and `output` and
|
* Holds if flow is propagated between `input` and `output` and
|
||||||
* if there is no summary for a callable in a `base` class or interface
|
* if there is no summary for a callable in a `base` class or interface
|
||||||
@@ -14,7 +15,7 @@ class IncludeFilteredSummarizedCallable extends IncludeSummarizedCallable instan
|
|||||||
super.propagatesFlow(input, output, preservesValue) and
|
super.propagatesFlow(input, output, preservesValue) and
|
||||||
not exists(IncludeSummarizedCallable rsc |
|
not exists(IncludeSummarizedCallable rsc |
|
||||||
isBaseCallableOrPrototype(rsc) and
|
isBaseCallableOrPrototype(rsc) and
|
||||||
rsc.(SummarizedCallable).propagatesFlow(input, output, preservesValue) and
|
rsc.propagatesFlow(input, output, preservesValue) and
|
||||||
this.(UnboundCallable).overridesOrImplementsUnbound(rsc)
|
this.(UnboundCallable).overridesOrImplementsUnbound(rsc)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import semmle.code.csharp.frameworks.EntityFramework::EntityFramework
|
import csharp
|
||||||
import shared.FlowSummaries
|
import shared.FlowSummaries
|
||||||
|
import semmle.code.csharp.frameworks.EntityFramework::EntityFramework
|
||||||
import semmle.code.csharp.dataflow.internal.ExternalFlow as ExternalFlow
|
import semmle.code.csharp.dataflow.internal.ExternalFlow as ExternalFlow
|
||||||
|
|
||||||
private class IncludeEFSummarizedCallable extends IncludeSummarizedCallable instanceof EFSummarizedCallable
|
private class IncludeEFSummarizedCallable extends IncludeSummarizedCallable instanceof EFSummarizedCallable
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import semmle.code.csharp.dataflow.FlowSummary
|
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private
|
||||||
|
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Public
|
||||||
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private::TestOutput
|
import semmle.code.csharp.dataflow.internal.FlowSummaryImpl::Private::TestOutput
|
||||||
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
private import semmle.code.csharp.dataflow.internal.ExternalFlow
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user