зеркало из https://github.com/github/codeql.git
Merge pull request #14573 from hvitved/flow-summary-impl-param
Move `FlowSummaryImpl.qll` to `dataflow` pack
This commit is contained in:
Коммит
c8b4a215bc
|
@ -53,14 +53,6 @@
|
||||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||||
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
|
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
|
||||||
],
|
],
|
||||||
"DataFlow Java/C#/Go/Ruby/Python/Swift Flow Summaries": [
|
|
||||||
"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",
|
|
||||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll",
|
|
||||||
"python/ql/lib/semmle/python/dataflow/new/internal/FlowSummaryImpl.qll",
|
|
||||||
"swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
|
|
||||||
],
|
|
||||||
"SsaReadPosition Java/C#": [
|
"SsaReadPosition Java/C#": [
|
||||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
|
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
|
||||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
|
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
|
||||||
|
@ -466,15 +458,6 @@
|
||||||
"python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
|
"python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
|
||||||
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
|
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
|
||||||
],
|
],
|
||||||
"AccessPathSyntax": [
|
|
||||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
|
|
||||||
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
|
|
||||||
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
|
|
||||||
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
|
|
||||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",
|
|
||||||
"python/ql/lib/semmle/python/dataflow/new/internal/AccessPathSyntax.qll",
|
|
||||||
"swift/ql/lib/codeql/swift/dataflow/internal/AccessPathSyntax.qll"
|
|
||||||
],
|
|
||||||
"IncompleteUrlSubstringSanitization": [
|
"IncompleteUrlSubstringSanitization": [
|
||||||
"javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll",
|
"javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll",
|
||||||
"ruby/ql/src/queries/security/cwe-020/IncompleteUrlSubstringSanitization.qll"
|
"ruby/ql/src/queries/security/cwe-020/IncompleteUrlSubstringSanitization.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,65 @@ 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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapter class for converting Mad summaries to `SummarizedCallable`s
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapter class for converting Mad neutrals to `NeutralCallable`s
|
||||||
|
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_ }
|
||||||
|
}
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -76,10 +76,10 @@
|
||||||
private import go
|
private import go
|
||||||
import internal.ExternalFlowExtensions
|
import internal.ExternalFlowExtensions
|
||||||
private import internal.DataFlowPrivate
|
private import internal.DataFlowPrivate
|
||||||
|
private import internal.FlowSummaryImpl
|
||||||
|
private import internal.FlowSummaryImpl::Private
|
||||||
private import internal.FlowSummaryImpl::Private::External
|
private import internal.FlowSummaryImpl::Private::External
|
||||||
private import internal.FlowSummaryImplSpecific
|
private import internal.FlowSummaryImpl::Public
|
||||||
private import internal.AccessPathSyntax
|
|
||||||
private import FlowSummary
|
|
||||||
private import codeql.mad.ModelValidation as SharedModelVal
|
private import codeql.mad.ModelValidation as SharedModelVal
|
||||||
|
|
||||||
/** Holds if `package` have MaD framework coverage. */
|
/** Holds if `package` have MaD framework coverage. */
|
||||||
|
@ -274,7 +274,7 @@ private string interpretPackage(string p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
|
/** Gets the source/sink/summary element corresponding to the supplied parameters. */
|
||||||
SourceOrSinkElement interpretElement(
|
SourceSinkInterpretationInput::SourceOrSinkElement interpretElement(
|
||||||
string pkg, string type, boolean subtypes, string name, string signature, string ext
|
string pkg, string type, boolean subtypes, string name, string signature, string ext
|
||||||
) {
|
) {
|
||||||
elementSpec(pkg, type, subtypes, name, signature, ext) and
|
elementSpec(pkg, type, subtypes, name, signature, ext) and
|
||||||
|
@ -298,8 +298,9 @@ SourceOrSinkElement interpretElement(
|
||||||
predicate hasExternalSpecification(Function f) {
|
predicate hasExternalSpecification(Function f) {
|
||||||
f = any(SummarizedCallable sc).asFunction()
|
f = any(SummarizedCallable sc).asFunction()
|
||||||
or
|
or
|
||||||
exists(SourceOrSinkElement e | f = e.asEntity() |
|
exists(SourceSinkInterpretationInput::SourceOrSinkElement e | f = e.asEntity() |
|
||||||
sourceElement(e, _, _, _) or sinkElement(e, _, _, _)
|
SourceSinkInterpretationInput::sourceElement(e, _, _) or
|
||||||
|
SourceSinkInterpretationInput::sinkElement(e, _, _)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +354,9 @@ private module Cached {
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate sourceNode(DataFlow::Node node, string kind) {
|
predicate sourceNode(DataFlow::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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -362,8 +365,73 @@ private module Cached {
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate sinkNode(DataFlow::Node node, string kind) {
|
predicate sinkNode(DataFlow::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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
import Cached
|
import Cached
|
||||||
|
|
||||||
|
private predicate interpretSummary(
|
||||||
|
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.asFunction() = interpretElement(namespace, type, subtypes, name, signature, ext).asEntity()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapter class for converting Mad summaries to `SummarizedCallable`s
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapter class for converting Mad neutrals to `NeutralCallable`s
|
||||||
|
private class NeutralCallableAdapter extends NeutralCallable {
|
||||||
|
string kind;
|
||||||
|
string provenance_;
|
||||||
|
|
||||||
|
NeutralCallableAdapter() {
|
||||||
|
// Neutral models have not been implemented for Go.
|
||||||
|
none() and
|
||||||
|
exists(this) and
|
||||||
|
exists(kind) and
|
||||||
|
exists(provenance_)
|
||||||
|
}
|
||||||
|
|
||||||
|
override string getKind() { result = kind }
|
||||||
|
|
||||||
|
override predicate hasProvenance(Provenance provenance) { provenance = provenance_ }
|
||||||
|
}
|
||||||
|
|
|
@ -10,40 +10,14 @@ private import internal.DataFlowUtil
|
||||||
// import all instances below
|
// import all instances below
|
||||||
private module Summaries { }
|
private module Summaries { }
|
||||||
|
|
||||||
class SummaryComponent = Impl::Public::SummaryComponent;
|
deprecated class SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
|
|
||||||
/** Provides predicates for constructing summary components. */
|
deprecated module SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
module SummaryComponent {
|
|
||||||
import Impl::Public::SummaryComponent
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a qualifier. */
|
deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
SummaryComponent qualifier() { result = argument(-1) }
|
|
||||||
|
|
||||||
/** Gets a summary component for field `f`. */
|
deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
SummaryComponent field(Field f) { result = content(any(FieldContent c | c.getField() = f)) }
|
|
||||||
|
|
||||||
/** 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 singleton stack representing a (normal) return. */
|
|
||||||
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
class SummarizedCallable = Impl::Public::SummarizedCallable;
|
class SummarizedCallable = Impl::Public::SummarizedCallable;
|
||||||
|
|
||||||
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))) }
|
|
||||||
}
|
|
|
@ -86,7 +86,8 @@ module Private {
|
||||||
|
|
||||||
/** Holds if this summary node is the `i`th argument of `call`. */
|
/** Holds if this summary node is the `i`th argument of `call`. */
|
||||||
predicate isArgumentOf(DataFlowCall call, int i) {
|
predicate isArgumentOf(DataFlowCall call, int i) {
|
||||||
FlowSummaryImpl::Private::summaryArgumentNode(call, this.getSummaryNode(), i)
|
// We do not currently have support for callback-based library models.
|
||||||
|
none()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if this summary node is a return node. */
|
/** Holds if this summary node is a return node. */
|
||||||
|
@ -96,7 +97,8 @@ module Private {
|
||||||
|
|
||||||
/** Holds if this summary node is an out node for `call`. */
|
/** Holds if this summary node is an out node for `call`. */
|
||||||
predicate isOut(DataFlowCall call) {
|
predicate isOut(DataFlowCall call) {
|
||||||
FlowSummaryImpl::Private::summaryOutNode(call, this.getSummaryNode(), _)
|
// We do not currently have support for callback-based library models.
|
||||||
|
none()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -420,7 +420,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, int pos |
|
||||||
|
p.isParameterOf(c, pos) and
|
||||||
|
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asSummarizedCallable(), pos)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An approximated `Content`. */
|
/** An approximated `Content`. */
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,292 +0,0 @@
|
||||||
/**
|
|
||||||
* Provides Go-specific classes and predicates for defining flow summaries.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private import go
|
|
||||||
private import DataFlowDispatch
|
|
||||||
private import DataFlowPrivate
|
|
||||||
private import DataFlowUtil
|
|
||||||
private import FlowSummaryImpl::Private
|
|
||||||
private import FlowSummaryImpl::Public
|
|
||||||
private import semmle.go.dataflow.ExternalFlow
|
|
||||||
private import DataFlowImplCommon
|
|
||||||
|
|
||||||
private module FlowSummaries {
|
|
||||||
private import semmle.go.dataflow.FlowSummary as F
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class of callables that are candidates for flow summary modeling.
|
|
||||||
*/
|
|
||||||
class SummarizedCallableBase = Callable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class of callables that are candidates for neutral modeling.
|
|
||||||
*/
|
|
||||||
class NeutralCallableBase = Callable;
|
|
||||||
|
|
||||||
DataFlowCallable inject(SummarizedCallable c) { result.asSummarizedCallable() = c or none() }
|
|
||||||
|
|
||||||
/** Gets the parameter position of the instance parameter. */
|
|
||||||
ArgumentPosition callbackSelfParameterPosition() { result = -1 }
|
|
||||||
|
|
||||||
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
|
|
||||||
string getParameterPosition(ParameterPosition pos) { result = pos.toString() }
|
|
||||||
|
|
||||||
/** Gets the textual representation of an argument position in the format used for flow summaries. */
|
|
||||||
string getArgumentPosition(ArgumentPosition pos) { result = pos.toString() }
|
|
||||||
|
|
||||||
/** Gets the synthesized data-flow call for `receiver`. */
|
|
||||||
DataFlowCall summaryDataFlowCall(SummaryNode receiver) {
|
|
||||||
// We do not currently have support for callback-based library models.
|
|
||||||
none()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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) { any() }
|
|
||||||
|
|
||||||
/** Gets the return type of kind `rk` for callable `c`. */
|
|
||||||
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the type of the `i`th parameter in a synthesized call that targets a
|
|
||||||
* callback of type `t`.
|
|
||||||
*/
|
|
||||||
bindingset[t, pos]
|
|
||||||
DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the return type of kind `rk` in a synthesized call that targets a
|
|
||||||
* callback of type `t`.
|
|
||||||
*/
|
|
||||||
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() }
|
|
||||||
|
|
||||||
/** Gets the type of synthetic global `sg`. */
|
|
||||||
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 package, string type, boolean subtypes, string name, string signature, string ext |
|
|
||||||
summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance) and
|
|
||||||
c.asFunction() = interpretElement(package, type, subtypes, name, signature, ext).asEntity()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if a neutral model exists for `c` of kind `kind`
|
|
||||||
* and with provenance `provenance`.
|
|
||||||
* Note. Neutral models have not been implemented for Go.
|
|
||||||
*/
|
|
||||||
predicate neutralElement(NeutralCallableBase c, string kind, string provenance) { none() }
|
|
||||||
|
|
||||||
/** Gets the summary component for specification component `c`, if any. */
|
|
||||||
bindingset[c]
|
|
||||||
SummaryComponent interpretComponentSpecific(string c) {
|
|
||||||
exists(int pos | parseReturn(c, pos) and result = SummaryComponent::return(getReturnKind(pos)))
|
|
||||||
or
|
|
||||||
exists(Content content | parseContent(c, content) and result = SummaryComponent::content(content))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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"
|
|
||||||
or
|
|
||||||
c instanceof PointerContent and result = "Dereference"
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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
|
|
||||||
exists(ReturnKind rk |
|
|
||||||
sc = TReturnSummaryComponent(rk) and
|
|
||||||
not rk = getReturnValueKind() and
|
|
||||||
result = "ReturnValue[" + rk.getIndex() + "]"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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) { parseReturn(c, _) }
|
|
||||||
|
|
||||||
private newtype TSourceOrSinkElement =
|
|
||||||
TEntityElement(Entity e) or
|
|
||||||
TAstElement(AstNode n)
|
|
||||||
|
|
||||||
/** An element representable by CSV modeling. */
|
|
||||||
class SourceOrSinkElement extends TSourceOrSinkElement {
|
|
||||||
/** Gets this source or sink element as an entity, if it is one. */
|
|
||||||
Entity asEntity() { this = TEntityElement(result) }
|
|
||||||
|
|
||||||
/** Gets this source or sink element as an AST node, if it is one. */
|
|
||||||
AstNode asAstNode() { this = TAstElement(result) }
|
|
||||||
|
|
||||||
/** Gets a textual representation of this source or sink element. */
|
|
||||||
string toString() {
|
|
||||||
result = "element representing " + [this.asEntity().toString(), this.asAstNode().toString()]
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) {
|
|
||||||
this.asEntity().hasLocationInfo(fp, sl, sc, el, ec) or
|
|
||||||
this.asAstNode().hasLocationInfo(fp, sl, sc, el, ec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 package, string type, boolean subtypes, string name, string signature, string ext |
|
|
||||||
sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance) and
|
|
||||||
e = interpretElement(package, 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(SourceOrSinkElement e, string input, string kind, string provenance) {
|
|
||||||
exists(string package, string type, boolean subtypes, string name, string signature, string ext |
|
|
||||||
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance) and
|
|
||||||
e = interpretElement(package, type, subtypes, name, signature, ext)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
|
|
||||||
ReturnKind getReturnValueKind() { result = getReturnKind(0) }
|
|
||||||
|
|
||||||
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 = this.asElement().asAstNode() }
|
|
||||||
|
|
||||||
/** Gets the callable that this node corresponds to, if any. */
|
|
||||||
DataFlowCallable asCallable() {
|
|
||||||
result.asSummarizedCallable().asFunction() = this.asElement().asEntity()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the target of this call, if any. */
|
|
||||||
SourceOrSinkElement getCallTarget() {
|
|
||||||
result.asEntity() = this.asCall().getNode().(DataFlow::CallNode).getTarget()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a textual representation of this node. */
|
|
||||||
string toString() {
|
|
||||||
result = this.asElement().toString()
|
|
||||||
or
|
|
||||||
result = this.asNode().toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the location of this node. */
|
|
||||||
predicate hasLocationInfo(string fp, int sl, int sc, int el, int ec) {
|
|
||||||
this.asElement().hasLocationInfo(fp, sl, sc, el, ec)
|
|
||||||
or
|
|
||||||
this.asNode().hasLocationInfo(fp, sl, sc, el, ec)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Provides additional sink specification logic required for annotations. */
|
|
||||||
pragma[inline]
|
|
||||||
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
|
|
||||||
exists(int pos | node.asNode() = getAnOutNodeExt(mid.asCall(), TValueReturn(getReturnKind(pos))) |
|
|
||||||
parseReturn(c, pos)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(Node n, SourceOrSinkElement e |
|
|
||||||
n = node.asNode() and
|
|
||||||
e = mid.asElement()
|
|
||||||
|
|
|
||||||
(c = "Parameter" or c = "") and
|
|
||||||
node.asNode().asParameter() = e.asEntity()
|
|
||||||
or
|
|
||||||
c = "" and
|
|
||||||
n.(DataFlow::FieldReadNode).getField() = e.asEntity()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Provides additional source specification logic required for annotations. */
|
|
||||||
pragma[inline]
|
|
||||||
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) {
|
|
||||||
exists(int pos, ReturnNodeExt ret |
|
|
||||||
parseReturn(c, pos) and
|
|
||||||
ret = n.asNode() and
|
|
||||||
ret.getKind().(ValueReturnKind).getKind() = getReturnKind(pos) and
|
|
||||||
mid.asCallable() = getNodeEnclosingCallable(ret)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(DataFlow::Write fw, Field f |
|
|
||||||
c = "" and
|
|
||||||
f = mid.asElement().asEntity() and
|
|
||||||
fw.writesField(_, f, n.asNode())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if specification component `c` parses as return value `n` or a range
|
|
||||||
* containing `n`.
|
|
||||||
*/
|
|
||||||
predicate parseReturn(AccessPathToken c, int n) {
|
|
||||||
(
|
|
||||||
c = "ReturnValue" and n = 0
|
|
||||||
or
|
|
||||||
c.getName() = "ReturnValue" and
|
|
||||||
n = parseConstantOrRange(c.getAnArgument())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingset[arg]
|
|
||||||
private int parseConstantOrRange(string arg) {
|
|
||||||
result = arg.toInt()
|
|
||||||
or
|
|
||||||
exists(int n1, int n2 |
|
|
||||||
arg.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 1).toInt() = n1 and
|
|
||||||
arg.regexpCapture("([-0-9]+)\\.\\.([0-9]+)", 2).toInt() = n2 and
|
|
||||||
result = [n1 .. n2]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
|
|
||||||
bindingset[arg]
|
|
||||||
ArgumentPosition parseParamBody(string arg) { result = parseConstantOrRange(arg) }
|
|
||||||
|
|
||||||
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
|
|
||||||
bindingset[arg]
|
|
||||||
ParameterPosition parseArgBody(string arg) { result = parseConstantOrRange(arg) }
|
|
|
@ -3,8 +3,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import go
|
import go
|
||||||
private import semmle.go.dataflow.FlowSummary
|
|
||||||
private import semmle.go.dataflow.internal.DataFlowPrivate
|
private import semmle.go.dataflow.internal.DataFlowPrivate
|
||||||
|
private import semmle.go.dataflow.internal.FlowSummaryImpl::Private
|
||||||
|
|
||||||
/** Provides models of commonly used functions in the `net/http` package. */
|
/** Provides models of commonly used functions in the `net/http` package. */
|
||||||
module NetHttp {
|
module NetHttp {
|
||||||
|
@ -154,7 +154,7 @@ module NetHttp {
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(
|
exists(
|
||||||
SummarizedCallable callable, DataFlow::CallNode call, SummaryComponentStack input,
|
SummarizedCallableImpl callable, DataFlow::CallNode call, SummaryComponentStack input,
|
||||||
SummaryComponentStack output
|
SummaryComponentStack output
|
||||||
|
|
|
|
||||||
this = call.getASyntacticArgument() and
|
this = call.getASyntacticArgument() and
|
||||||
|
|
|
@ -89,12 +89,13 @@
|
||||||
|
|
||||||
import java
|
import java
|
||||||
private import semmle.code.java.dataflow.DataFlow::DataFlow
|
private import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||||
|
private import FlowSummary as FlowSummary
|
||||||
private import internal.DataFlowPrivate
|
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.FlowSummaryImpl::Private::External
|
||||||
private import internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
|
||||||
private import internal.AccessPathSyntax
|
|
||||||
private import internal.ExternalFlowExtensions as Extensions
|
private import internal.ExternalFlowExtensions as Extensions
|
||||||
private import FlowSummary
|
|
||||||
private import codeql.mad.ModelValidation as SharedModelVal
|
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. */
|
/** 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"
|
||||||
|
@ -478,7 +494,9 @@ private module Cached {
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate sourceNode(Node node, string kind) {
|
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,56 @@ private module Cached {
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate sinkNode(Node node, string kind) {
|
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
|
import Cached
|
||||||
|
|
||||||
|
// adapter class for converting Mad summaries to `SummarizedCallable`s
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adapter class for converting Mad neutrals to `NeutralCallable`s
|
||||||
|
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.FlowSummaryImpl as Impl
|
||||||
private import internal.DataFlowUtil
|
private import internal.DataFlowUtil
|
||||||
|
|
||||||
class SummaryComponent = Impl::Public::SummaryComponent;
|
deprecated class SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
|
|
||||||
/** Provides predicates for constructing summary components. */
|
deprecated module SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
module SummaryComponent {
|
|
||||||
import Impl::Public::SummaryComponent
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a qualifier. */
|
deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
SummaryComponent qualifier() { result = argument(-1) }
|
|
||||||
|
|
||||||
/** Gets a summary component for field `f`. */
|
deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
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()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A synthetic callable with a set of concrete call sites and a flow summary. */
|
/** A synthetic callable with a set of concrete call sites and a flow summary. */
|
||||||
abstract class SyntheticCallable extends string {
|
abstract class SyntheticCallable extends string {
|
||||||
|
@ -77,11 +27,7 @@ abstract class SyntheticCallable extends string {
|
||||||
*
|
*
|
||||||
* See `SummarizedCallable::propagatesFlow` for details.
|
* See `SummarizedCallable::propagatesFlow` for details.
|
||||||
*/
|
*/
|
||||||
predicate propagatesFlow(
|
abstract predicate propagatesFlow(string input, string output, boolean preservesValue);
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
|
||||||
) {
|
|
||||||
none()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the type of the parameter at the specified position with -1 indicating
|
* Gets the type of the parameter at the specified position with -1 indicating
|
||||||
|
@ -180,11 +126,9 @@ class SummarizedCallable = Impl::Public::SummarizedCallable;
|
||||||
* to `SummarizedCallable`.
|
* to `SummarizedCallable`.
|
||||||
*/
|
*/
|
||||||
private class SummarizedSyntheticCallableAdapter extends SummarizedCallable, TSyntheticCallable {
|
private class SummarizedSyntheticCallableAdapter extends SummarizedCallable, TSyntheticCallable {
|
||||||
override predicate propagatesFlow(
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
|
||||||
) {
|
|
||||||
this.asSyntheticCallable().propagatesFlow(input, output, 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() }
|
override string toString() { result = this.getSummaryNode().toString() }
|
||||||
|
|
||||||
/** Holds if this summary node is the `i`th argument of `call`. */
|
/** Holds if this summary node is the `i`th argument of `call`. */
|
||||||
predicate isArgumentOf(DataFlowCall call, int i) {
|
predicate isArgumentOf(SummaryCall call, int i) {
|
||||||
FlowSummaryImpl::Private::summaryArgumentNode(call, this.getSummaryNode(), i)
|
FlowSummaryImpl::Private::summaryArgumentNode(call.getReceiver(), this.getSummaryNode(), i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if this summary node is a return node. */
|
/** Holds if this summary node is a return node. */
|
||||||
predicate isReturn() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), _) }
|
predicate isReturn() { FlowSummaryImpl::Private::summaryReturnNode(this.getSummaryNode(), _) }
|
||||||
|
|
||||||
/** Holds if this summary node is an out node for `call`. */
|
/** Holds if this summary node is an out node for `call`. */
|
||||||
predicate isOut(DataFlowCall call) {
|
predicate isOut(SummaryCall call) {
|
||||||
FlowSummaryImpl::Private::summaryOutNode(call, this.getSummaryNode(), _)
|
FlowSummaryImpl::Private::summaryOutNode(call.getReceiver(), this.getSummaryNode(), _)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -578,7 +578,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)
|
||||||
|
)
|
||||||
or
|
or
|
||||||
CaptureFlow::heuristicAllowInstanceParameterReturnInSelf(p.(InstanceParameterNode).getCallable())
|
CaptureFlow::heuristicAllowInstanceParameterReturnInSelf(p.(InstanceParameterNode).getCallable())
|
||||||
}
|
}
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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
|
|
||||||
}
|
|
|
@ -60,7 +60,6 @@ Method getRunnerTarget(MethodCall ma) {
|
||||||
}
|
}
|
||||||
|
|
||||||
import semmle.code.java.dataflow.FlowSummary
|
import semmle.code.java.dataflow.FlowSummary
|
||||||
import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific as ImplSpecific
|
|
||||||
|
|
||||||
private predicate mayInvokeCallback(SrcMethod m, int n) {
|
private predicate mayInvokeCallback(SrcMethod m, int n) {
|
||||||
m.getParameterType(n).(RefType).getSourceDeclaration() instanceof FunctionalInterface and
|
m.getParameterType(n).(RefType).getSourceDeclaration() instanceof FunctionalInterface and
|
||||||
|
@ -72,23 +71,11 @@ private class SummarizedCallableWithCallback extends SummarizedCallable {
|
||||||
|
|
||||||
SummarizedCallableWithCallback() { mayInvokeCallback(this.asCallable(), pos) }
|
SummarizedCallableWithCallback() { mayInvokeCallback(this.asCallable(), pos) }
|
||||||
|
|
||||||
override predicate propagatesFlow(
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
input = "Argument[" + pos + "]" and
|
||||||
) {
|
output = "Argument[" + pos + "].Parameter[-1]" and
|
||||||
input = SummaryComponentStack::argument(pos) and
|
|
||||||
output = SummaryComponentStack::push(SummaryComponent::parameter(-1), input) and
|
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate hasProvenance(Provenance provenance) { provenance = "hq-generated" }
|
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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -32,11 +32,9 @@ private class CollectToContainer extends SyntheticCallable {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlow(
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
input = "Argument[this].Element" and
|
||||||
) {
|
output = "ReturnValue.Element" and
|
||||||
input = SummaryComponentStack::elementOf(SummaryComponentStack::qualifier()) and
|
|
||||||
output = SummaryComponentStack::elementOf(SummaryComponentStack::return()) and
|
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,11 +44,9 @@ private class CollectToJoining extends SyntheticCallable {
|
||||||
|
|
||||||
override Call getACall() { result.(CollectCall).getArgument(0).(Collector).hasName("joining") }
|
override Call getACall() { result.(CollectCall).getArgument(0).(Collector).hasName("joining") }
|
||||||
|
|
||||||
override predicate propagatesFlow(
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
input = "Argument[this].Element" and
|
||||||
) {
|
output = "ReturnValue" and
|
||||||
input = SummaryComponentStack::elementOf(SummaryComponentStack::qualifier()) and
|
|
||||||
output = SummaryComponentStack::return() and
|
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,28 +66,9 @@ private class CollectToGroupingBy extends SyntheticCallable {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlow(
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
input = "Argument[this].Element" and
|
||||||
) {
|
output = "ReturnValue.MapValue.Element" and
|
||||||
input = SummaryComponentStack::elementOf(SummaryComponentStack::qualifier()) and
|
|
||||||
output =
|
|
||||||
SummaryComponentStack::elementOf(SummaryComponentStack::mapValueOf(SummaryComponentStack::return())) and
|
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RequiredComponentStackForCollect extends RequiredSummaryComponentStack {
|
|
||||||
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
|
|
||||||
head = SummaryComponent::element() and
|
|
||||||
tail = SummaryComponentStack::qualifier()
|
|
||||||
or
|
|
||||||
head = SummaryComponent::element() and
|
|
||||||
tail = SummaryComponentStack::return()
|
|
||||||
or
|
|
||||||
head = SummaryComponent::element() and
|
|
||||||
tail = SummaryComponentStack::mapValueOf(SummaryComponentStack::return())
|
|
||||||
or
|
|
||||||
head = SummaryComponent::mapValue() and
|
|
||||||
tail = SummaryComponentStack::return()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ private import semmle.code.java.dataflow.ExternalFlow
|
||||||
private import semmle.code.java.dataflow.FlowSteps
|
private import semmle.code.java.dataflow.FlowSteps
|
||||||
private import semmle.code.java.dataflow.FlowSummary
|
private import semmle.code.java.dataflow.FlowSummary
|
||||||
private import semmle.code.java.dataflow.internal.BaseSSA as BaseSsa
|
private import semmle.code.java.dataflow.internal.BaseSSA as BaseSsa
|
||||||
|
private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
|
|
||||||
/** The class `android.content.Intent`. */
|
/** The class `android.content.Intent`. */
|
||||||
class TypeIntent extends Class {
|
class TypeIntent extends Class {
|
||||||
|
@ -332,12 +333,10 @@ private class StartActivitiesSyntheticCallable extends SyntheticCallable {
|
||||||
result.targetsComponentType(targetType)
|
result.targetsComponentType(targetType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlow(
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
|
||||||
) {
|
|
||||||
exists(ActivityIntentSyntheticGlobal glob | glob.getTargetType() = targetType |
|
exists(ActivityIntentSyntheticGlobal glob | glob.getTargetType() = targetType |
|
||||||
input = SummaryComponentStack::arrayElementOf(SummaryComponentStack::argument(0)) and
|
input = "Argument[0].ArrayElement" and
|
||||||
output = SummaryComponentStack::singleton(SummaryComponent::syntheticGlobal(glob)) and
|
output = "SyntheticGlobal[" + glob + "]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -358,18 +357,16 @@ private class GetIntentSyntheticCallable extends SyntheticCallable {
|
||||||
result.getEnclosingCallable().getDeclaringType() = targetType
|
result.getEnclosingCallable().getDeclaringType() = targetType
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlow(
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
|
||||||
) {
|
|
||||||
exists(ActivityIntentSyntheticGlobal glob | glob.getTargetType() = targetType |
|
exists(ActivityIntentSyntheticGlobal glob | glob.getTargetType() = targetType |
|
||||||
input = SummaryComponentStack::singleton(SummaryComponent::syntheticGlobal(glob)) and
|
input = "SyntheticGlobal[" + glob + "]" and
|
||||||
output = SummaryComponentStack::return() and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ActivityIntentSyntheticGlobal extends SummaryComponent::SyntheticGlobal {
|
private class ActivityIntentSyntheticGlobal extends FlowSummaryImpl::Private::SyntheticGlobal {
|
||||||
AndroidComponent targetType;
|
AndroidComponent targetType;
|
||||||
|
|
||||||
ActivityIntentSyntheticGlobal() {
|
ActivityIntentSyntheticGlobal() {
|
||||||
|
@ -382,13 +379,6 @@ private class ActivityIntentSyntheticGlobal extends SummaryComponent::SyntheticG
|
||||||
AndroidComponent getTargetType() { result = targetType }
|
AndroidComponent getTargetType() { result = targetType }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class RequiredComponentStackForStartActivities extends RequiredSummaryComponentStack {
|
|
||||||
override predicate required(SummaryComponent head, SummaryComponentStack tail) {
|
|
||||||
head = SummaryComponent::arrayElement() and
|
|
||||||
tail = SummaryComponentStack::argument(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A value-preserving step from the intent argument of a `sendBroadcast` call to
|
* A value-preserving step from the intent argument of a `sendBroadcast` call to
|
||||||
* the intent parameter in the `onReceive` method of the receiver the
|
* the intent parameter in the `onReceive` method of the receiver the
|
||||||
|
|
|
@ -304,7 +304,7 @@ class TopJdkApi extends Callable {
|
||||||
|
|
||||||
/** Holds if this API has a manual neutral summary model. */
|
/** Holds if this API has a manual neutral summary model. */
|
||||||
private predicate hasManualNeutralSummary() {
|
private predicate hasManualNeutralSummary() {
|
||||||
this.(FlowSummaryImpl::Public::NeutralSummaryCallable).hasManualModel()
|
this = any(FlowSummaryImpl::Public::NeutralSummaryCallable n | n.hasManualModel()).asCallable()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if this API has a manual MaD model. */
|
/** Holds if this API has a manual MaD model. */
|
||||||
|
|
|
@ -79,7 +79,7 @@ class ExternalApi extends Callable {
|
||||||
|
|
||||||
/** Holds if this API is a known neutral. */
|
/** Holds if this API is a known neutral. */
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate isNeutral() { this instanceof FlowSummaryImpl::Public::NeutralCallable }
|
predicate isNeutral() { this = any(FlowSummaryImpl::Public::NeutralCallable n).asCallable() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
|
* Holds if this API is supported by existing CodeQL libraries, that is, it is either a
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
private import java
|
private import java
|
||||||
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
private import semmle.code.java.dataflow.internal.DataFlowPrivate
|
||||||
private import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific
|
private import semmle.code.java.dataflow.internal.FlowSummaryImpl
|
||||||
private import semmle.code.java.dataflow.internal.ModelExclusions
|
private import semmle.code.java.dataflow.internal.ModelExclusions
|
||||||
private import ModelEditor
|
private import ModelEditor
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ private import ModelEditor
|
||||||
* A class of effectively public callables from source code.
|
* A class of effectively public callables from source code.
|
||||||
*/
|
*/
|
||||||
class PublicEndpointFromSource extends Endpoint, ModelApi {
|
class PublicEndpointFromSource extends Endpoint, ModelApi {
|
||||||
override predicate isSource() { sourceElement(this, _, _, _) }
|
override predicate isSource() { SourceSinkInterpretationInput::sourceElement(this, _, _) }
|
||||||
|
|
||||||
override predicate isSink() { sinkElement(this, _, _, _) }
|
override predicate isSink() { SourceSinkInterpretationInput::sinkElement(this, _, _) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,2 @@
|
||||||
import java
|
import java
|
||||||
import semmle.code.java.dataflow.ExternalFlow
|
import semmle.code.java.dataflow.ExternalFlow::ModelValidation
|
||||||
import semmle.code.java.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) }
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ extractor: javascript
|
||||||
library: true
|
library: true
|
||||||
upgrades: upgrades
|
upgrades: upgrades
|
||||||
dependencies:
|
dependencies:
|
||||||
|
codeql/dataflow: ${workspace}
|
||||||
codeql/mad: ${workspace}
|
codeql/mad: ${workspace}
|
||||||
codeql/regex: ${workspace}
|
codeql/regex: ${workspace}
|
||||||
codeql/tutorial: ${workspace}
|
codeql/tutorial: ${workspace}
|
||||||
|
|
|
@ -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))) }
|
|
||||||
}
|
|
|
@ -70,8 +70,8 @@ private module API = Specific::API;
|
||||||
|
|
||||||
private module DataFlow = Specific::DataFlow;
|
private module DataFlow = Specific::DataFlow;
|
||||||
|
|
||||||
private import Specific::AccessPathSyntax
|
|
||||||
private import ApiGraphModelsExtensions as Extensions
|
private import ApiGraphModelsExtensions as Extensions
|
||||||
|
private import codeql.dataflow.internal.AccessPathSyntax
|
||||||
|
|
||||||
/** Module containing hooks for providing input data to be interpreted as a model. */
|
/** Module containing hooks for providing input data to be interpreted as a model. */
|
||||||
module ModelInput {
|
module ModelInput {
|
||||||
|
@ -327,29 +327,29 @@ predicate isRelevantFullPath(string type, string path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A string from a CSV row that should be parsed as an access path. */
|
/** A string from a CSV row that should be parsed as an access path. */
|
||||||
private class AccessPathRange extends AccessPath::Range {
|
private predicate accessPathRange(string s) {
|
||||||
AccessPathRange() {
|
isRelevantFullPath(_, s)
|
||||||
isRelevantFullPath(_, this)
|
or
|
||||||
or
|
exists(string type | isRelevantType(type) |
|
||||||
exists(string type | isRelevantType(type) |
|
summaryModel(type, _, s, _, _) or
|
||||||
summaryModel(type, _, this, _, _) or
|
summaryModel(type, _, _, s, _)
|
||||||
summaryModel(type, _, _, this, _)
|
)
|
||||||
)
|
or
|
||||||
or
|
typeVariableModel(_, s)
|
||||||
typeVariableModel(_, this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import AccessPath<accessPathRange/1>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a successor of `node` in the API graph.
|
* Gets a successor of `node` in the API graph.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
|
API::Node getSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
|
||||||
// API graphs use the same label for arguments and parameters. An edge originating from a
|
// API graphs use the same label for arguments and parameters. An edge originating from a
|
||||||
// use-node represents an argument, and an edge originating from a def-node represents a parameter.
|
// use-node represents an argument, and an edge originating from a def-node represents a parameter.
|
||||||
// We just map both to the same thing.
|
// We just map both to the same thing.
|
||||||
token.getName() = ["Argument", "Parameter"] and
|
token.getName() = ["Argument", "Parameter"] and
|
||||||
result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument()))
|
result = node.getParameter(parseIntUnbounded(token.getAnArgument()))
|
||||||
or
|
or
|
||||||
token.getName() = "ReturnValue" and
|
token.getName() = "ReturnValue" and
|
||||||
result = node.getReturn()
|
result = node.getReturn()
|
||||||
|
@ -362,11 +362,9 @@ API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||||
* Gets an API-graph successor for the given invocation.
|
* Gets an API-graph successor for the given invocation.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken token) {
|
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathTokenBase token) {
|
||||||
token.getName() = "Argument" and
|
token.getName() = "Argument" and
|
||||||
result =
|
result = invoke.getParameter(parseIntWithArity(token.getAnArgument(), invoke.getNumArgument()))
|
||||||
invoke
|
|
||||||
.getParameter(AccessPath::parseIntWithArity(token.getAnArgument(), invoke.getNumArgument()))
|
|
||||||
or
|
or
|
||||||
token.getName() = "ReturnValue" and
|
token.getName() = "ReturnValue" and
|
||||||
result = invoke.getReturn()
|
result = invoke.getReturn()
|
||||||
|
@ -378,10 +376,12 @@ API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken to
|
||||||
/**
|
/**
|
||||||
* Holds if `invoke` invokes a call-site filter given by `token`.
|
* Holds if `invoke` invokes a call-site filter given by `token`.
|
||||||
*/
|
*/
|
||||||
pragma[inline]
|
bindingset[token]
|
||||||
private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, AccessPathToken token) {
|
private predicate invocationMatchesCallSiteFilter(
|
||||||
|
Specific::InvokeNode invoke, AccessPathTokenBase token
|
||||||
|
) {
|
||||||
token.getName() = "WithArity" and
|
token.getName() = "WithArity" and
|
||||||
invoke.getNumArgument() = AccessPath::parseIntUnbounded(token.getAnArgument())
|
invoke.getNumArgument() = parseIntUnbounded(token.getAnArgument())
|
||||||
or
|
or
|
||||||
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
|
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,13 @@
|
||||||
* It must export the following members:
|
* It must export the following members:
|
||||||
* ```ql
|
* ```ql
|
||||||
* class Unit // a unit type
|
* class Unit // a unit type
|
||||||
* module AccessPathSyntax // a re-export of the AccessPathSyntax module
|
|
||||||
* class InvokeNode // a type representing an invocation connected to the API graph
|
* class InvokeNode // a type representing an invocation connected to the API graph
|
||||||
* module API // the API graph module
|
* module API // the API graph module
|
||||||
* predicate isPackageUsed(string package)
|
* predicate isPackageUsed(string package)
|
||||||
* API::Node getExtraNodeFromPath(string package, string type, string path, int n)
|
* API::Node getExtraNodeFromPath(string package, string type, string path, int n)
|
||||||
* API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token)
|
* API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token)
|
||||||
* API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token)
|
* API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathTokenBase token)
|
||||||
* predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToken token)
|
* predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathTokenBase token)
|
||||||
* InvokeNode getAnInvocationOf(API::Node node)
|
* InvokeNode getAnInvocationOf(API::Node node)
|
||||||
* predicate isExtraValidTokenNameInIdentifyingAccessPath(string name)
|
* predicate isExtraValidTokenNameInIdentifyingAccessPath(string name)
|
||||||
* predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name)
|
* predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name)
|
||||||
|
@ -21,13 +20,12 @@
|
||||||
|
|
||||||
private import javascript as JS
|
private import javascript as JS
|
||||||
private import ApiGraphModels
|
private import ApiGraphModels
|
||||||
|
private import codeql.dataflow.internal.AccessPathSyntax
|
||||||
|
|
||||||
// Re-export libraries needed by ApiGraphModels.qll
|
// Re-export libraries needed by ApiGraphModels.qll
|
||||||
module API = JS::API;
|
module API = JS::API;
|
||||||
|
|
||||||
import semmle.javascript.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
|
|
||||||
import JS::DataFlow as DataFlow
|
import JS::DataFlow as DataFlow
|
||||||
private import AccessPathSyntax
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `rawType` represents the JavaScript type `qualifiedName` from the given NPM `package`.
|
* Holds if `rawType` represents the JavaScript type `qualifiedName` from the given NPM `package`.
|
||||||
|
@ -137,7 +135,7 @@ API::Node getExtraNodeFromType(string type) {
|
||||||
* Gets a JavaScript-specific API graph successor of `node` reachable by resolving `token`.
|
* Gets a JavaScript-specific API graph successor of `node` reachable by resolving `token`.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
|
||||||
token.getName() = "Member" and
|
token.getName() = "Member" and
|
||||||
result = node.getMember(token.getAnArgument())
|
result = node.getMember(token.getAnArgument())
|
||||||
or
|
or
|
||||||
|
@ -183,7 +181,7 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||||
* Gets a JavaScript-specific API graph successor of `node` reachable by resolving `token`.
|
* Gets a JavaScript-specific API graph successor of `node` reachable by resolving `token`.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathToken token) {
|
API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathTokenBase token) {
|
||||||
token.getName() = "Instance" and
|
token.getName() = "Instance" and
|
||||||
result = node.getInstance()
|
result = node.getInstance()
|
||||||
or
|
or
|
||||||
|
@ -233,7 +231,7 @@ API::Node getAFuzzySuccessor(API::Node node) {
|
||||||
* Holds if `invoke` matches the JS-specific call site filter in `token`.
|
* Holds if `invoke` matches the JS-specific call site filter in `token`.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
predicate invocationMatchesExtraCallSiteFilter(API::InvokeNode invoke, AccessPathToken token) {
|
predicate invocationMatchesExtraCallSiteFilter(API::InvokeNode invoke, AccessPathTokenBase token) {
|
||||||
token.getName() = "NewCall" and
|
token.getName() = "NewCall" and
|
||||||
invoke instanceof API::NewNode
|
invoke instanceof API::NewNode
|
||||||
or
|
or
|
||||||
|
@ -246,9 +244,8 @@ predicate invocationMatchesExtraCallSiteFilter(API::InvokeNode invoke, AccessPat
|
||||||
operand = token.getAnArgument() and
|
operand = token.getAnArgument() and
|
||||||
argIndex = operand.splitAt("=", 0) and
|
argIndex = operand.splitAt("=", 0) and
|
||||||
stringValue = operand.splitAt("=", 1) and
|
stringValue = operand.splitAt("=", 1) and
|
||||||
invoke
|
invoke.getArgument(parseIntWithArity(argIndex, invoke.getNumArgument())).getStringValue() =
|
||||||
.getArgument(AccessPath::parseIntWithArity(argIndex, invoke.getNumArgument()))
|
stringValue
|
||||||
.getStringValue() = stringValue
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,7 +335,7 @@ predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string a
|
||||||
or
|
or
|
||||||
name = "WithStringArgument" and
|
name = "WithStringArgument" and
|
||||||
exists(argument.indexOf("=")) and
|
exists(argument.indexOf("=")) and
|
||||||
exists(AccessPath::parseIntWithArity(argument.splitAt("=", 0), 10))
|
exists(parseIntWithArity(argument.splitAt("=", 0), 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
module ModelOutputSpecific {
|
module ModelOutputSpecific {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import javascript
|
import javascript
|
||||||
import testUtilities.ConsistencyChecking
|
import testUtilities.ConsistencyChecking
|
||||||
import semmle.javascript.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
|
import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||||
|
|
||||||
class Steps extends ModelInput::SummaryModelCsv {
|
class Steps extends ModelInput::SummaryModelCsv {
|
||||||
override predicate row(string row) {
|
override predicate row(string row) {
|
||||||
|
@ -126,6 +126,6 @@ class SyntaxErrorTest extends ModelInput::SinkModelCsv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
query predicate syntaxErrors(AccessPathSyntax::AccessPath path) { path.hasSyntaxError() }
|
query predicate syntaxErrors(ApiGraphModels::AccessPath path) { path.hasSyntaxError() }
|
||||||
|
|
||||||
query predicate warning = ModelOutput::getAWarning/0;
|
query predicate warning = ModelOutput::getAWarning/0;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import javascript
|
import javascript
|
||||||
import semmle.javascript.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
|
|
||||||
import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
import semmle.javascript.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||||
|
|
||||||
private class InvalidTypeModel extends ModelInput::TypeModelCsv {
|
private class InvalidTypeModel extends ModelInput::TypeModelCsv {
|
||||||
|
|
|
@ -13,61 +13,14 @@ private module Summaries {
|
||||||
private import semmle.python.Frameworks
|
private import semmle.python.Frameworks
|
||||||
}
|
}
|
||||||
|
|
||||||
class SummaryComponent = Impl::Public::SummaryComponent;
|
deprecated class SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
|
|
||||||
/** Provides predicates for constructing summary components. */
|
/** Provides predicates for constructing summary components. */
|
||||||
module SummaryComponent {
|
deprecated module SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
private import Impl::Public::SummaryComponent as SC
|
|
||||||
|
|
||||||
predicate parameter = SC::parameter/1;
|
deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
|
|
||||||
predicate argument = SC::argument/1;
|
deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
|
|
||||||
predicate content = SC::content/1;
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a list element. */
|
|
||||||
SummaryComponent listElement() { result = content(any(ListElementContent c)) }
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a set element. */
|
|
||||||
SummaryComponent setElement() { result = content(any(SetElementContent c)) }
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a tuple element. */
|
|
||||||
SummaryComponent tupleElement(int index) {
|
|
||||||
exists(TupleElementContent c | c.getIndex() = index and result = content(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a dictionary element. */
|
|
||||||
SummaryComponent dictionaryElement(string key) {
|
|
||||||
exists(DictionaryElementContent c | c.getKey() = key and result = content(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a dictionary element at any key. */
|
|
||||||
SummaryComponent dictionaryElementAny() { result = content(any(DictionaryElementAnyContent c)) }
|
|
||||||
|
|
||||||
/** Gets a summary component that represents an attribute element. */
|
|
||||||
SummaryComponent attribute(string attr) {
|
|
||||||
exists(AttributeContent c | c.getAttribute() = attr and result = content(c))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a summary component that represents the return value of a call. */
|
|
||||||
SummaryComponent return() { result = SC::return(any(ReturnKind rk)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
|
|
||||||
|
|
||||||
/** Provides predicates for constructing stacks of summary components. */
|
|
||||||
module SummaryComponentStack {
|
|
||||||
private import Impl::Public::SummaryComponentStack as SCS
|
|
||||||
|
|
||||||
predicate singleton = SCS::singleton/1;
|
|
||||||
|
|
||||||
predicate push = SCS::push/2;
|
|
||||||
|
|
||||||
predicate argument = SCS::argument/1;
|
|
||||||
|
|
||||||
/** Gets a singleton stack representing the return value of a call. */
|
|
||||||
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A callable with a flow summary, identified by a unique string. */
|
/** A callable with a flow summary, identified by a unique string. */
|
||||||
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
|
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
|
||||||
|
@ -75,21 +28,14 @@ abstract class SummarizedCallable extends LibraryCallable, Impl::Public::Summari
|
||||||
SummarizedCallable() { any() }
|
SummarizedCallable() { any() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as
|
* DEPRECATED: Use `propagatesFlow` instead.
|
||||||
*
|
|
||||||
* ```ql
|
|
||||||
* propagatesFlow(
|
|
||||||
* SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
|
||||||
* )
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* but uses an external (string) representation of the input and output stacks.
|
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
deprecated predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() }
|
this.propagatesFlow(input, output, preservesValue)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
|
deprecated class RequiredSummaryComponentStack = Impl::Private::RequiredSummaryComponentStack;
|
||||||
|
|
||||||
private class SummarizedCallableFromModel extends SummarizedCallable {
|
private class SummarizedCallableFromModel extends SummarizedCallable {
|
||||||
string type;
|
string type;
|
||||||
|
@ -109,7 +55,7 @@ private class SummarizedCallableFromModel extends SummarizedCallable {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(string kind | ModelOutput::relevantSummaryModel(type, path, input, output, kind) |
|
exists(string kind | ModelOutput::relevantSummaryModel(type, path, input, output, kind) |
|
||||||
kind = "value" and
|
kind = "value" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -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))) }
|
|
||||||
}
|
|
|
@ -36,7 +36,6 @@ private import python
|
||||||
private import DataFlowPublic
|
private import DataFlowPublic
|
||||||
private import DataFlowPrivate
|
private import DataFlowPrivate
|
||||||
private import FlowSummaryImpl as FlowSummaryImpl
|
private import FlowSummaryImpl as FlowSummaryImpl
|
||||||
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
|
||||||
private import semmle.python.internal.CachedStages
|
private import semmle.python.internal.CachedStages
|
||||||
private import semmle.python.dataflow.new.internal.TypeTracker::CallGraphConstruction as CallGraphConstruction
|
private import semmle.python.dataflow.new.internal.TypeTracker::CallGraphConstruction as CallGraphConstruction
|
||||||
|
|
||||||
|
@ -49,13 +48,13 @@ newtype TParameterPosition =
|
||||||
// since synthetic parameters are made for a synthetic summary callable, based on
|
// since synthetic parameters are made for a synthetic summary callable, based on
|
||||||
// what Argument positions they have flow for, we need to make sure we have such
|
// what Argument positions they have flow for, we need to make sure we have such
|
||||||
// parameter positions available.
|
// parameter positions available.
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedPositionalArgumentPosition(_, index)
|
FlowSummaryImpl::ParsePositions::isParsedPositionalArgumentPosition(_, index)
|
||||||
} or
|
} or
|
||||||
TKeywordParameterPosition(string name) {
|
TKeywordParameterPosition(string name) {
|
||||||
name = any(Parameter p).getName()
|
name = any(Parameter p).getName()
|
||||||
or
|
or
|
||||||
// see comment for TPositionalParameterPosition
|
// see comment for TPositionalParameterPosition
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedKeywordArgumentPosition(_, name)
|
FlowSummaryImpl::ParsePositions::isParsedKeywordArgumentPosition(_, name)
|
||||||
} or
|
} or
|
||||||
TStarArgsParameterPosition(int index) {
|
TStarArgsParameterPosition(int index) {
|
||||||
// since `.getPosition` does not work for `*args`, we need *args parameter positions
|
// since `.getPosition` does not work for `*args`, we need *args parameter positions
|
||||||
|
@ -136,13 +135,13 @@ newtype TArgumentPosition =
|
||||||
// since synthetic calls within a summarized callable could use a unique argument
|
// since synthetic calls within a summarized callable could use a unique argument
|
||||||
// position, we need to ensure we make these available (these are specified as
|
// position, we need to ensure we make these available (these are specified as
|
||||||
// parameters in the flow-summary spec)
|
// parameters in the flow-summary spec)
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedPositionalParameterPosition(_, index)
|
FlowSummaryImpl::ParsePositions::isParsedPositionalParameterPosition(_, index)
|
||||||
} or
|
} or
|
||||||
TKeywordArgumentPosition(string name) {
|
TKeywordArgumentPosition(string name) {
|
||||||
exists(any(CallNode c).getArgByName(name))
|
exists(any(CallNode c).getArgByName(name))
|
||||||
or
|
or
|
||||||
// see comment for TPositionalArgumentPosition
|
// see comment for TPositionalArgumentPosition
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedKeywordParameterPosition(_, name)
|
FlowSummaryImpl::ParsePositions::isParsedKeywordParameterPosition(_, name)
|
||||||
} or
|
} or
|
||||||
TStarArgsArgumentPosition(int index) {
|
TStarArgsArgumentPosition(int index) {
|
||||||
exists(Call c | c.getPositionalArg(index) instanceof Starred)
|
exists(Call c | c.getPositionalArg(index) instanceof Starred)
|
||||||
|
@ -1559,12 +1558,15 @@ private class SummaryReturnNode extends FlowSummaryNode, ReturnNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
|
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
|
||||||
|
private SummaryCall call_;
|
||||||
|
private ArgumentPosition pos_;
|
||||||
|
|
||||||
SummaryArgumentNode() {
|
SummaryArgumentNode() {
|
||||||
FlowSummaryImpl::Private::summaryArgumentNode(_, this.getSummaryNode(), _)
|
FlowSummaryImpl::Private::summaryArgumentNode(call_.getReceiver(), this.getSummaryNode(), pos_)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||||
FlowSummaryImpl::Private::summaryArgumentNode(call, this.getSummaryNode(), pos)
|
call = call_ and pos = pos_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1662,10 +1664,16 @@ private module OutNodes {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SummaryOutNode extends FlowSummaryNode, OutNode {
|
private class SummaryOutNode extends FlowSummaryNode, OutNode {
|
||||||
SummaryOutNode() { FlowSummaryImpl::Private::summaryOutNode(_, this.getSummaryNode(), _) }
|
private SummaryCall call;
|
||||||
|
private ReturnKind kind_;
|
||||||
|
|
||||||
|
SummaryOutNode() {
|
||||||
|
FlowSummaryImpl::Private::summaryOutNode(call.getReceiver(), this.getSummaryNode(), kind_)
|
||||||
|
}
|
||||||
|
|
||||||
override DataFlowCall getCall(ReturnKind kind) {
|
override DataFlowCall getCall(ReturnKind kind) {
|
||||||
FlowSummaryImpl::Private::summaryOutNode(result, this.getSummaryNode(), kind)
|
result = call and
|
||||||
|
kind = kind_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1028,7 +1028,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 |
|
||||||
|
p.(ParameterNodeImpl).isParameterOf(c, pos) and
|
||||||
|
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asLibraryCallable(), pos)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An approximated `Content`. */
|
/** An approximated `Content`. */
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,324 +0,0 @@
|
||||||
/**
|
|
||||||
* Provides Python specific classes and predicates for defining flow summaries.
|
|
||||||
*
|
|
||||||
* Flow summaries are defined for callables that are not extracted.
|
|
||||||
* Such callables go by different names in different parts of our codebase:
|
|
||||||
*
|
|
||||||
* - in `FlowSummary.qll`, which is user facing, they are called `SummarizedCallable`s.
|
|
||||||
* These contain summaries, implemented by the user via the predicates `propagatesFlow` and `propagatesFlowExt`.
|
|
||||||
*
|
|
||||||
* - in the data flow layer, they are called `LibraryCallable`s (as in the Ruby codebase).
|
|
||||||
* These are identified by strings and has predicates for finding calls to them.
|
|
||||||
*
|
|
||||||
* Having both extracted and non-extracted callables means that we now have three types of calls:
|
|
||||||
* - Extracted calls to extracted callables, either `NormalCall` or `SpecialCall`. These are handled by standard data flow.
|
|
||||||
* - Extracted calls to non-extracted callables, `LibraryCall`. These are handled by looking up the relevant summary when the
|
|
||||||
* global data flow graph is connected up via `getViableCallable`.
|
|
||||||
* - Non-extracted calls, `SummaryCall`. These are synthesised by the flow summary framework.
|
|
||||||
*
|
|
||||||
* The first two can be referred to as `ExtractedDataFlowCall`. In fact, `LibraryCall` is a subclass of `NormalCall`, where
|
|
||||||
* `getCallable` is set to `none()`. The member predicate `ExtractedDataFlowCall::getCallable` is _not_ the mechanism for
|
|
||||||
* call resolution in global data flow. That mechanism is `getViableCallable`.
|
|
||||||
* Resolving a call to a non-extracted callable goes via `LibraryCallable::getACall`, which may involve type tracking.
|
|
||||||
* To avoid that type tracking becomes mutually recursive with data flow, type tracking must use a call graph not including summaries.
|
|
||||||
* Type tracking sees the callgraph given by `ExtractedDataFlowCall::getACallable`.
|
|
||||||
*
|
|
||||||
* We do not support summaries of special methods via the special methods framework,
|
|
||||||
* the summary would have to identify the call.
|
|
||||||
*
|
|
||||||
* We might, while we still extract the standard library, want to support flow summaries of
|
|
||||||
* extracted callables, so that we can model part of the standard library with flow summaries.
|
|
||||||
* For this to work, we have be careful with the enclosing callable predicate.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private import python
|
|
||||||
private import DataFlowPrivate
|
|
||||||
private import DataFlowPublic
|
|
||||||
private import DataFlowImplCommon
|
|
||||||
private import FlowSummaryImpl::Private
|
|
||||||
private import FlowSummaryImpl::Public
|
|
||||||
private import semmle.python.dataflow.new.FlowSummary as FlowSummary
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class of callables that are candidates for flow summary modeling.
|
|
||||||
*/
|
|
||||||
class SummarizedCallableBase = string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class of callables that are candidates for neutral modeling.
|
|
||||||
*/
|
|
||||||
class NeutralCallableBase = string;
|
|
||||||
|
|
||||||
/** View a `SummarizedCallable` as a `DataFlowCallable`. */
|
|
||||||
DataFlowCallable inject(SummarizedCallable c) { result.asLibraryCallable() = c }
|
|
||||||
|
|
||||||
/** Gets the parameter position of the instance parameter. */
|
|
||||||
ArgumentPosition callbackSelfParameterPosition() { none() } // disables implicit summary flow to `this` for callbacks
|
|
||||||
|
|
||||||
/** 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) { any() }
|
|
||||||
|
|
||||||
/** Gets the type of the parameter at the given position. */
|
|
||||||
DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) { any() }
|
|
||||||
|
|
||||||
/** Gets the return type of kind `rk` for callable `c`. */
|
|
||||||
bindingset[c, rk]
|
|
||||||
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the type of the parameter matching arguments at position `pos` in a
|
|
||||||
* synthesized call that targets a callback of type `t`.
|
|
||||||
*/
|
|
||||||
bindingset[t, pos]
|
|
||||||
DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the return type of kind `rk` in a synthesized call that targets a
|
|
||||||
* callback of type `t`.
|
|
||||||
*/
|
|
||||||
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() }
|
|
||||||
|
|
||||||
/** Gets the type of synthetic global `sg`. */
|
|
||||||
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if an external flow summary exists for `c` with input specification
|
|
||||||
* `input`, output specification `output`, kind `kind`, and provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate summaryElement(
|
|
||||||
FlowSummary::SummarizedCallable c, string input, string output, string kind, string provenance
|
|
||||||
) {
|
|
||||||
exists(boolean preservesValue |
|
|
||||||
c.propagatesFlowExt(input, output, preservesValue) and
|
|
||||||
(if preservesValue = true then kind = "value" else kind = "taint") and
|
|
||||||
provenance = "manual"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if a neutral model exists for `c` of kind `kind`
|
|
||||||
* and with provenance `provenance`.
|
|
||||||
* Note. Neutral models have not been implemented for Python.
|
|
||||||
*/
|
|
||||||
predicate neutralElement(NeutralCallableBase c, string kind, string provenance) { none() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the summary component for specification component `c`, if any.
|
|
||||||
*
|
|
||||||
* This covers all the Python-specific components of a flow summary.
|
|
||||||
*/
|
|
||||||
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
|
||||||
c = "ListElement" and
|
|
||||||
result = FlowSummary::SummaryComponent::listElement()
|
|
||||||
or
|
|
||||||
c = "SetElement" and
|
|
||||||
result = FlowSummary::SummaryComponent::setElement()
|
|
||||||
or
|
|
||||||
exists(int index |
|
|
||||||
c.getAnArgument("TupleElement") = index.toString() and
|
|
||||||
result = FlowSummary::SummaryComponent::tupleElement(index)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string key |
|
|
||||||
c.getAnArgument("DictionaryElement") = key and
|
|
||||||
result = FlowSummary::SummaryComponent::dictionaryElement(key)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
c = "DictionaryElementAny" and
|
|
||||||
result = FlowSummary::SummaryComponent::dictionaryElementAny()
|
|
||||||
or
|
|
||||||
exists(string attr |
|
|
||||||
c.getAnArgument("Attribute") = attr and
|
|
||||||
result = FlowSummary::SummaryComponent::attribute(attr)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private string getContentSpecific(Content cs) {
|
|
||||||
cs = TListElementContent() and result = "ListElement"
|
|
||||||
or
|
|
||||||
cs = TSetElementContent() and result = "SetElement"
|
|
||||||
or
|
|
||||||
exists(int index |
|
|
||||||
cs = TTupleElementContent(index) and result = "TupleElement[" + index.toString() + "]"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string key |
|
|
||||||
cs = TDictionaryElementContent(key) and result = "DictionaryElement[" + key + "]"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
cs = TDictionaryElementAnyContent() and result = "DictionaryElementAny"
|
|
||||||
or
|
|
||||||
exists(string attr | cs = TAttributeContent(attr) and result = "Attribute[" + attr + "]")
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
|
|
||||||
string getParameterPosition(ParameterPosition pos) {
|
|
||||||
pos.isSelf() and result = "self"
|
|
||||||
or
|
|
||||||
exists(int i |
|
|
||||||
pos.isPositional(i) and
|
|
||||||
result = i.toString()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string name |
|
|
||||||
pos.isKeyword(name) and
|
|
||||||
result = name + ":"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the textual representation of an argument position in the format used for flow summaries. */
|
|
||||||
string getArgumentPosition(ArgumentPosition pos) {
|
|
||||||
pos.isSelf() and result = "self"
|
|
||||||
or
|
|
||||||
exists(int i |
|
|
||||||
pos.isPositional(i) and
|
|
||||||
result = i.toString()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string name |
|
|
||||||
pos.isKeyword(name) and
|
|
||||||
result = name + ":"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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() }
|
|
||||||
|
|
||||||
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
|
|
||||||
ReturnKind getReturnValueKind() { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All definitions in this module are required by the shared implementation
|
|
||||||
* (for source/sink interpretation), but they are unused for Python, where
|
|
||||||
* we rely on API graphs instead.
|
|
||||||
*/
|
|
||||||
private module UnusedSourceSinkInterpretation {
|
|
||||||
/**
|
|
||||||
* Holds if an external source specification exists for `n` with output specification
|
|
||||||
* `output`, kind `kind`, and provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate sourceElement(AstNode n, string output, string kind, string provenance) { none() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if an external sink specification exists for `n` with input specification
|
|
||||||
* `input`, kind `kind` and provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate sinkElement(AstNode n, string input, string kind, string provenance) { none() }
|
|
||||||
|
|
||||||
class SourceOrSinkElement = AstNode;
|
|
||||||
|
|
||||||
/** An entity used to interpret a source/sink specification. */
|
|
||||||
class InterpretNode extends AstNode_ {
|
|
||||||
// InterpretNode is going away, this is just a dummy implementation.
|
|
||||||
// However, we have some old location tests picking them up, so we
|
|
||||||
// explicitly define them to not exist.
|
|
||||||
InterpretNode() { none() }
|
|
||||||
|
|
||||||
/** Gets the element that this node corresponds to, if any. */
|
|
||||||
SourceOrSinkElement asElement() { none() }
|
|
||||||
|
|
||||||
/** Gets the data-flow node that this node corresponds to, if any. */
|
|
||||||
Node asNode() { none() }
|
|
||||||
|
|
||||||
/** Gets the call that this node corresponds to, if any. */
|
|
||||||
DataFlowCall asCall() { none() }
|
|
||||||
|
|
||||||
/** Gets the callable that this node corresponds to, if any. */
|
|
||||||
DataFlowCallable asCallable() { none() }
|
|
||||||
|
|
||||||
/** Gets the target of this call, if any. */
|
|
||||||
SourceOrSinkElement getCallTarget() { none() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Provides additional sink specification logic. */
|
|
||||||
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
|
|
||||||
|
|
||||||
/** Provides additional source specification logic. */
|
|
||||||
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
|
|
||||||
}
|
|
||||||
|
|
||||||
import UnusedSourceSinkInterpretation
|
|
||||||
|
|
||||||
module ParsePositions {
|
|
||||||
private import FlowSummaryImpl
|
|
||||||
|
|
||||||
private predicate isParamBody(string body) {
|
|
||||||
exists(AccessPathToken tok |
|
|
||||||
tok.getName() = "Parameter" and
|
|
||||||
body = tok.getAnArgument()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate isArgBody(string body) {
|
|
||||||
exists(AccessPathToken tok |
|
|
||||||
tok.getName() = "Argument" and
|
|
||||||
body = tok.getAnArgument()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedPositionalParameterPosition(string c, int i) {
|
|
||||||
isParamBody(c) and
|
|
||||||
i = AccessPath::parseInt(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedKeywordParameterPosition(string c, string paramName) {
|
|
||||||
isParamBody(c) and
|
|
||||||
c = paramName + ":"
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedPositionalArgumentPosition(string c, int i) {
|
|
||||||
isArgBody(c) and
|
|
||||||
i = AccessPath::parseInt(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedKeywordArgumentPosition(string c, string argName) {
|
|
||||||
isArgBody(c) and
|
|
||||||
c = argName + ":"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
|
|
||||||
ArgumentPosition parseParamBody(string s) {
|
|
||||||
exists(int i |
|
|
||||||
ParsePositions::isParsedPositionalParameterPosition(s, i) and
|
|
||||||
result.isPositional(i)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string name |
|
|
||||||
ParsePositions::isParsedKeywordParameterPosition(s, name) and
|
|
||||||
result.isKeyword(name)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
s = "self" and
|
|
||||||
result.isSelf()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
|
|
||||||
ParameterPosition parseArgBody(string s) {
|
|
||||||
exists(int i |
|
|
||||||
ParsePositions::isParsedPositionalArgumentPosition(s, i) and
|
|
||||||
result.isPositional(i)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string name |
|
|
||||||
ParsePositions::isParsedKeywordArgumentPosition(s, name) and
|
|
||||||
result.isKeyword(name)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
s = "self" and
|
|
||||||
result.isSelf()
|
|
||||||
}
|
|
|
@ -177,7 +177,7 @@ class Boolean extends boolean {
|
||||||
}
|
}
|
||||||
|
|
||||||
private import SummaryTypeTracker as SummaryTypeTracker
|
private import SummaryTypeTracker as SummaryTypeTracker
|
||||||
private import semmle.python.dataflow.new.FlowSummary as FlowSummary
|
private import semmle.python.dataflow.new.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch
|
private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch
|
||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
|
@ -205,30 +205,30 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||||
TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content) { none() }
|
TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content) { none() }
|
||||||
|
|
||||||
// Callables
|
// Callables
|
||||||
class SummarizedCallable = FlowSummary::SummarizedCallable;
|
class SummarizedCallable = FlowSummaryImpl::Private::SummarizedCallableImpl;
|
||||||
|
|
||||||
// Summaries and their stacks
|
// Summaries and their stacks
|
||||||
class SummaryComponent = FlowSummary::SummaryComponent;
|
class SummaryComponent = FlowSummaryImpl::Private::SummaryComponent;
|
||||||
|
|
||||||
class SummaryComponentStack = FlowSummary::SummaryComponentStack;
|
class SummaryComponentStack = FlowSummaryImpl::Private::SummaryComponentStack;
|
||||||
|
|
||||||
predicate singleton = FlowSummary::SummaryComponentStack::singleton/1;
|
predicate singleton = FlowSummaryImpl::Private::SummaryComponentStack::singleton/1;
|
||||||
|
|
||||||
predicate push = FlowSummary::SummaryComponentStack::push/2;
|
predicate push = FlowSummaryImpl::Private::SummaryComponentStack::push/2;
|
||||||
|
|
||||||
// Relating content to summaries
|
// Relating content to summaries
|
||||||
predicate content = FlowSummary::SummaryComponent::content/1;
|
predicate content = FlowSummaryImpl::Private::SummaryComponent::content/1;
|
||||||
|
|
||||||
SummaryComponent withoutContent(TypeTrackerContent contents) { none() }
|
SummaryComponent withoutContent(TypeTrackerContent contents) { none() }
|
||||||
|
|
||||||
SummaryComponent withContent(TypeTrackerContent contents) { none() }
|
SummaryComponent withContent(TypeTrackerContent contents) { none() }
|
||||||
|
|
||||||
predicate return = FlowSummary::SummaryComponent::return/0;
|
predicate return = FlowSummaryImpl::Private::SummaryComponent::return/0;
|
||||||
|
|
||||||
// Relating nodes to summaries
|
// Relating nodes to summaries
|
||||||
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
|
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
|
||||||
exists(DataFlowDispatch::ParameterPosition pos |
|
exists(DataFlowDispatch::ParameterPosition pos |
|
||||||
arg = FlowSummary::SummaryComponent::argument(pos) and
|
arg = FlowSummaryImpl::Private::SummaryComponent::argument(pos) and
|
||||||
argumentPositionMatch(call, result, pos) and
|
argumentPositionMatch(call, result, pos) and
|
||||||
isPostUpdate = [false, true] // todo: implement when/if Python uses post-update nodes in type tracking
|
isPostUpdate = [false, true] // todo: implement when/if Python uses post-update nodes in type tracking
|
||||||
)
|
)
|
||||||
|
@ -238,7 +238,7 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||||
exists(
|
exists(
|
||||||
DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos, Parameter p
|
DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos, Parameter p
|
||||||
|
|
|
|
||||||
param = FlowSummary::SummaryComponent::parameter(apos) and
|
param = FlowSummaryImpl::Private::SummaryComponent::parameter(apos) and
|
||||||
DataFlowDispatch::parameterMatch(ppos, apos) and
|
DataFlowDispatch::parameterMatch(ppos, apos) and
|
||||||
result.asCfgNode().getNode() = p and
|
result.asCfgNode().getNode() = p and
|
||||||
(
|
(
|
||||||
|
@ -254,14 +254,16 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
Node returnOf(Node callable, SummaryComponent return) {
|
Node returnOf(Node callable, SummaryComponent return) {
|
||||||
return = FlowSummary::SummaryComponent::return() and
|
return = FlowSummaryImpl::Private::SummaryComponent::return() and
|
||||||
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
|
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
|
||||||
result.asCfgNode() =
|
result.asCfgNode() =
|
||||||
callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode()
|
callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relating callables to nodes
|
// Relating callables to nodes
|
||||||
Node callTo(SummarizedCallable callable) { result = callable.getACallSimple() }
|
Node callTo(SummarizedCallable callable) {
|
||||||
|
result = callable.(DataFlowDispatch::LibraryCallable).getACallSimple()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow<SummaryTypeTrackerInput>;
|
private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow<SummaryTypeTrackerInput>;
|
||||||
|
|
|
@ -624,7 +624,7 @@ module Flask {
|
||||||
.getAValueReachableFromSource()
|
.getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -650,7 +650,7 @@ module Flask {
|
||||||
.getAValueReachableFromSource()
|
.getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
// Technically it's `Iterator[str]`, but list will do :)
|
// Technically it's `Iterator[str]`, but list will do :)
|
||||||
output = "ReturnValue.ListElement" and
|
output = "ReturnValue.ListElement" and
|
||||||
|
|
|
@ -3085,7 +3085,7 @@ private module StdlibPrivate {
|
||||||
result = API::moduleImport("re").getMember("compile").getAValueReachableFromSource()
|
result = API::moduleImport("re").getMember("compile").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input in ["Argument[0]", "Argument[pattern:]"] and
|
input in ["Argument[0]", "Argument[pattern:]"] and
|
||||||
output = "ReturnValue.Attribute[pattern]" and
|
output = "ReturnValue.Attribute[pattern]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -3116,7 +3116,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(string arg |
|
exists(string arg |
|
||||||
this = "re.Match" and arg = "Argument[1]"
|
this = "re.Match" and arg = "Argument[1]"
|
||||||
or
|
or
|
||||||
|
@ -3173,7 +3173,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
methodName = "expand" and
|
methodName = "expand" and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
(
|
(
|
||||||
|
@ -3229,7 +3229,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(int offset |
|
exists(int offset |
|
||||||
// for non-compiled regex the first argument is the pattern, so we need to
|
// for non-compiled regex the first argument is the pattern, so we need to
|
||||||
// account for this difference
|
// account for this difference
|
||||||
|
@ -4079,7 +4079,7 @@ private module StdlibPrivate {
|
||||||
result = API::builtin("dict").getAValueReachableFromSource()
|
result = API::builtin("dict").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||||
input = "Argument[0].DictionaryElement[" + key + "]" and
|
input = "Argument[0].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue.DictionaryElement[" + key + "]" and
|
output = "ReturnValue.DictionaryElement[" + key + "]" and
|
||||||
|
@ -4108,7 +4108,7 @@ private module StdlibPrivate {
|
||||||
result = API::builtin("list").getAValueReachableFromSource()
|
result = API::builtin("list").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[0].ListElement"
|
input = "Argument[0].ListElement"
|
||||||
or
|
or
|
||||||
|
@ -4138,7 +4138,7 @@ private module StdlibPrivate {
|
||||||
result = API::builtin("tuple").getAValueReachableFromSource()
|
result = API::builtin("tuple").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
exists(DataFlow::TupleElementContent tc, int i | i = tc.getIndex() |
|
||||||
input = "Argument[0].TupleElement[" + i.toString() + "]" and
|
input = "Argument[0].TupleElement[" + i.toString() + "]" and
|
||||||
output = "ReturnValue.TupleElement[" + i.toString() + "]" and
|
output = "ReturnValue.TupleElement[" + i.toString() + "]" and
|
||||||
|
@ -4163,7 +4163,7 @@ private module StdlibPrivate {
|
||||||
result = API::builtin("set").getAValueReachableFromSource()
|
result = API::builtin("set").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[0].ListElement"
|
input = "Argument[0].ListElement"
|
||||||
or
|
or
|
||||||
|
@ -4193,8 +4193,8 @@ private module StdlibPrivate {
|
||||||
result = API::builtin("frozenset").getAValueReachableFromSource()
|
result = API::builtin("frozenset").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
any(SetSummary s).propagatesFlowExt(input, output, preservesValue)
|
any(SetSummary s).propagatesFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4211,7 +4211,7 @@ private module StdlibPrivate {
|
||||||
result = API::builtin("reversed").getAValueReachableFromSource()
|
result = API::builtin("reversed").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[0].ListElement"
|
input = "Argument[0].ListElement"
|
||||||
or
|
or
|
||||||
|
@ -4241,7 +4241,7 @@ private module StdlibPrivate {
|
||||||
result = API::builtin("sorted").getAValueReachableFromSource()
|
result = API::builtin("sorted").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(string content |
|
exists(string content |
|
||||||
content = "ListElement"
|
content = "ListElement"
|
||||||
or
|
or
|
||||||
|
@ -4273,7 +4273,7 @@ private module StdlibPrivate {
|
||||||
result = API::builtin("iter").getAValueReachableFromSource()
|
result = API::builtin("iter").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[0].ListElement"
|
input = "Argument[0].ListElement"
|
||||||
or
|
or
|
||||||
|
@ -4303,7 +4303,7 @@ private module StdlibPrivate {
|
||||||
result = API::builtin("next").getAValueReachableFromSource()
|
result = API::builtin("next").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[0].ListElement"
|
input = "Argument[0].ListElement"
|
||||||
or
|
or
|
||||||
|
@ -4336,7 +4336,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(string content |
|
exists(string content |
|
||||||
content = "ListElement"
|
content = "ListElement"
|
||||||
or
|
or
|
||||||
|
@ -4378,7 +4378,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].ListElement" and
|
input = "Argument[self].ListElement" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -4415,7 +4415,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -4438,7 +4438,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -4460,7 +4460,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// default value
|
// default value
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
|
@ -4483,7 +4483,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue.TupleElement[1]" and
|
output = "ReturnValue.TupleElement[1]" and
|
||||||
|
@ -4509,7 +4509,7 @@ private module StdlibPrivate {
|
||||||
result.(DataFlow::AttrRead).getAttributeName() = "setdefault"
|
result.(DataFlow::AttrRead).getAttributeName() = "setdefault"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// store/read steps with dictionary content of this is modeled in DataFlowPrivate
|
// store/read steps with dictionary content of this is modeled in DataFlowPrivate
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
|
@ -4538,7 +4538,7 @@ private module StdlibPrivate {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { none() }
|
override DataFlow::ArgumentNode getACallback() { none() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// If key is in the dictionary, return its value.
|
// If key is in the dictionary, return its value.
|
||||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
|
@ -4567,7 +4567,7 @@ private module StdlibPrivate {
|
||||||
result.(DataFlow::AttrRead).getAttributeName() = "values"
|
result.(DataFlow::AttrRead).getAttributeName() = "values"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue.ListElement" and
|
output = "ReturnValue.ListElement" and
|
||||||
|
@ -4594,7 +4594,7 @@ private module StdlibPrivate {
|
||||||
result.(DataFlow::AttrRead).getAttributeName() = "keys"
|
result.(DataFlow::AttrRead).getAttributeName() = "keys"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
// TODO: Once we have DictKeyContent, we need to transform that into ListElementContent
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
|
@ -4618,7 +4618,7 @@ private module StdlibPrivate {
|
||||||
result.(DataFlow::AttrRead).getAttributeName() = "items"
|
result.(DataFlow::AttrRead).getAttributeName() = "items"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
exists(DataFlow::DictionaryElementContent dc, string key | key = dc.getKey() |
|
||||||
input = "Argument[self].DictionaryElement[" + key + "]" and
|
input = "Argument[self].DictionaryElement[" + key + "]" and
|
||||||
output = "ReturnValue.ListElement.TupleElement[1]" and
|
output = "ReturnValue.ListElement.TupleElement[1]" and
|
||||||
|
@ -4648,7 +4648,7 @@ private module StdlibPrivate {
|
||||||
result.(DataFlow::AttrRead).getAttributeName() = "append"
|
result.(DataFlow::AttrRead).getAttributeName() = "append"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// newly added element added to this
|
// newly added element added to this
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "Argument[self].ListElement" and
|
output = "Argument[self].ListElement" and
|
||||||
|
@ -4675,7 +4675,7 @@ private module StdlibPrivate {
|
||||||
result.(DataFlow::AttrRead).getAttributeName() = "add"
|
result.(DataFlow::AttrRead).getAttributeName() = "add"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// newly added element added to this
|
// newly added element added to this
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "Argument[self].SetElement" and
|
output = "Argument[self].SetElement" and
|
||||||
|
@ -4705,7 +4705,7 @@ private module StdlibPrivate {
|
||||||
API::moduleImport("os").getMember(["getenv", "getenvb"]).getAValueReachableFromSource()
|
API::moduleImport("os").getMember(["getenv", "getenvb"]).getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input in ["Argument[1]", "Argument[default:]"] and
|
input in ["Argument[1]", "Argument[default:]"] and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -70,8 +70,8 @@ private module API = Specific::API;
|
||||||
|
|
||||||
private module DataFlow = Specific::DataFlow;
|
private module DataFlow = Specific::DataFlow;
|
||||||
|
|
||||||
private import Specific::AccessPathSyntax
|
|
||||||
private import ApiGraphModelsExtensions as Extensions
|
private import ApiGraphModelsExtensions as Extensions
|
||||||
|
private import codeql.dataflow.internal.AccessPathSyntax
|
||||||
|
|
||||||
/** Module containing hooks for providing input data to be interpreted as a model. */
|
/** Module containing hooks for providing input data to be interpreted as a model. */
|
||||||
module ModelInput {
|
module ModelInput {
|
||||||
|
@ -327,29 +327,29 @@ predicate isRelevantFullPath(string type, string path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A string from a CSV row that should be parsed as an access path. */
|
/** A string from a CSV row that should be parsed as an access path. */
|
||||||
private class AccessPathRange extends AccessPath::Range {
|
private predicate accessPathRange(string s) {
|
||||||
AccessPathRange() {
|
isRelevantFullPath(_, s)
|
||||||
isRelevantFullPath(_, this)
|
or
|
||||||
or
|
exists(string type | isRelevantType(type) |
|
||||||
exists(string type | isRelevantType(type) |
|
summaryModel(type, _, s, _, _) or
|
||||||
summaryModel(type, _, this, _, _) or
|
summaryModel(type, _, _, s, _)
|
||||||
summaryModel(type, _, _, this, _)
|
)
|
||||||
)
|
or
|
||||||
or
|
typeVariableModel(_, s)
|
||||||
typeVariableModel(_, this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import AccessPath<accessPathRange/1>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a successor of `node` in the API graph.
|
* Gets a successor of `node` in the API graph.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
|
API::Node getSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
|
||||||
// API graphs use the same label for arguments and parameters. An edge originating from a
|
// API graphs use the same label for arguments and parameters. An edge originating from a
|
||||||
// use-node represents an argument, and an edge originating from a def-node represents a parameter.
|
// use-node represents an argument, and an edge originating from a def-node represents a parameter.
|
||||||
// We just map both to the same thing.
|
// We just map both to the same thing.
|
||||||
token.getName() = ["Argument", "Parameter"] and
|
token.getName() = ["Argument", "Parameter"] and
|
||||||
result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument()))
|
result = node.getParameter(parseIntUnbounded(token.getAnArgument()))
|
||||||
or
|
or
|
||||||
token.getName() = "ReturnValue" and
|
token.getName() = "ReturnValue" and
|
||||||
result = node.getReturn()
|
result = node.getReturn()
|
||||||
|
@ -362,11 +362,9 @@ API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||||
* Gets an API-graph successor for the given invocation.
|
* Gets an API-graph successor for the given invocation.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken token) {
|
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathTokenBase token) {
|
||||||
token.getName() = "Argument" and
|
token.getName() = "Argument" and
|
||||||
result =
|
result = invoke.getParameter(parseIntWithArity(token.getAnArgument(), invoke.getNumArgument()))
|
||||||
invoke
|
|
||||||
.getParameter(AccessPath::parseIntWithArity(token.getAnArgument(), invoke.getNumArgument()))
|
|
||||||
or
|
or
|
||||||
token.getName() = "ReturnValue" and
|
token.getName() = "ReturnValue" and
|
||||||
result = invoke.getReturn()
|
result = invoke.getReturn()
|
||||||
|
@ -378,10 +376,12 @@ API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken to
|
||||||
/**
|
/**
|
||||||
* Holds if `invoke` invokes a call-site filter given by `token`.
|
* Holds if `invoke` invokes a call-site filter given by `token`.
|
||||||
*/
|
*/
|
||||||
pragma[inline]
|
bindingset[token]
|
||||||
private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, AccessPathToken token) {
|
private predicate invocationMatchesCallSiteFilter(
|
||||||
|
Specific::InvokeNode invoke, AccessPathTokenBase token
|
||||||
|
) {
|
||||||
token.getName() = "WithArity" and
|
token.getName() = "WithArity" and
|
||||||
invoke.getNumArgument() = AccessPath::parseIntUnbounded(token.getAnArgument())
|
invoke.getNumArgument() = parseIntUnbounded(token.getAnArgument())
|
||||||
or
|
or
|
||||||
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
|
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
* It must export the following members:
|
* It must export the following members:
|
||||||
* ```ql
|
* ```ql
|
||||||
* class Unit // a unit type
|
* class Unit // a unit type
|
||||||
* module AccessPathSyntax // a re-export of the AccessPathSyntax module
|
*
|
||||||
* class InvokeNode // a type representing an invocation connected to the API graph
|
* class InvokeNode // a type representing an invocation connected to the API graph
|
||||||
* module API // the API graph module
|
* module API // the API graph module
|
||||||
* predicate isPackageUsed(string package)
|
* predicate isPackageUsed(string package)
|
||||||
* API::Node getExtraNodeFromPath(string package, string type, string path, int n)
|
* API::Node getExtraNodeFromPath(string package, string type, string path, int n)
|
||||||
* API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token)
|
* API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token)
|
||||||
* API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathToken token)
|
* API::Node getExtraSuccessorFromInvoke(API::InvokeNode node, AccessPathTokenBase token)
|
||||||
* predicate invocationMatchesExtraCallSiteFilter(API::InvokeNode invoke, AccessPathToken token)
|
* predicate invocationMatchesExtraCallSiteFilter(API::InvokeNode invoke, AccessPathTokenBase token)
|
||||||
* InvokeNode getAnInvocationOf(API::Node node)
|
* InvokeNode getAnInvocationOf(API::Node node)
|
||||||
* predicate isExtraValidTokenNameInIdentifyingAccessPath(string name)
|
* predicate isExtraValidTokenNameInIdentifyingAccessPath(string name)
|
||||||
* predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name)
|
* predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name)
|
||||||
|
@ -21,11 +21,10 @@
|
||||||
|
|
||||||
private import python as PY
|
private import python as PY
|
||||||
private import ApiGraphModels
|
private import ApiGraphModels
|
||||||
|
private import codeql.dataflow.internal.AccessPathSyntax
|
||||||
import semmle.python.ApiGraphs::API as API
|
import semmle.python.ApiGraphs::API as API
|
||||||
// Re-export libraries needed by ApiGraphModels.qll
|
// Re-export libraries needed by ApiGraphModels.qll
|
||||||
import semmle.python.dataflow.new.internal.AccessPathSyntax as AccessPathSyntax
|
|
||||||
import semmle.python.dataflow.new.DataFlow::DataFlow as DataFlow
|
import semmle.python.dataflow.new.DataFlow::DataFlow as DataFlow
|
||||||
private import AccessPathSyntax
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if models describing `type` may be relevant for the analysis of this database.
|
* Holds if models describing `type` may be relevant for the analysis of this database.
|
||||||
|
@ -49,7 +48,7 @@ API::Node getExtraNodeFromType(string type) { result = API::moduleImport(type) }
|
||||||
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.
|
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
|
||||||
token.getName() = "Member" and
|
token.getName() = "Member" and
|
||||||
result = node.getMember(token.getAnArgument())
|
result = node.getMember(token.getAnArgument())
|
||||||
or
|
or
|
||||||
|
@ -89,7 +88,7 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||||
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.
|
* Gets a Python-specific API graph successor of `node` reachable by resolving `token`.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getExtraSuccessorFromInvoke(API::CallNode node, AccessPathToken token) {
|
API::Node getExtraSuccessorFromInvoke(API::CallNode node, AccessPathTokenBase token) {
|
||||||
token.getName() = "Instance" and
|
token.getName() = "Instance" and
|
||||||
result = node.getReturn()
|
result = node.getReturn()
|
||||||
or
|
or
|
||||||
|
@ -129,7 +128,7 @@ API::Node getAFuzzySuccessor(API::Node node) {
|
||||||
* Holds if `invoke` matches the PY-specific call site filter in `token`.
|
* Holds if `invoke` matches the PY-specific call site filter in `token`.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
predicate invocationMatchesExtraCallSiteFilter(API::CallNode invoke, AccessPathToken token) {
|
predicate invocationMatchesExtraCallSiteFilter(API::CallNode invoke, AccessPathTokenBase token) {
|
||||||
token.getName() = "Call" and exists(invoke) // there is only one kind of call in Python.
|
token.getName() = "Call" and exists(invoke) // there is only one kind of call in Python.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,6 @@ import semmle.python.dataflow.new.FlowSummary
|
||||||
import semmle.python.dataflow.new.internal.FlowSummaryImpl
|
import semmle.python.dataflow.new.internal.FlowSummaryImpl
|
||||||
|
|
||||||
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
|
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
|
||||||
(sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and
|
(sc.propagatesFlow(s, _, _) or sc.propagatesFlow(_, s, _)) and
|
||||||
Private::External::invalidSpecComponent(s, c)
|
Private::External::invalidSpecComponent(s, c)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import semmle.python.dataflow.new.internal.FlowSummaryImpl
|
||||||
|
|
||||||
from SummarizedCallable sc, string s, string c, string attr
|
from SummarizedCallable sc, string s, string c, string attr
|
||||||
where
|
where
|
||||||
(sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and
|
(sc.propagatesFlow(s, _, _) or sc.propagatesFlow(_, s, _)) and
|
||||||
Private::External::invalidSpecComponent(s, c) and
|
Private::External::invalidSpecComponent(s, c) and
|
||||||
c = "Attribute[" + attr + "]"
|
c = "Attribute[" + attr + "]"
|
||||||
select "The attribute \"" + attr +
|
select "The attribute \"" + attr +
|
||||||
|
|
|
@ -18,6 +18,10 @@ module RecursionGuard {
|
||||||
(TT::callStep(_, _) implies any())
|
(TT::callStep(_, _) implies any())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +35,7 @@ private class SummarizedCallableIdentity extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -48,7 +52,7 @@ private class SummarizedCallableApplyLambda extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
output = "Argument[0].Parameter[0]" and
|
output = "Argument[0].Parameter[0]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -68,7 +72,7 @@ private class SummarizedCallableReversed extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0].ListElement" and
|
input = "Argument[0].ListElement" and
|
||||||
output = "ReturnValue.ListElement" and
|
output = "ReturnValue.ListElement" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -84,7 +88,7 @@ private class SummarizedCallableMap extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[1].ListElement" and
|
input = "Argument[1].ListElement" and
|
||||||
output = "Argument[0].Parameter[0]" and
|
output = "Argument[0].Parameter[0]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -104,7 +108,7 @@ private class SummarizedCallableAppend extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -126,7 +130,7 @@ private class SummarizedCallableJsonLoads extends SummarizedCallable {
|
||||||
result = API::moduleImport("json").getMember("loads").getAValueReachableFromSource()
|
result = API::moduleImport("json").getMember("loads").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue.ListElement" and
|
output = "ReturnValue.ListElement" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -12,7 +12,7 @@ import experimental.dataflow.testTaintConfig
|
||||||
private import TestSummaries
|
private import TestSummaries
|
||||||
|
|
||||||
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
|
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
|
||||||
(sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and
|
(sc.propagatesFlow(s, _, _) or sc.propagatesFlow(_, s, _)) and
|
||||||
Private::External::invalidSpecComponent(s, c)
|
Private::External::invalidSpecComponent(s, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,10 @@ module RecursionGuard {
|
||||||
(TT::callStep(_, _) implies any())
|
(TT::callStep(_, _) implies any())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
override DataFlow::CallCfgNode getACallSimple() { none() }
|
override DataFlow::CallCfgNode getACallSimple() { none() }
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
@ -39,7 +43,7 @@ private class SummarizedCallableIdentity extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -58,7 +62,7 @@ private class SummarizedCallableApplyLambda extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
output = "Argument[0].Parameter[0]" and
|
output = "Argument[0].Parameter[0]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -80,7 +84,7 @@ private class SummarizedCallableReversed extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0].ListElement" and
|
input = "Argument[0].ListElement" and
|
||||||
output = "ReturnValue.ListElement" and
|
output = "ReturnValue.ListElement" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -98,7 +102,7 @@ private class SummarizedCallableMap extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[1].ListElement" and
|
input = "Argument[1].ListElement" and
|
||||||
output = "Argument[0].Parameter[0]" and
|
output = "Argument[0].Parameter[0]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -120,7 +124,7 @@ private class SummarizedCallableAppend extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -144,7 +148,7 @@ private class SummarizedCallableJsonLoads extends SummarizedCallable {
|
||||||
result = API::moduleImport("json").getMember("loads").getAValueReachableFromSource()
|
result = API::moduleImport("json").getMember("loads").getAValueReachableFromSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue.ListElement" and
|
output = "ReturnValue.ListElement" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -163,7 +167,7 @@ private class SummarizedCallableReadSecret extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0].Attribute[secret]" and
|
input = "Argument[0].Attribute[secret]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -181,7 +185,7 @@ private class SummarizedCallableSetSecret extends SummarizedCallable {
|
||||||
|
|
||||||
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
output = "Argument[0].Attribute[secret]" and
|
output = "Argument[0].Attribute[secret]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import python
|
import python
|
||||||
import semmle.python.dataflow.new.internal.AccessPathSyntax as AccessPathSyntax
|
private import semmle.python.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||||
import semmle.python.frameworks.data.ModelsAsData
|
import semmle.python.frameworks.data.ModelsAsData
|
||||||
import semmle.python.dataflow.new.TaintTracking
|
import semmle.python.dataflow.new.TaintTracking
|
||||||
import semmle.python.dataflow.new.DataFlow
|
import semmle.python.dataflow.new.DataFlow
|
||||||
|
@ -27,6 +27,6 @@ query predicate isSource(DataFlow::Node node, string kind) {
|
||||||
node = ModelOutput::getASourceNode(kind).asSource()
|
node = ModelOutput::getASourceNode(kind).asSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
query predicate syntaxErrors(AccessPathSyntax::AccessPath path) { path.hasSyntaxError() }
|
query predicate syntaxErrors(ApiGraphModels::AccessPath path) { path.hasSyntaxError() }
|
||||||
|
|
||||||
query predicate warning = ModelOutput::getAWarning/0;
|
query predicate warning = ModelOutput::getAWarning/0;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import python
|
import python
|
||||||
import semmle.python.dataflow.new.internal.AccessPathSyntax as AccessPathSyntax
|
|
||||||
import semmle.python.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
import semmle.python.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||||
import semmle.python.frameworks.data.ModelsAsData
|
import semmle.python.frameworks.data.ModelsAsData
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ have no source code, so we include a flow summary for it:
|
||||||
private class ChompSummary extends SimpleSummarizedCallable {
|
private class ChompSummary extends SimpleSummarizedCallable {
|
||||||
ChompSummary() { this = "chomp" }
|
ChompSummary() { this = "chomp" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
|
|
@ -8,7 +8,6 @@ private import internal.FlowSummaryImpl as Impl
|
||||||
private import internal.DataFlowDispatch
|
private import internal.DataFlowDispatch
|
||||||
private import internal.DataFlowImplCommon as DataFlowImplCommon
|
private import internal.DataFlowImplCommon as DataFlowImplCommon
|
||||||
private import internal.DataFlowPrivate
|
private import internal.DataFlowPrivate
|
||||||
private import internal.FlowSummaryImplSpecific
|
|
||||||
|
|
||||||
// import all instances below
|
// import all instances below
|
||||||
private module Summaries {
|
private module Summaries {
|
||||||
|
@ -16,104 +15,13 @@ private module Summaries {
|
||||||
private import codeql.ruby.frameworks.data.ModelsAsData
|
private import codeql.ruby.frameworks.data.ModelsAsData
|
||||||
}
|
}
|
||||||
|
|
||||||
class SummaryComponent = Impl::Public::SummaryComponent;
|
deprecated class SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
|
|
||||||
/** Provides predicates for constructing summary components. */
|
deprecated module SummaryComponent = Impl::Private::SummaryComponent;
|
||||||
module SummaryComponent {
|
|
||||||
private import Impl::Public::SummaryComponent as SC
|
|
||||||
|
|
||||||
predicate parameter = SC::parameter/1;
|
deprecated class SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
|
|
||||||
predicate argument = SC::argument/1;
|
deprecated module SummaryComponentStack = Impl::Private::SummaryComponentStack;
|
||||||
|
|
||||||
predicate content = SC::content/1;
|
|
||||||
|
|
||||||
predicate withoutContent = SC::withoutContent/1;
|
|
||||||
|
|
||||||
predicate withContent = SC::withContent/1;
|
|
||||||
|
|
||||||
class SyntheticGlobal = SC::SyntheticGlobal;
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a receiver. */
|
|
||||||
SummaryComponent receiver() { result = argument(any(ParameterPosition pos | pos.isSelf())) }
|
|
||||||
|
|
||||||
/** Gets a summary component that represents a block argument. */
|
|
||||||
SummaryComponent block() { result = argument(any(ParameterPosition pos | pos.isBlock())) }
|
|
||||||
|
|
||||||
/** Gets a summary component that represents an element in a collection at an unknown index. */
|
|
||||||
SummaryComponent elementUnknown() {
|
|
||||||
result = SC::content(TSingletonContent(TUnknownElementContent()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a summary component that represents an element in a collection at a known index. */
|
|
||||||
SummaryComponent elementKnown(ConstantValue cv) {
|
|
||||||
result = SC::content(TSingletonContent(DataFlow::Content::getElementContent(cv)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a summary component that represents an element in a collection at a specific
|
|
||||||
* known index `cv`, or an unknown index.
|
|
||||||
*/
|
|
||||||
SummaryComponent elementKnownOrUnknown(ConstantValue cv) {
|
|
||||||
result = SC::content(TKnownOrUnknownElementContent(TKnownElementContent(cv)))
|
|
||||||
or
|
|
||||||
not exists(TKnownElementContent(cv)) and
|
|
||||||
result = elementUnknown()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a summary component that represents an element in a collection at either an unknown
|
|
||||||
* index or known index. This has the same semantics as
|
|
||||||
*
|
|
||||||
* ```ql
|
|
||||||
* elementKnown() or elementUnknown(_)
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* but is more efficient, because it is represented by a single value.
|
|
||||||
*/
|
|
||||||
SummaryComponent elementAny() { result = SC::content(TAnyElementContent()) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a summary component that represents an element in a collection at known
|
|
||||||
* integer index `lower` or above.
|
|
||||||
*/
|
|
||||||
SummaryComponent elementLowerBound(int lower) {
|
|
||||||
result = SC::content(TElementLowerBoundContent(lower, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a summary component that represents an element in a collection at known
|
|
||||||
* integer index `lower` or above, or possibly at an unknown index.
|
|
||||||
*/
|
|
||||||
SummaryComponent elementLowerBoundOrUnknown(int lower) {
|
|
||||||
result = SC::content(TElementLowerBoundContent(lower, true))
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets a summary component that represents the return value of a call. */
|
|
||||||
SummaryComponent return() { result = SC::return(any(NormalReturnKind rk)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
|
|
||||||
|
|
||||||
/** Provides predicates for constructing stacks of summary components. */
|
|
||||||
module SummaryComponentStack {
|
|
||||||
private import Impl::Public::SummaryComponentStack as SCS
|
|
||||||
|
|
||||||
predicate singleton = SCS::singleton/1;
|
|
||||||
|
|
||||||
predicate push = SCS::push/2;
|
|
||||||
|
|
||||||
predicate argument = SCS::argument/1;
|
|
||||||
|
|
||||||
/** Gets a singleton stack representing a receiver. */
|
|
||||||
SummaryComponentStack receiver() { result = singleton(SummaryComponent::receiver()) }
|
|
||||||
|
|
||||||
/** Gets a singleton stack representing a block argument. */
|
|
||||||
SummaryComponentStack block() { result = singleton(SummaryComponent::block()) }
|
|
||||||
|
|
||||||
/** Gets a singleton stack representing the return value of a call. */
|
|
||||||
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A callable with a flow summary, identified by a unique string. */
|
/** A callable with a flow summary, identified by a unique string. */
|
||||||
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
|
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
|
||||||
|
@ -121,18 +29,11 @@ abstract class SummarizedCallable extends LibraryCallable, Impl::Public::Summari
|
||||||
SummarizedCallable() { any() }
|
SummarizedCallable() { any() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as
|
* DEPRECATED: Use `propagatesFlow` instead.
|
||||||
*
|
|
||||||
* ```ql
|
|
||||||
* propagatesFlow(
|
|
||||||
* SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
|
|
||||||
* )
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* but uses an external (string) representation of the input and output stacks.
|
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
deprecated predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||||
predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() }
|
this.propagatesFlow(input, output, preservesValue)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the synthesized parameter that results from an input specification
|
* Gets the synthesized parameter that results from an input specification
|
||||||
|
@ -141,7 +42,7 @@ abstract class SummarizedCallable extends LibraryCallable, Impl::Public::Summari
|
||||||
DataFlow::ParameterNode getParameter(string s) {
|
DataFlow::ParameterNode getParameter(string s) {
|
||||||
exists(ParameterPosition pos |
|
exists(ParameterPosition pos |
|
||||||
DataFlowImplCommon::parameterNode(result, TLibraryCallable(this), pos) and
|
DataFlowImplCommon::parameterNode(result, TLibraryCallable(this), pos) and
|
||||||
s = getParameterPosition(pos)
|
s = Impl::Input::encodeParameterPosition(pos)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +60,7 @@ abstract class SimpleSummarizedCallable extends SummarizedCallable {
|
||||||
final override MethodCall getACallSimple() { result = mc }
|
final override MethodCall getACallSimple() { result = mc }
|
||||||
}
|
}
|
||||||
|
|
||||||
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
|
deprecated class RequiredSummaryComponentStack = Impl::Private::RequiredSummaryComponentStack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a set of special flow summaries to ensure that callbacks passed into
|
* Provides a set of special flow summaries to ensure that callbacks passed into
|
||||||
|
@ -199,7 +100,7 @@ private module LibraryCallbackSummaries {
|
||||||
libraryCallHasLambdaArg(result.getAControlFlowNode(), _)
|
libraryCallHasLambdaArg(result.getAControlFlowNode(), _)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[block]" and
|
input = "Argument[block]" and
|
||||||
output = "Argument[block].Parameter[lambda-self]"
|
output = "Argument[block].Parameter[lambda-self]"
|
||||||
|
|
|
@ -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))) }
|
|
||||||
}
|
|
|
@ -4,7 +4,6 @@ private import DataFlowPrivate
|
||||||
private import codeql.ruby.typetracking.internal.TypeTrackingImpl
|
private import codeql.ruby.typetracking.internal.TypeTrackingImpl
|
||||||
private import codeql.ruby.ast.internal.Module
|
private import codeql.ruby.ast.internal.Module
|
||||||
private import FlowSummaryImpl as FlowSummaryImpl
|
private import FlowSummaryImpl as FlowSummaryImpl
|
||||||
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
|
||||||
private import codeql.ruby.dataflow.FlowSummary
|
private import codeql.ruby.dataflow.FlowSummary
|
||||||
private import codeql.ruby.dataflow.SSA
|
private import codeql.ruby.dataflow.SSA
|
||||||
private import codeql.util.Boolean
|
private import codeql.util.Boolean
|
||||||
|
@ -426,14 +425,14 @@ private module Cached {
|
||||||
TPositionalArgumentPosition(int pos) {
|
TPositionalArgumentPosition(int pos) {
|
||||||
exists(Call c | exists(c.getArgument(pos)))
|
exists(Call c | exists(c.getArgument(pos)))
|
||||||
or
|
or
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedParameterPosition(_, pos)
|
FlowSummaryImpl::ParsePositions::isParsedParameterPosition(_, pos)
|
||||||
} or
|
} or
|
||||||
TKeywordArgumentPosition(string name) {
|
TKeywordArgumentPosition(string name) {
|
||||||
name = any(KeywordParameter kp).getName()
|
name = any(KeywordParameter kp).getName()
|
||||||
or
|
or
|
||||||
exists(any(Call c).getKeywordArgument(name))
|
exists(any(Call c).getKeywordArgument(name))
|
||||||
or
|
or
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedKeywordParameterPosition(_, name)
|
FlowSummaryImpl::ParsePositions::isParsedKeywordParameterPosition(_, name)
|
||||||
} or
|
} or
|
||||||
THashSplatArgumentPosition() or
|
THashSplatArgumentPosition() or
|
||||||
TSynthHashSplatArgumentPosition() or
|
TSynthHashSplatArgumentPosition() or
|
||||||
|
@ -450,15 +449,17 @@ private module Cached {
|
||||||
TPositionalParameterPosition(int pos) {
|
TPositionalParameterPosition(int pos) {
|
||||||
pos = any(Parameter p).getPosition()
|
pos = any(Parameter p).getPosition()
|
||||||
or
|
or
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedArgumentPosition(_, pos)
|
FlowSummaryImpl::ParsePositions::isParsedArgumentPosition(_, pos)
|
||||||
} or
|
} or
|
||||||
TPositionalParameterLowerBoundPosition(int pos) {
|
TPositionalParameterLowerBoundPosition(int pos) {
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedArgumentLowerBoundPosition(_, pos)
|
FlowSummaryImpl::ParsePositions::isParsedArgumentLowerBoundPosition(_, pos)
|
||||||
} or
|
} or
|
||||||
TKeywordParameterPosition(string name) {
|
TKeywordParameterPosition(string name) {
|
||||||
name = any(KeywordParameter kp).getName()
|
name = any(KeywordParameter kp).getName()
|
||||||
or
|
or
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedKeywordArgumentPosition(_, name)
|
exists(any(Call c).getKeywordArgument(name))
|
||||||
|
or
|
||||||
|
FlowSummaryImpl::ParsePositions::isParsedKeywordArgumentPosition(_, name)
|
||||||
} or
|
} or
|
||||||
THashSplatParameterPosition() or
|
THashSplatParameterPosition() or
|
||||||
TSynthHashSplatParameterPosition() or
|
TSynthHashSplatParameterPosition() or
|
||||||
|
|
|
@ -8,7 +8,6 @@ private import DataFlowPublic
|
||||||
private import DataFlowDispatch
|
private import DataFlowDispatch
|
||||||
private import SsaImpl as SsaImpl
|
private import SsaImpl as SsaImpl
|
||||||
private import FlowSummaryImpl as FlowSummaryImpl
|
private import FlowSummaryImpl as FlowSummaryImpl
|
||||||
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
|
||||||
private import codeql.ruby.frameworks.data.ModelsAsData
|
private import codeql.ruby.frameworks.data.ModelsAsData
|
||||||
|
|
||||||
/** Gets the callable in which this node occurs. */
|
/** Gets the callable in which this node occurs. */
|
||||||
|
@ -629,8 +628,7 @@ private module Cached {
|
||||||
TAnyElementContent() or
|
TAnyElementContent() or
|
||||||
TKnownOrUnknownElementContent(Content::KnownElementContent c) or
|
TKnownOrUnknownElementContent(Content::KnownElementContent c) or
|
||||||
TElementLowerBoundContent(int lower, boolean includeUnknown) {
|
TElementLowerBoundContent(int lower, boolean includeUnknown) {
|
||||||
FlowSummaryImplSpecific::ParsePositions::isParsedElementLowerBoundPosition(_, includeUnknown,
|
FlowSummaryImpl::ParsePositions::isParsedElementLowerBoundPosition(_, includeUnknown, lower)
|
||||||
lower)
|
|
||||||
} or
|
} or
|
||||||
TElementContentOfTypeContent(string type, Boolean includeUnknown) {
|
TElementContentOfTypeContent(string type, Boolean includeUnknown) {
|
||||||
type = any(Content::KnownElementContent content).getIndex().getValueType()
|
type = any(Content::KnownElementContent content).getIndex().getValueType()
|
||||||
|
@ -700,6 +698,21 @@ private module Cached {
|
||||||
THashSplatContentApprox(string approx) { approx = approxKnownElementIndex(_) } or
|
THashSplatContentApprox(string approx) { approx = approxKnownElementIndex(_) } or
|
||||||
TNonElementContentApprox(Content c) { not c instanceof Content::ElementContent } or
|
TNonElementContentApprox(Content c) { not c instanceof Content::ElementContent } or
|
||||||
TCapturedVariableContentApprox(VariableCapture::CapturedVariable v)
|
TCapturedVariableContentApprox(VariableCapture::CapturedVariable v)
|
||||||
|
|
||||||
|
cached
|
||||||
|
newtype TDataFlowType =
|
||||||
|
TLambdaDataFlowType(Callable c) { c = any(LambdaSelfReferenceNode n).getCallable() } or
|
||||||
|
// In order to reduce the set of cons-candidates, we annotate all implicit (hash) splat
|
||||||
|
// creations with the name of the method that they are passed into. This includes
|
||||||
|
// array/hash literals as well (where the name is simply `[]`), because of how they
|
||||||
|
// are modeled (see `Array.qll` and `Hash.qll`).
|
||||||
|
TSynthHashSplatArgumentType(string methodName) {
|
||||||
|
methodName = any(SynthHashSplatArgumentNode n).getMethodName()
|
||||||
|
} or
|
||||||
|
TSynthSplatArgumentType(string methodName) {
|
||||||
|
methodName = any(SynthSplatArgumentNode n).getMethodName()
|
||||||
|
} or
|
||||||
|
TUnknownDataFlowType()
|
||||||
}
|
}
|
||||||
|
|
||||||
class TElementContent =
|
class TElementContent =
|
||||||
|
@ -1254,11 +1267,11 @@ module ArgumentNodes {
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
|
private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode {
|
||||||
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 sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
|
override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) {
|
||||||
|
@ -1641,11 +1654,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_ }
|
||||||
|
@ -1803,20 +1816,6 @@ predicate expectsContent(Node n, ContentSet c) {
|
||||||
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
|
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
|
||||||
}
|
}
|
||||||
|
|
||||||
private newtype TDataFlowType =
|
|
||||||
TLambdaDataFlowType(Callable c) { c = any(LambdaSelfReferenceNode n).getCallable() } or
|
|
||||||
// In order to reduce the set of cons-candidates, we annotate all implicit (hash) splat
|
|
||||||
// creations with the name of the method that they are passed into. This includes
|
|
||||||
// array/hash literals as well (where the name is simply `[]`), because of how they
|
|
||||||
// are modeled (see `Array.qll` and `Hash.qll`).
|
|
||||||
TSynthHashSplatArgumentType(string methodName) {
|
|
||||||
methodName = any(SynthHashSplatArgumentNode n).getMethodName()
|
|
||||||
} or
|
|
||||||
TSynthSplatArgumentType(string methodName) {
|
|
||||||
methodName = any(SynthSplatArgumentNode n).getMethodName()
|
|
||||||
} or
|
|
||||||
TUnknownDataFlowType()
|
|
||||||
|
|
||||||
class DataFlowType extends TDataFlowType {
|
class DataFlowType extends TDataFlowType {
|
||||||
string toString() { result = "" }
|
string toString() { result = "" }
|
||||||
}
|
}
|
||||||
|
@ -2043,7 +2042,10 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
||||||
* by default as a heuristic.
|
* by default as a heuristic.
|
||||||
*/
|
*/
|
||||||
predicate allowParameterReturnInSelf(ParameterNodeImpl p) {
|
predicate allowParameterReturnInSelf(ParameterNodeImpl p) {
|
||||||
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
|
exists(DataFlowCallable c, ParameterPosition pos |
|
||||||
|
p.isParameterOf(c, pos) and
|
||||||
|
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(c.asLibraryCallable(), pos)
|
||||||
|
)
|
||||||
or
|
or
|
||||||
VariableCapture::Flow::heuristicAllowInstanceParameterReturnInSelf(p.(SelfParameterNode)
|
VariableCapture::Flow::heuristicAllowInstanceParameterReturnInSelf(p.(SelfParameterNode)
|
||||||
.getCallable())
|
.getCallable())
|
||||||
|
|
|
@ -212,12 +212,14 @@ class ExprNode extends Node, TExprNode {
|
||||||
* The value of a parameter at function entry, viewed as a node in a data
|
* The value of a parameter at function entry, viewed as a node in a data
|
||||||
* flow graph.
|
* flow graph.
|
||||||
*/
|
*/
|
||||||
class ParameterNode extends LocalSourceNode instanceof ParameterNodeImpl {
|
class ParameterNode extends LocalSourceNode {
|
||||||
|
ParameterNode() { exists(getParameterPosition(this, _)) }
|
||||||
|
|
||||||
/** Gets the parameter corresponding to this node, if any. */
|
/** Gets the parameter corresponding to this node, if any. */
|
||||||
final Parameter getParameter() { result = super.getParameter() }
|
final Parameter getParameter() { result = getParameter(this) }
|
||||||
|
|
||||||
/** Gets the callable that this parameter belongs to. */
|
/** Gets the callable that this parameter belongs to. */
|
||||||
final Callable getCallable() { result = super.getCfgScope() }
|
final Callable getCallable() { result = getCfgScope(this) }
|
||||||
|
|
||||||
/** Gets the name of the parameter, if any. */
|
/** Gets the name of the parameter, if any. */
|
||||||
final string getName() { result = this.getParameter().(NamedParameter).getName() }
|
final string getName() { result = this.getParameter().(NamedParameter).getName() }
|
||||||
|
@ -348,9 +350,13 @@ class LocalSourceNode extends Node {
|
||||||
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
* Nodes corresponding to AST elements, for example `ExprNode`, usually refer
|
||||||
* to the value before the update.
|
* to the value before the update.
|
||||||
*/
|
*/
|
||||||
class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
|
class PostUpdateNode extends Node {
|
||||||
|
private Node pre;
|
||||||
|
|
||||||
|
PostUpdateNode() { pre = getPreUpdateNode(this) }
|
||||||
|
|
||||||
/** Gets the node before the state update. */
|
/** Gets the node before the state update. */
|
||||||
Node getPreUpdateNode() { result = super.getPreUpdateNode() }
|
Node getPreUpdateNode() { result = pre }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An SSA definition, viewed as a node in a data flow graph. */
|
/** An SSA definition, viewed as a node in a data flow graph. */
|
||||||
|
@ -383,6 +389,28 @@ private module Cached {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
CfgScope getCfgScope(NodeImpl node) { result = node.getCfgScope() }
|
||||||
|
|
||||||
|
cached
|
||||||
|
ReturnNode getAReturnNode(Callable callable) { getCfgScope(result) = callable }
|
||||||
|
|
||||||
|
cached
|
||||||
|
Parameter getParameter(ParameterNodeImpl param) { result = param.getParameter() }
|
||||||
|
|
||||||
|
cached
|
||||||
|
ParameterPosition getParameterPosition(ParameterNodeImpl param, DataFlowCallable c) {
|
||||||
|
param.isParameterOf(c, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
ParameterPosition getSourceParameterPosition(ParameterNodeImpl param, Callable c) {
|
||||||
|
param.isSourceParameterOf(c, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
cached
|
||||||
|
Node getPreUpdateNode(PostUpdateNodeImpl node) { result = node.getPreUpdateNode() }
|
||||||
|
|
||||||
cached
|
cached
|
||||||
predicate methodHasSuperCall(MethodNode method, CallNode call) {
|
predicate methodHasSuperCall(MethodNode method, CallNode call) {
|
||||||
call.isSuperCall() and method = call.getEnclosingMethod()
|
call.isSuperCall() and method = call.getEnclosingMethod()
|
||||||
|
@ -1271,7 +1299,7 @@ class CallableNode extends StmtSequenceNode {
|
||||||
Callable asCallableAstNode() { result = callable }
|
Callable asCallableAstNode() { result = callable }
|
||||||
|
|
||||||
private ParameterPosition getParameterPosition(ParameterNodeImpl node) {
|
private ParameterPosition getParameterPosition(ParameterNodeImpl node) {
|
||||||
node.isSourceParameterOf(callable, result)
|
result = getSourceParameterPosition(node, callable)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the `n`th positional parameter. */
|
/** Gets the `n`th positional parameter. */
|
||||||
|
@ -1311,7 +1339,7 @@ class CallableNode extends StmtSequenceNode {
|
||||||
/**
|
/**
|
||||||
* Gets a data flow node whose value is about to be returned by this callable.
|
* Gets a data flow node whose value is about to be returned by this callable.
|
||||||
*/
|
*/
|
||||||
Node getAReturnNode() { result.(ReturnNode).(NodeImpl).getCfgScope() = callable }
|
Node getAReturnNode() { result = getAReturnNode(callable) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DEPRECATED. Use `getAReturnNode` instead.
|
* DEPRECATED. Use `getAReturnNode` instead.
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,430 +0,0 @@
|
||||||
/**
|
|
||||||
* Provides Ruby specific classes and predicates for defining flow summaries.
|
|
||||||
*/
|
|
||||||
|
|
||||||
private import codeql.ruby.AST
|
|
||||||
private import DataFlowDispatch
|
|
||||||
private import DataFlowPrivate
|
|
||||||
private import DataFlowPublic
|
|
||||||
private import DataFlowImplCommon
|
|
||||||
private import FlowSummaryImpl::Private
|
|
||||||
private import FlowSummaryImpl::Public
|
|
||||||
private import codeql.ruby.dataflow.FlowSummary as FlowSummary
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class of callables that are candidates for flow summary modeling.
|
|
||||||
*/
|
|
||||||
class SummarizedCallableBase = string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class of callables that are candidates for neutral modeling.
|
|
||||||
*/
|
|
||||||
class NeutralCallableBase = string;
|
|
||||||
|
|
||||||
DataFlowCallable inject(SummarizedCallable c) { result.asLibraryCallable() = c }
|
|
||||||
|
|
||||||
/** Gets the parameter position representing a callback itself, if any. */
|
|
||||||
ArgumentPosition callbackSelfParameterPosition() { result.isLambdaSelf() }
|
|
||||||
|
|
||||||
/** Gets the synthesized data-flow call for `receiver`. */
|
|
||||||
SummaryCall summaryDataFlowCall(SummaryNode receiver) { receiver = result.getReceiver() }
|
|
||||||
|
|
||||||
/** Gets the type of content `c`. */
|
|
||||||
DataFlowType getContentType(ContentSet c) { any() }
|
|
||||||
|
|
||||||
/** Gets the type of the parameter at the given position. */
|
|
||||||
DataFlowType getParameterType(SummarizedCallable c, ParameterPosition pos) { any() }
|
|
||||||
|
|
||||||
/** Gets the return type of kind `rk` for callable `c`. */
|
|
||||||
bindingset[c, rk]
|
|
||||||
DataFlowType getReturnType(SummarizedCallable c, ReturnKind rk) { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the type of the `i`th parameter in a synthesized call that targets a
|
|
||||||
* callback of type `t`.
|
|
||||||
*/
|
|
||||||
bindingset[t, pos]
|
|
||||||
DataFlowType getCallbackParameterType(DataFlowType t, ArgumentPosition pos) { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the return type of kind `rk` in a synthesized call that targets a
|
|
||||||
* callback of type `t`.
|
|
||||||
*/
|
|
||||||
DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() }
|
|
||||||
|
|
||||||
/** Gets the type of synthetic global `sg`. */
|
|
||||||
DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if an external flow summary exists for `c` with input specification
|
|
||||||
* `input`, output specification `output`, kind `kind`, and provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate summaryElement(
|
|
||||||
FlowSummary::SummarizedCallable c, string input, string output, string kind, string provenance
|
|
||||||
) {
|
|
||||||
exists(boolean preservesValue |
|
|
||||||
c.propagatesFlowExt(input, output, preservesValue) and
|
|
||||||
(if preservesValue = true then kind = "value" else kind = "taint") and
|
|
||||||
provenance = "manual"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if a neutral model exists for `c` of kind `kind`
|
|
||||||
* and with provenance `provenance`.
|
|
||||||
* Note. Neutral models have not been implemented for Ruby.
|
|
||||||
*/
|
|
||||||
predicate neutralElement(NeutralCallableBase c, string kind, string provenance) { none() }
|
|
||||||
|
|
||||||
bindingset[arg]
|
|
||||||
private SummaryComponent interpretElementArg(string arg) {
|
|
||||||
arg = "?" and
|
|
||||||
result = FlowSummary::SummaryComponent::elementUnknown()
|
|
||||||
or
|
|
||||||
arg = "any" and
|
|
||||||
result = FlowSummary::SummaryComponent::elementAny()
|
|
||||||
or
|
|
||||||
exists(int lower, boolean includeUnknown |
|
|
||||||
ParsePositions::isParsedElementLowerBoundPosition(arg, includeUnknown, lower)
|
|
||||||
|
|
|
||||||
includeUnknown = false and
|
|
||||||
result = FlowSummary::SummaryComponent::elementLowerBound(lower)
|
|
||||||
or
|
|
||||||
includeUnknown = true and
|
|
||||||
result = FlowSummary::SummaryComponent::elementLowerBoundOrUnknown(lower)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(ConstantValue cv, string argAdjusted, boolean includeUnknown |
|
|
||||||
argAdjusted = ParsePositions::adjustElementArgument(arg, includeUnknown) and
|
|
||||||
(
|
|
||||||
includeUnknown = false and
|
|
||||||
result = FlowSummary::SummaryComponent::elementKnown(cv)
|
|
||||||
or
|
|
||||||
includeUnknown = true and
|
|
||||||
result = FlowSummary::SummaryComponent::elementKnownOrUnknown(cv)
|
|
||||||
)
|
|
||||||
|
|
|
||||||
cv.isInt(AccessPath::parseInt(argAdjusted))
|
|
||||||
or
|
|
||||||
not exists(AccessPath::parseInt(argAdjusted)) and
|
|
||||||
cv.serialize() = argAdjusted
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the summary component for specification component `c`, if any.
|
|
||||||
*
|
|
||||||
* This covers all the Ruby-specific components of a flow summary.
|
|
||||||
*/
|
|
||||||
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
|
||||||
exists(string arg, ParameterPosition ppos |
|
|
||||||
arg = c.getAnArgument("Argument") and
|
|
||||||
result = FlowSummary::SummaryComponent::argument(ppos)
|
|
||||||
|
|
|
||||||
arg = "any" and
|
|
||||||
ppos.isAny()
|
|
||||||
or
|
|
||||||
ppos.isPositionalLowerBound(AccessPath::parseLowerBound(arg))
|
|
||||||
or
|
|
||||||
arg = "hash-splat" and
|
|
||||||
ppos.isHashSplat()
|
|
||||||
or
|
|
||||||
arg = "splat" and
|
|
||||||
ppos.isSplat(0)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
result = interpretElementArg(c.getAnArgument("Element"))
|
|
||||||
or
|
|
||||||
result =
|
|
||||||
FlowSummary::SummaryComponent::content(TSingletonContent(TFieldContent(c.getAnArgument("Field"))))
|
|
||||||
or
|
|
||||||
exists(ContentSet cs |
|
|
||||||
FlowSummary::SummaryComponent::content(cs) = interpretElementArg(c.getAnArgument("WithElement")) and
|
|
||||||
result = FlowSummary::SummaryComponent::withContent(cs)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(ContentSet cs |
|
|
||||||
FlowSummary::SummaryComponent::content(cs) =
|
|
||||||
interpretElementArg(c.getAnArgument("WithoutElement")) and
|
|
||||||
result = FlowSummary::SummaryComponent::withoutContent(cs)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private string getContentSpecific(Content c) {
|
|
||||||
exists(string name | c = TFieldContent(name) and result = "Field[" + name + "]")
|
|
||||||
or
|
|
||||||
exists(ConstantValue cv |
|
|
||||||
c = TKnownElementContent(cv) and result = "Element[" + cv.serialize() + "!]"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
c = TUnknownElementContent() and result = "Element[?]"
|
|
||||||
}
|
|
||||||
|
|
||||||
private string getContentSetSpecific(ContentSet cs) {
|
|
||||||
exists(Content c | cs = TSingletonContent(c) and result = getContentSpecific(c))
|
|
||||||
or
|
|
||||||
cs = TAnyElementContent() and result = "Element[any]"
|
|
||||||
or
|
|
||||||
exists(Content::KnownElementContent kec |
|
|
||||||
cs = TKnownOrUnknownElementContent(kec) and
|
|
||||||
result = "Element[" + kec.getIndex().serialize() + "]"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(int lower, boolean includeUnknown, string unknown |
|
|
||||||
cs = TElementLowerBoundContent(lower, includeUnknown) and
|
|
||||||
(if includeUnknown = true then unknown = "" else unknown = "!") and
|
|
||||||
result = "Element[" + lower + ".." + unknown + "]"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the textual representation of a summary component in the format used for MaD models. */
|
|
||||||
string getMadRepresentationSpecific(SummaryComponent sc) {
|
|
||||||
exists(ContentSet cs | sc = TContentSummaryComponent(cs) and result = getContentSetSpecific(cs))
|
|
||||||
or
|
|
||||||
exists(ContentSet cs |
|
|
||||||
sc = TWithoutContentSummaryComponent(cs) and
|
|
||||||
result = "WithoutElement[" + getContentSetSpecific(cs) + "]"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(ContentSet cs |
|
|
||||||
sc = TWithContentSummaryComponent(cs) and
|
|
||||||
result = "WithElement[" + getContentSetSpecific(cs) + "]"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(ReturnKind rk |
|
|
||||||
sc = TReturnSummaryComponent(rk) and
|
|
||||||
not rk = getReturnValueKind() and
|
|
||||||
result = "ReturnValue[" + rk + "]"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
|
|
||||||
string getParameterPosition(ParameterPosition pos) {
|
|
||||||
exists(int i |
|
|
||||||
pos.isPositional(i) and
|
|
||||||
result = i.toString()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(int i |
|
|
||||||
pos.isPositionalLowerBound(i) and
|
|
||||||
result = i + ".."
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string name |
|
|
||||||
pos.isKeyword(name) and
|
|
||||||
result = name + ":"
|
|
||||||
)
|
|
||||||
or
|
|
||||||
pos.isSelf() and
|
|
||||||
result = "self"
|
|
||||||
or
|
|
||||||
pos.isLambdaSelf() and
|
|
||||||
result = "lambda-self"
|
|
||||||
or
|
|
||||||
pos.isBlock() and
|
|
||||||
result = "block"
|
|
||||||
or
|
|
||||||
pos.isAny() and
|
|
||||||
result = "any"
|
|
||||||
or
|
|
||||||
pos.isAnyNamed() and
|
|
||||||
result = "any-named"
|
|
||||||
or
|
|
||||||
pos.isHashSplat() and
|
|
||||||
result = "hash-splat"
|
|
||||||
or
|
|
||||||
pos.isSplat(0) and
|
|
||||||
result = "splat"
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the textual representation of an argument position in the format used for flow summaries. */
|
|
||||||
string getArgumentPosition(ArgumentPosition pos) {
|
|
||||||
pos.isSelf() and result = "self"
|
|
||||||
or
|
|
||||||
pos.isLambdaSelf() and result = "lambda-self"
|
|
||||||
or
|
|
||||||
pos.isBlock() and result = "block"
|
|
||||||
or
|
|
||||||
exists(int i |
|
|
||||||
pos.isPositional(i) and
|
|
||||||
result = i.toString()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string name |
|
|
||||||
pos.isKeyword(name) and
|
|
||||||
result = name + ":"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 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() }
|
|
||||||
|
|
||||||
/** Gets the return kind corresponding to specification `"ReturnValue"`. */
|
|
||||||
NormalReturnKind getReturnValueKind() { any() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All definitions in this module are required by the shared implementation
|
|
||||||
* (for source/sink interpretation), but they are unused for Ruby, where
|
|
||||||
* we rely on API graphs instead.
|
|
||||||
*/
|
|
||||||
private module UnusedSourceSinkInterpretation {
|
|
||||||
/**
|
|
||||||
* Holds if an external source specification exists for `n` with output specification
|
|
||||||
* `output`, kind `kind`, and provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate sourceElement(AstNode n, string output, string kind, string provenance) { none() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if an external sink specification exists for `n` with input specification
|
|
||||||
* `input`, kind `kind` and provenance `provenance`.
|
|
||||||
*/
|
|
||||||
predicate sinkElement(AstNode n, string input, string kind, string provenance) { none() }
|
|
||||||
|
|
||||||
class SourceOrSinkElement = AstNode;
|
|
||||||
|
|
||||||
/** An entity used to interpret a source/sink specification. */
|
|
||||||
class InterpretNode extends AstNode {
|
|
||||||
/** Gets the element that this node corresponds to, if any. */
|
|
||||||
SourceOrSinkElement asElement() { none() }
|
|
||||||
|
|
||||||
/** Gets the data-flow node that this node corresponds to, if any. */
|
|
||||||
Node asNode() { none() }
|
|
||||||
|
|
||||||
/** Gets the call that this node corresponds to, if any. */
|
|
||||||
DataFlowCall asCall() { none() }
|
|
||||||
|
|
||||||
/** Gets the callable that this node corresponds to, if any. */
|
|
||||||
DataFlowCallable asCallable() { none() }
|
|
||||||
|
|
||||||
/** Gets the target of this call, if any. */
|
|
||||||
Callable getCallTarget() { none() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Provides additional sink specification logic. */
|
|
||||||
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
|
|
||||||
|
|
||||||
/** Provides additional source specification logic. */
|
|
||||||
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
|
|
||||||
}
|
|
||||||
|
|
||||||
import UnusedSourceSinkInterpretation
|
|
||||||
|
|
||||||
module ParsePositions {
|
|
||||||
private import FlowSummaryImpl
|
|
||||||
|
|
||||||
private predicate isParamBody(string body) {
|
|
||||||
body = any(AccessPathToken tok).getAnArgument("Parameter")
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate isArgBody(string body) {
|
|
||||||
body = any(AccessPathToken tok).getAnArgument("Argument")
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate isElementBody(string body) {
|
|
||||||
body = any(AccessPathToken tok).getAnArgument(["Element", "WithElement", "WithoutElement"])
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedParameterPosition(string c, int i) {
|
|
||||||
isParamBody(c) and
|
|
||||||
i = AccessPath::parseInt(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedArgumentPosition(string c, int i) {
|
|
||||||
isArgBody(c) and
|
|
||||||
i = AccessPath::parseInt(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedArgumentLowerBoundPosition(string c, int i) {
|
|
||||||
isArgBody(c) and
|
|
||||||
i = AccessPath::parseLowerBound(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedKeywordParameterPosition(string c, string paramName) {
|
|
||||||
isParamBody(c) and
|
|
||||||
c = paramName + ":"
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedKeywordArgumentPosition(string c, string paramName) {
|
|
||||||
isArgBody(c) and
|
|
||||||
c = paramName + ":"
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingset[arg]
|
|
||||||
string adjustElementArgument(string arg, boolean includeUnknown) {
|
|
||||||
result = arg.regexpCapture("(.*)!", 1) and
|
|
||||||
includeUnknown = false
|
|
||||||
or
|
|
||||||
result = arg and
|
|
||||||
not arg.matches("%!") and
|
|
||||||
includeUnknown = true
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isParsedElementLowerBoundPosition(string c, boolean includeUnknown, int lower) {
|
|
||||||
isElementBody(c) and
|
|
||||||
lower = AccessPath::parseLowerBound(adjustElementArgument(c, includeUnknown))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
|
|
||||||
ArgumentPosition parseParamBody(string s) {
|
|
||||||
exists(int i |
|
|
||||||
ParsePositions::isParsedParameterPosition(s, i) and
|
|
||||||
result.isPositional(i)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string name |
|
|
||||||
ParsePositions::isParsedKeywordParameterPosition(s, name) and
|
|
||||||
result.isKeyword(name)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
s = "self" and
|
|
||||||
result.isSelf()
|
|
||||||
or
|
|
||||||
s = "lambda-self" and
|
|
||||||
result.isLambdaSelf()
|
|
||||||
or
|
|
||||||
s = "block" and
|
|
||||||
result.isBlock()
|
|
||||||
or
|
|
||||||
s = "any" and
|
|
||||||
result.isAny()
|
|
||||||
or
|
|
||||||
s = "any-named" and
|
|
||||||
result.isAnyNamed()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
|
|
||||||
ParameterPosition parseArgBody(string s) {
|
|
||||||
exists(int i |
|
|
||||||
ParsePositions::isParsedArgumentPosition(s, i) and
|
|
||||||
result.isPositional(i)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(int i |
|
|
||||||
ParsePositions::isParsedArgumentLowerBoundPosition(s, i) and
|
|
||||||
result.isPositionalLowerBound(i)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(string name |
|
|
||||||
ParsePositions::isParsedKeywordArgumentPosition(s, name) and
|
|
||||||
result.isKeyword(name)
|
|
||||||
)
|
|
||||||
or
|
|
||||||
s = "self" and
|
|
||||||
result.isSelf()
|
|
||||||
or
|
|
||||||
s = "lambda-self" and
|
|
||||||
result.isLambdaSelf()
|
|
||||||
or
|
|
||||||
s = "block" and
|
|
||||||
result.isBlock()
|
|
||||||
or
|
|
||||||
s = "any" and
|
|
||||||
result.isAny()
|
|
||||||
or
|
|
||||||
s = "any-named" and
|
|
||||||
result.isAnyNamed()
|
|
||||||
}
|
|
|
@ -541,7 +541,7 @@ private module ParamsSummaries {
|
||||||
result = paramsInstance().getAMethodCall(methodReturnsTaintFromSelf()).asExpr().getExpr()
|
result = paramsInstance().getAMethodCall(methodReturnsTaintFromSelf()).asExpr().getExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -564,7 +564,7 @@ private module ParamsSummaries {
|
||||||
[result.getReceiver(), result.getArgument(0)]
|
[result.getReceiver(), result.getArgument(0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = ["Argument[self]", "Argument[0]"] and
|
input = ["Argument[self]", "Argument[0]"] and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -588,7 +588,7 @@ private module ParamsSummaries {
|
||||||
[result.getReceiver(), result.getArgument(0)]
|
[result.getReceiver(), result.getArgument(0)]
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = ["Argument[self]", "Argument[0]"] and
|
input = ["Argument[self]", "Argument[0]"] and
|
||||||
output = ["ReturnValue", "Argument[self]"] and
|
output = ["ReturnValue", "Argument[self]"] and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
|
|
@ -61,7 +61,7 @@ module ActiveSupport {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[self]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ module ActiveSupport {
|
||||||
private class IdentitySummary extends SimpleSummarizedCallable {
|
private class IdentitySummary extends SimpleSummarizedCallable {
|
||||||
IdentitySummary() { this = ["presence", "deep_dup"] }
|
IdentitySummary() { this = ["presence", "deep_dup"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -109,7 +109,7 @@ module ActiveSupport {
|
||||||
private class ToJsonSummary extends SimpleSummarizedCallable {
|
private class ToJsonSummary extends SimpleSummarizedCallable {
|
||||||
ToJsonSummary() { this = "to_json" }
|
ToJsonSummary() { this = "to_json" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = ["Argument[self]", "Argument[self].Element[any]"] and
|
input = ["Argument[self]", "Argument[self].Element[any]"] and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -124,7 +124,7 @@ module ActiveSupport {
|
||||||
private class WithIndifferentAccessSummary extends SimpleSummarizedCallable {
|
private class WithIndifferentAccessSummary extends SimpleSummarizedCallable {
|
||||||
WithIndifferentAccessSummary() { this = "with_indifferent_access" }
|
WithIndifferentAccessSummary() { this = "with_indifferent_access" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "ReturnValue.Element[any]" and
|
output = "ReturnValue.Element[any]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -137,7 +137,7 @@ module ActiveSupport {
|
||||||
private class ReverseMergeSummary extends SimpleSummarizedCallable {
|
private class ReverseMergeSummary extends SimpleSummarizedCallable {
|
||||||
ReverseMergeSummary() { this = ["reverse_merge", "with_defaults"] }
|
ReverseMergeSummary() { this = ["reverse_merge", "with_defaults"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self,0].WithElement[any]" and
|
input = "Argument[self,0].WithElement[any]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -150,7 +150,7 @@ module ActiveSupport {
|
||||||
private class ReverseMergeBangSummary extends SimpleSummarizedCallable {
|
private class ReverseMergeBangSummary extends SimpleSummarizedCallable {
|
||||||
ReverseMergeBangSummary() { this = ["reverse_merge!", "with_defaults!", "reverse_update"] }
|
ReverseMergeBangSummary() { this = ["reverse_merge!", "with_defaults!", "reverse_update"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self,0].WithElement[any]" and
|
input = "Argument[self,0].WithElement[any]" and
|
||||||
output = ["ReturnValue", "Argument[self]"] and
|
output = ["ReturnValue", "Argument[self]"] and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -166,7 +166,7 @@ module ActiveSupport {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -209,7 +209,7 @@ module ActiveSupport {
|
||||||
|
|
||||||
final override MethodCall getACall() { result = mc }
|
final override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
exists(string s | s = getExtractComponent(mc, _) |
|
exists(string s | s = getExtractComponent(mc, _) |
|
||||||
input = "Argument[self].Element[" + s + "!]" and
|
input = "Argument[self].Element[" + s + "!]" and
|
||||||
|
@ -244,7 +244,7 @@ module ActiveSupport {
|
||||||
private class CompactBlankSummary extends SimpleSummarizedCallable {
|
private class CompactBlankSummary extends SimpleSummarizedCallable {
|
||||||
CompactBlankSummary() { this = "compact_blank" }
|
CompactBlankSummary() { this = "compact_blank" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -254,7 +254,7 @@ module ActiveSupport {
|
||||||
private class ExcludingSummary extends SimpleSummarizedCallable {
|
private class ExcludingSummary extends SimpleSummarizedCallable {
|
||||||
ExcludingSummary() { this = ["excluding", "without"] }
|
ExcludingSummary() { this = ["excluding", "without"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -264,7 +264,7 @@ module ActiveSupport {
|
||||||
private class InOrderOfSummary extends SimpleSummarizedCallable {
|
private class InOrderOfSummary extends SimpleSummarizedCallable {
|
||||||
InOrderOfSummary() { this = "in_order_of" }
|
InOrderOfSummary() { this = "in_order_of" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -277,7 +277,7 @@ module ActiveSupport {
|
||||||
private class IncludingSummary extends SimpleSummarizedCallable {
|
private class IncludingSummary extends SimpleSummarizedCallable {
|
||||||
IncludingSummary() { this = "including" }
|
IncludingSummary() { this = "including" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
exists(ArrayIndex i |
|
exists(ArrayIndex i |
|
||||||
input = "Argument[self].Element[" + i + "]" and
|
input = "Argument[self].Element[" + i + "]" and
|
||||||
|
@ -299,7 +299,7 @@ module ActiveSupport {
|
||||||
private class IndexBySummary extends SimpleSummarizedCallable {
|
private class IndexBySummary extends SimpleSummarizedCallable {
|
||||||
IndexBySummary() { this = "index_by" }
|
IndexBySummary() { this = "index_by" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = ["Argument[block].Parameter[0]", "ReturnValue.Element[?]"] and
|
output = ["Argument[block].Parameter[0]", "ReturnValue.Element[?]"] and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -309,7 +309,7 @@ module ActiveSupport {
|
||||||
private class IndexWithSummary extends SimpleSummarizedCallable {
|
private class IndexWithSummary extends SimpleSummarizedCallable {
|
||||||
IndexWithSummary() { this = "index_with" }
|
IndexWithSummary() { this = "index_with" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "Argument[block].Parameter[0]" and
|
output = "Argument[block].Parameter[0]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -338,7 +338,7 @@ module ActiveSupport {
|
||||||
|
|
||||||
override MethodCall getACall() { result = mc }
|
override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[0].Element[" + key + "]" and
|
input = "Argument[self].Element[0].Element[" + key + "]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -369,7 +369,7 @@ module ActiveSupport {
|
||||||
|
|
||||||
override MethodCall getACall() { result = mc }
|
override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(string s, int i |
|
exists(string s, int i |
|
||||||
s = getKeyArgument(mc, i) and
|
s = getKeyArgument(mc, i) and
|
||||||
input = "Argument[self].Element[0].Element[" + s + "]" and
|
input = "Argument[self].Element[0].Element[" + s + "]" and
|
||||||
|
@ -392,7 +392,7 @@ module ActiveSupport {
|
||||||
|
|
||||||
override MethodCall getACall() { result = mc }
|
override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any].Element[" + key + "]" and
|
input = "Argument[self].Element[any].Element[" + key + "]" and
|
||||||
output = "ReturnValue.Element[any]" and
|
output = "ReturnValue.Element[any]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -423,7 +423,7 @@ module ActiveSupport {
|
||||||
|
|
||||||
override MethodCall getACall() { result = mc }
|
override MethodCall getACall() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(string s, int i |
|
exists(string s, int i |
|
||||||
s = getKeyArgument(mc, i) and
|
s = getKeyArgument(mc, i) and
|
||||||
input = "Argument[self].Element[any].Element[" + s + "]" and
|
input = "Argument[self].Element[any].Element[" + s + "]" and
|
||||||
|
@ -436,7 +436,7 @@ module ActiveSupport {
|
||||||
private class SoleSummary extends SimpleSummarizedCallable {
|
private class SoleSummary extends SimpleSummarizedCallable {
|
||||||
SoleSummary() { this = "sole" }
|
SoleSummary() { this = "sole" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[0]" and
|
input = "Argument[self].Element[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -470,7 +470,7 @@ module ActiveSupport {
|
||||||
private class JsonEscapeSummary extends SimpleSummarizedCallable {
|
private class JsonEscapeSummary extends SimpleSummarizedCallable {
|
||||||
JsonEscapeSummary() { this = "json_escape" }
|
JsonEscapeSummary() { this = "json_escape" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
|
|
@ -25,7 +25,7 @@ module Arel {
|
||||||
result = API::getTopLevelMember("Arel").getAMethodCall("sql").asExpr().getExpr()
|
result = API::getTopLevelMember("Arel").getAMethodCall("sql").asExpr().getExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ private class SplatSummary extends SummarizedCallable {
|
||||||
|
|
||||||
override SplatExpr getACallSimple() { any() }
|
override SplatExpr getACallSimple() { any() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
// *1 = [1]
|
// *1 = [1]
|
||||||
input = "Argument[self].WithoutElement[any]" and
|
input = "Argument[self].WithoutElement[any]" and
|
||||||
|
@ -82,7 +82,7 @@ private class HashSplatSummary extends SummarizedCallable {
|
||||||
|
|
||||||
override HashSplatExpr getACallSimple() { any() }
|
override HashSplatExpr getACallSimple() { any() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].WithElement[any]" and
|
input = "Argument[self].WithElement[any]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -18,7 +18,7 @@ module Erb {
|
||||||
|
|
||||||
override MethodCall getACall() { result = any(ErbTemplateNewCall c).asExpr().getExpr() }
|
override MethodCall getACall() { result = any(ErbTemplateNewCall c).asExpr().getExpr() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,7 +115,7 @@ module File {
|
||||||
result = API::getTopLevelMember("File").getAMethodCall(methodName).asExpr().getExpr()
|
result = API::getTopLevelMember("File").getAMethodCall(methodName).asExpr().getExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -133,7 +133,7 @@ module File {
|
||||||
result = API::getTopLevelMember("File").getAMethodCall("join").asExpr().getExpr()
|
result = API::getTopLevelMember("File").getAMethodCall("join").asExpr().getExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0,1..]" and
|
input = "Argument[0,1..]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
|
|
@ -19,7 +19,7 @@ module NetLdap {
|
||||||
|
|
||||||
override MethodCall getACall() { result = any(NetLdapConnection l).asExpr().getExpr() }
|
override MethodCall getACall() { result = any(NetLdapConnection l).asExpr().getExpr() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ module NetLdap {
|
||||||
|
|
||||||
override MethodCall getACall() { result = any(NetLdapFilter l).asExpr().getExpr() }
|
override MethodCall getACall() { result = any(NetLdapFilter l).asExpr().getExpr() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = ["Argument[0]", "Argument[1]"] and output = "ReturnValue" and preservesValue = false
|
input = ["Argument[0]", "Argument[1]"] and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ module Mysql2 {
|
||||||
|
|
||||||
override MethodCall getACall() { result = any(Mysql2Connection c).asExpr().getExpr() }
|
override MethodCall getACall() { result = any(Mysql2Connection c).asExpr().getExpr() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ module Mysql2 {
|
||||||
|
|
||||||
override MethodCall getACall() { result = any(Mysql2EscapeSanitization c).asExpr().getExpr() }
|
override MethodCall getACall() { result = any(Mysql2EscapeSanitization c).asExpr().getExpr() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ module Pg {
|
||||||
|
|
||||||
override MethodCall getACall() { result = any(PgConnection c).asExpr().getExpr() }
|
override MethodCall getACall() { result = any(PgConnection c).asExpr().getExpr() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -314,7 +314,7 @@ private predicate isPotentialRenderCall(MethodCall renderCall, Location loc, Erb
|
||||||
// TODO: initialization hooks, e.g. before_configuration, after_initialize...
|
// TODO: initialization hooks, e.g. before_configuration, after_initialize...
|
||||||
// TODO: initializers
|
// TODO: initializers
|
||||||
/** A synthetic global to represent the value passed to the `locals` argument of a render call for a specific ERB file. */
|
/** A synthetic global to represent the value passed to the `locals` argument of a render call for a specific ERB file. */
|
||||||
private class LocalAssignsHashSyntheticGlobal extends SummaryComponent::SyntheticGlobal {
|
private class LocalAssignsHashSyntheticGlobal extends string {
|
||||||
private ErbFile erbFile;
|
private ErbFile erbFile;
|
||||||
private string id;
|
private string id;
|
||||||
// Note that we can't use an actual `Rails::RenderCall` here due to problems with non-monotonic recursion
|
// Note that we can't use an actual `Rails::RenderCall` here due to problems with non-monotonic recursion
|
||||||
|
@ -346,7 +346,7 @@ private class RenderLocalsSummary extends SummarizedCallable {
|
||||||
|
|
||||||
override Rails::RenderCall getACall() { result = glob.getARenderCall() }
|
override Rails::RenderCall getACall() { result = glob.getARenderCall() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[locals:]" and
|
input = "Argument[locals:]" and
|
||||||
output = "SyntheticGlobal[" + glob + "]" and
|
output = "SyntheticGlobal[" + glob + "]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -364,7 +364,7 @@ private class AccessLocalsSummary extends SummarizedCallable {
|
||||||
result.getMethodName() = "local_assigns"
|
result.getMethodName() = "local_assigns"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "SyntheticGlobal[" + glob + "]" and
|
input = "SyntheticGlobal[" + glob + "]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -394,7 +394,7 @@ private class AccessLocalsKeySummary extends SummarizedCallable {
|
||||||
result.getReceiver() instanceof SelfVariableReadAccess
|
result.getReceiver() instanceof SelfVariableReadAccess
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "SyntheticGlobal[" + glob + "].Element[:" + methodName + "]" and
|
input = "SyntheticGlobal[" + glob + "].Element[:" + methodName + "]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -19,7 +19,7 @@ module Sequel {
|
||||||
|
|
||||||
override MethodCall getACall() { result = any(SequelConnection c).asExpr().getExpr() }
|
override MethodCall getACall() { result = any(SequelConnection c).asExpr().getExpr() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,7 @@ module Sinatra {
|
||||||
/**
|
/**
|
||||||
* A synthetic global representing the hash of local variables passed to an ERB template.
|
* A synthetic global representing the hash of local variables passed to an ERB template.
|
||||||
*/
|
*/
|
||||||
class ErbLocalsHashSyntheticGlobal extends SummaryComponent::SyntheticGlobal {
|
class ErbLocalsHashSyntheticGlobal extends string {
|
||||||
private string id;
|
private string id;
|
||||||
private MethodCall erbCall;
|
private MethodCall erbCall;
|
||||||
private ErbFile erbFile;
|
private ErbFile erbFile;
|
||||||
|
@ -172,7 +172,7 @@ module Sinatra {
|
||||||
|
|
||||||
override MethodCall getACall() { result = any(ErbCall c).asExpr().getExpr() }
|
override MethodCall getACall() { result = any(ErbCall c).asExpr().getExpr() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[locals:]" and
|
input = "Argument[locals:]" and
|
||||||
output = "SyntheticGlobal[" + any(ErbLocalsHashSyntheticGlobal global) + "]" and
|
output = "SyntheticGlobal[" + any(ErbLocalsHashSyntheticGlobal global) + "]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -207,7 +207,7 @@ module Sinatra {
|
||||||
result.getReceiver() instanceof SelfVariableReadAccess
|
result.getReceiver() instanceof SelfVariableReadAccess
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "SyntheticGlobal[" + global + "].Element[:" + local + "]" and
|
input = "SyntheticGlobal[" + global + "].Element[:" + local + "]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -94,7 +94,7 @@ module Sqlite3 {
|
||||||
|
|
||||||
override MethodCall getACall() { result = any(SQLite3QuoteSanitization c).asExpr().getExpr() }
|
override MethodCall getACall() { result = any(SQLite3QuoteSanitization c).asExpr().getExpr() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -17,7 +17,7 @@ private class Base64Decode extends SummarizedCallable {
|
||||||
.getExpr()
|
.getExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
|
|
@ -31,7 +31,7 @@ module Hash {
|
||||||
|
|
||||||
final override MethodCall getACallSimple() { result = getAStaticHashCall("[]") }
|
final override MethodCall getACallSimple() { result = getAStaticHashCall("[]") }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// we make use of the special `hash-splat` argument kind, which contains all keyword
|
// we make use of the special `hash-splat` argument kind, which contains all keyword
|
||||||
// arguments wrapped in an implicit hash, as well as explicit hash splat arguments
|
// arguments wrapped in an implicit hash, as well as explicit hash splat arguments
|
||||||
input = "Argument[hash-splat]" and
|
input = "Argument[hash-splat]" and
|
||||||
|
@ -62,7 +62,7 @@ module Hash {
|
||||||
result.getNumberOfArguments() = 1
|
result.getNumberOfArguments() = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
// Hash[{symbol: x}]
|
// Hash[{symbol: x}]
|
||||||
input = "Argument[0].WithElement[any]" and
|
input = "Argument[0].WithElement[any]" and
|
||||||
|
@ -102,7 +102,7 @@ module Hash {
|
||||||
exists(result.getArgument(i))
|
exists(result.getArgument(i))
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// Hash[:symbol, x]
|
// Hash[:symbol, x]
|
||||||
input = "Argument[" + i + "]" and
|
input = "Argument[" + i + "]" and
|
||||||
output = "ReturnValue.Element[" + key.serialize() + "]" and
|
output = "ReturnValue.Element[" + key.serialize() + "]" and
|
||||||
|
@ -115,7 +115,7 @@ module Hash {
|
||||||
|
|
||||||
override MethodCall getACallSimple() { result = getAStaticHashCall("try_convert") }
|
override MethodCall getACallSimple() { result = getAStaticHashCall("try_convert") }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0].WithElement[any]" and
|
input = "Argument[0].WithElement[any]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -130,7 +130,7 @@ module Hash {
|
||||||
|
|
||||||
final override MethodCall getACallSimple() { result = mc }
|
final override MethodCall getACallSimple() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -145,8 +145,8 @@ module Hash {
|
||||||
this = "store(" + key.serialize() + ")"
|
this = "store(" + key.serialize() + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
super.propagatesFlowExt(input, output, preservesValue)
|
super.propagatesFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
output = "Argument[self].Element[" + key.serialize() + "]" and
|
output = "Argument[self].Element[" + key.serialize() + "]" and
|
||||||
|
@ -164,8 +164,8 @@ module Hash {
|
||||||
this = "store"
|
this = "store"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
super.propagatesFlowExt(input, output, preservesValue)
|
super.propagatesFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
output = "Argument[self].Element[?]" and
|
output = "Argument[self].Element[?]" and
|
||||||
|
@ -192,7 +192,7 @@ module Hash {
|
||||||
key = DataFlow::Content::getKnownElementIndex(mc.getArgument(0))
|
key = DataFlow::Content::getKnownElementIndex(mc.getArgument(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[" + key.serialize() + "]" and
|
input = "Argument[self].Element[" + key.serialize() + "]" and
|
||||||
output = "ReturnValue.Element[1]" and
|
output = "ReturnValue.Element[1]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -208,7 +208,7 @@ module Hash {
|
||||||
not exists(DataFlow::Content::getKnownElementIndex(result.getArgument(0)))
|
not exists(DataFlow::Content::getKnownElementIndex(result.getArgument(0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any].WithoutElement[any]" and
|
input = "Argument[self].Element[any].WithoutElement[any]" and
|
||||||
output = "ReturnValue.Element[1]" and
|
output = "ReturnValue.Element[1]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -218,7 +218,7 @@ module Hash {
|
||||||
private class EachPairSummary extends SimpleSummarizedCallable {
|
private class EachPairSummary extends SimpleSummarizedCallable {
|
||||||
EachPairSummary() { this = "each_pair" }
|
EachPairSummary() { this = "each_pair" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "Argument[block].Parameter[1]"
|
output = "Argument[block].Parameter[1]"
|
||||||
|
@ -233,7 +233,7 @@ module Hash {
|
||||||
private class EachValueSummary extends SimpleSummarizedCallable {
|
private class EachValueSummary extends SimpleSummarizedCallable {
|
||||||
EachValueSummary() { this = "each_value" }
|
EachValueSummary() { this = "each_value" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "Argument[block].Parameter[0]"
|
output = "Argument[block].Parameter[0]"
|
||||||
|
@ -264,7 +264,7 @@ module Hash {
|
||||||
|
|
||||||
final override MethodCall getACallSimple() { result = mc }
|
final override MethodCall getACallSimple() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input =
|
input =
|
||||||
"Argument[self]" +
|
"Argument[self]" +
|
||||||
concat(int i, string s |
|
concat(int i, string s |
|
||||||
|
@ -290,7 +290,7 @@ abstract private class FetchValuesSummary extends SummarizedCallable {
|
||||||
|
|
||||||
final override MethodCall getACallSimple() { result = mc }
|
final override MethodCall getACallSimple() { result = mc }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].WithElement[?]" and
|
input = "Argument[self].WithElement[?]" and
|
||||||
output = "ReturnValue"
|
output = "ReturnValue"
|
||||||
|
@ -314,8 +314,8 @@ private class FetchValuesKnownSummary extends FetchValuesSummary {
|
||||||
this = "fetch_values(" + key.serialize() + ")"
|
this = "fetch_values(" + key.serialize() + ")"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
super.propagatesFlowExt(input, output, preservesValue)
|
super.propagatesFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
input = "Argument[self].Element[" + key.serialize() + "]" and
|
input = "Argument[self].Element[" + key.serialize() + "]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
|
@ -329,8 +329,8 @@ private class FetchValuesUnknownSummary extends FetchValuesSummary {
|
||||||
this = "fetch_values(?)"
|
this = "fetch_values(?)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
super.propagatesFlowExt(input, output, preservesValue)
|
super.propagatesFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
|
@ -345,7 +345,7 @@ private class MergeSummary extends SimpleSummarizedCallable {
|
||||||
this = ["merge", "deep_merge"]
|
this = ["merge", "deep_merge"]
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self,any].WithElement[any]" and
|
input = "Argument[self,any].WithElement[any]" and
|
||||||
output = "ReturnValue"
|
output = "ReturnValue"
|
||||||
|
@ -364,7 +364,7 @@ private class MergeBangSummary extends SimpleSummarizedCallable {
|
||||||
this = ["merge!", "deep_merge!", "update"]
|
this = ["merge!", "deep_merge!", "update"]
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self,any].WithElement[any]" and
|
input = "Argument[self,any].WithElement[any]" and
|
||||||
output = ["ReturnValue", "Argument[self]"]
|
output = ["ReturnValue", "Argument[self]"]
|
||||||
|
@ -379,7 +379,7 @@ private class MergeBangSummary extends SimpleSummarizedCallable {
|
||||||
private class RassocSummary extends SimpleSummarizedCallable {
|
private class RassocSummary extends SimpleSummarizedCallable {
|
||||||
RassocSummary() { this = "rassoc" }
|
RassocSummary() { this = "rassoc" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any].WithoutElement[any]" and
|
input = "Argument[self].Element[any].WithoutElement[any]" and
|
||||||
output = "ReturnValue.Element[1]" and
|
output = "ReturnValue.Element[1]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -404,7 +404,7 @@ private class SliceKnownSummary extends SliceSummary {
|
||||||
not key.isInt(_) // covered in `Array.qll`
|
not key.isInt(_) // covered in `Array.qll`
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].WithElement[" + key.serialize() + "]" and
|
input = "Argument[self].WithElement[" + key.serialize() + "]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -417,7 +417,7 @@ private class SliceUnknownSummary extends SliceSummary {
|
||||||
this = "slice(?)"
|
this = "slice(?)"
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].WithoutElement[0..!].WithElement[any]" and
|
input = "Argument[self].WithoutElement[0..!].WithElement[any]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -427,7 +427,7 @@ private class SliceUnknownSummary extends SliceSummary {
|
||||||
private class ToASummary extends SimpleSummarizedCallable {
|
private class ToASummary extends SimpleSummarizedCallable {
|
||||||
ToASummary() { this = "to_a" }
|
ToASummary() { this = "to_a" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].WithoutElement[0..!].Element[any]" and
|
input = "Argument[self].WithoutElement[0..!].Element[any]" and
|
||||||
output = "ReturnValue.Element[?].Element[1]" and
|
output = "ReturnValue.Element[?].Element[1]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -437,7 +437,7 @@ private class ToASummary extends SimpleSummarizedCallable {
|
||||||
private class ToHWithoutBlockSummary extends SimpleSummarizedCallable {
|
private class ToHWithoutBlockSummary extends SimpleSummarizedCallable {
|
||||||
ToHWithoutBlockSummary() { this = ["to_h", "to_hash"] and not exists(mc.getBlock()) }
|
ToHWithoutBlockSummary() { this = ["to_h", "to_hash"] and not exists(mc.getBlock()) }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].WithElement[any]" and
|
input = "Argument[self].WithElement[any]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -447,7 +447,7 @@ private class ToHWithoutBlockSummary extends SimpleSummarizedCallable {
|
||||||
private class ToHWithBlockSummary extends SimpleSummarizedCallable {
|
private class ToHWithBlockSummary extends SimpleSummarizedCallable {
|
||||||
ToHWithBlockSummary() { this = "to_h" and exists(mc.getBlock()) }
|
ToHWithBlockSummary() { this = "to_h" and exists(mc.getBlock()) }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "Argument[block].Parameter[1]"
|
output = "Argument[block].Parameter[1]"
|
||||||
|
@ -462,7 +462,7 @@ private class ToHWithBlockSummary extends SimpleSummarizedCallable {
|
||||||
private class TransformKeysSummary extends SimpleSummarizedCallable {
|
private class TransformKeysSummary extends SimpleSummarizedCallable {
|
||||||
TransformKeysSummary() { this = "transform_keys" }
|
TransformKeysSummary() { this = "transform_keys" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -472,7 +472,7 @@ private class TransformKeysSummary extends SimpleSummarizedCallable {
|
||||||
private class TransformKeysBangSummary extends SimpleSummarizedCallable {
|
private class TransformKeysBangSummary extends SimpleSummarizedCallable {
|
||||||
TransformKeysBangSummary() { this = "transform_keys!" }
|
TransformKeysBangSummary() { this = "transform_keys!" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "Argument[self].Element[?]"
|
output = "Argument[self].Element[?]"
|
||||||
|
@ -484,7 +484,7 @@ private class TransformKeysBangSummary extends SimpleSummarizedCallable {
|
||||||
private class TransformValuesSummary extends SimpleSummarizedCallable {
|
private class TransformValuesSummary extends SimpleSummarizedCallable {
|
||||||
TransformValuesSummary() { this = "transform_values" }
|
TransformValuesSummary() { this = "transform_values" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "Argument[block].Parameter[0]"
|
output = "Argument[block].Parameter[0]"
|
||||||
|
@ -499,7 +499,7 @@ private class TransformValuesSummary extends SimpleSummarizedCallable {
|
||||||
private class TransformValuesBangSummary extends SimpleSummarizedCallable {
|
private class TransformValuesBangSummary extends SimpleSummarizedCallable {
|
||||||
TransformValuesBangSummary() { this = "transform_values!" }
|
TransformValuesBangSummary() { this = "transform_values!" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "Argument[block].Parameter[0]"
|
output = "Argument[block].Parameter[0]"
|
||||||
|
@ -517,7 +517,7 @@ private class TransformValuesBangSummary extends SimpleSummarizedCallable {
|
||||||
private class ValuesSummary extends SimpleSummarizedCallable {
|
private class ValuesSummary extends SimpleSummarizedCallable {
|
||||||
ValuesSummary() { this = "values" }
|
ValuesSummary() { this = "values" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self].Element[any]" and
|
input = "Argument[self].Element[any]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -177,7 +177,7 @@ module Kernel {
|
||||||
private class TapSummary extends SimpleSummarizedCallable {
|
private class TapSummary extends SimpleSummarizedCallable {
|
||||||
TapSummary() { this = "tap" }
|
TapSummary() { this = "tap" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = ["ReturnValue", "Argument[block].Parameter[0]"] and
|
output = ["ReturnValue", "Argument[block].Parameter[0]"] and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -219,7 +219,7 @@ module Kernel {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
(
|
(
|
||||||
// already an array
|
// already an array
|
||||||
input = "Argument[0].WithElement[0..]" and
|
input = "Argument[0].WithElement[0..]" and
|
||||||
|
|
|
@ -36,7 +36,7 @@ module Object {
|
||||||
private class DupSummary extends SimpleSummarizedCallable {
|
private class DupSummary extends SimpleSummarizedCallable {
|
||||||
DupSummary() { this = "dup" }
|
DupSummary() { this = "dup" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -127,7 +127,7 @@ module String {
|
||||||
result = API::getTopLevelMember("String").getAnInstantiation().getExprNode().getExpr()
|
result = API::getTopLevelMember("String").getAnInstantiation().getExprNode().getExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -142,7 +142,7 @@ module String {
|
||||||
API::getTopLevelMember("String").getAMethodCall("try_convert").getExprNode().getExpr()
|
API::getTopLevelMember("String").getAMethodCall("try_convert").getExprNode().getExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -155,7 +155,7 @@ module String {
|
||||||
private class FormatSummary extends SimpleSummarizedCallable {
|
private class FormatSummary extends SimpleSummarizedCallable {
|
||||||
FormatSummary() { this = "%" }
|
FormatSummary() { this = "%" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = ["Argument[self]", "Argument[0]", "Argument[0].Element[any]"] and
|
input = ["Argument[self]", "Argument[0]", "Argument[0].Element[any]"] and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -169,7 +169,7 @@ module String {
|
||||||
private class BSummary extends SimpleSummarizedCallable {
|
private class BSummary extends SimpleSummarizedCallable {
|
||||||
BSummary() { this = "b" }
|
BSummary() { this = "b" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ module String {
|
||||||
private class BytesliceSummary extends SimpleSummarizedCallable {
|
private class BytesliceSummary extends SimpleSummarizedCallable {
|
||||||
BytesliceSummary() { this = "byteslice" }
|
BytesliceSummary() { this = "byteslice" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ module String {
|
||||||
private class CapitalizeSummary extends SimpleSummarizedCallable {
|
private class CapitalizeSummary extends SimpleSummarizedCallable {
|
||||||
CapitalizeSummary() { this = ["capitalize", "capitalize!"] }
|
CapitalizeSummary() { this = ["capitalize", "capitalize!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
output = "ReturnValue"
|
output = "ReturnValue"
|
||||||
|
@ -204,7 +204,7 @@ module String {
|
||||||
private class CenterSummary extends SimpleSummarizedCallable {
|
private class CenterSummary extends SimpleSummarizedCallable {
|
||||||
CenterSummary() { this = ["center", "ljust", "rjust"] }
|
CenterSummary() { this = ["center", "ljust", "rjust"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
|
@ -219,7 +219,7 @@ module String {
|
||||||
private class ChompSummary extends SimpleSummarizedCallable {
|
private class ChompSummary extends SimpleSummarizedCallable {
|
||||||
ChompSummary() { this = ["chomp", "chomp!", "chop", "chop!"] }
|
ChompSummary() { this = ["chomp", "chomp!", "chop", "chop!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
this = ["chomp!", "chop!"] and
|
this = ["chomp!", "chop!"] and
|
||||||
|
@ -236,6 +236,10 @@ module String {
|
||||||
*/
|
*/
|
||||||
private class ClearSummary extends SimpleSummarizedCallable {
|
private class ClearSummary extends SimpleSummarizedCallable {
|
||||||
ClearSummary() { none() }
|
ClearSummary() { none() }
|
||||||
|
|
||||||
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -249,7 +253,7 @@ module String {
|
||||||
none()
|
none()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self,0..]" and
|
input = "Argument[self,0..]" and
|
||||||
output = ["ReturnValue", "Argument[self]"] and
|
output = ["ReturnValue", "Argument[self]"] and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -262,7 +266,7 @@ module String {
|
||||||
private class DeleteSummary extends SimpleSummarizedCallable {
|
private class DeleteSummary extends SimpleSummarizedCallable {
|
||||||
DeleteSummary() { this = ["delete", "delete_prefix", "delete_suffix"] + ["", "!"] }
|
DeleteSummary() { this = ["delete", "delete_prefix", "delete_suffix"] + ["", "!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,7 +277,7 @@ module String {
|
||||||
private class DowncaseSummary extends SimpleSummarizedCallable {
|
private class DowncaseSummary extends SimpleSummarizedCallable {
|
||||||
DowncaseSummary() { this = ["downcase", "upcase", "swapcase"] + ["", "!"] }
|
DowncaseSummary() { this = ["downcase", "upcase", "swapcase"] + ["", "!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,7 +288,7 @@ module String {
|
||||||
private class DumpSummary extends SimpleSummarizedCallable {
|
private class DumpSummary extends SimpleSummarizedCallable {
|
||||||
DumpSummary() { this = ["dump", "undump"] }
|
DumpSummary() { this = ["dump", "undump"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,7 +312,7 @@ module String {
|
||||||
private class EachLineBlockSummary extends EachLineSummary {
|
private class EachLineBlockSummary extends EachLineSummary {
|
||||||
EachLineBlockSummary() { this = "each_line_with_block" and exists(mc.getBlock()) }
|
EachLineBlockSummary() { this = "each_line_with_block" and exists(mc.getBlock()) }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = ["Argument[block].Parameter[0]", "ReturnValue"]
|
output = ["Argument[block].Parameter[0]", "ReturnValue"]
|
||||||
|
@ -321,7 +325,7 @@ module String {
|
||||||
private class EachLineNoBlockSummary extends EachLineSummary {
|
private class EachLineNoBlockSummary extends EachLineSummary {
|
||||||
EachLineNoBlockSummary() { this = "each_line_without_block" and not exists(mc.getBlock()) }
|
EachLineNoBlockSummary() { this = "each_line_without_block" and not exists(mc.getBlock()) }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue.Element[?]"
|
output = "ReturnValue.Element[?]"
|
||||||
|
@ -334,7 +338,7 @@ module String {
|
||||||
private class EncodeSummary extends SimpleSummarizedCallable {
|
private class EncodeSummary extends SimpleSummarizedCallable {
|
||||||
EncodeSummary() { this = ["encode", "unicode_normalize"] + ["", "!"] }
|
EncodeSummary() { this = ["encode", "unicode_normalize"] + ["", "!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,7 +349,7 @@ module String {
|
||||||
private class ForceEncodingSummary extends SimpleSummarizedCallable {
|
private class ForceEncodingSummary extends SimpleSummarizedCallable {
|
||||||
ForceEncodingSummary() { this = "force_encoding" }
|
ForceEncodingSummary() { this = "force_encoding" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,7 +360,7 @@ module String {
|
||||||
private class FreezeSummary extends SimpleSummarizedCallable {
|
private class FreezeSummary extends SimpleSummarizedCallable {
|
||||||
FreezeSummary() { this = "freeze" }
|
FreezeSummary() { this = "freeze" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -370,7 +374,7 @@ module String {
|
||||||
// str.gsub(pattern, replacement) -> new_str
|
// str.gsub(pattern, replacement) -> new_str
|
||||||
// str.gsub(pattern) {|match| block } -> new_str
|
// str.gsub(pattern) {|match| block } -> new_str
|
||||||
// str.gsub(pattern) -> enumerator of matches
|
// str.gsub(pattern) -> enumerator of matches
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// receiver -> return value
|
// receiver -> return value
|
||||||
// replacement -> return value
|
// replacement -> return value
|
||||||
// block return -> return value
|
// block return -> return value
|
||||||
|
@ -390,7 +394,7 @@ module String {
|
||||||
none()
|
none()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
input = "Argument[1]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[1]" and output = "ReturnValue" and preservesValue = false
|
||||||
|
@ -403,7 +407,7 @@ module String {
|
||||||
private class InspectSummary extends SimpleSummarizedCallable {
|
private class InspectSummary extends SimpleSummarizedCallable {
|
||||||
InspectSummary() { this = "inspect" }
|
InspectSummary() { this = "inspect" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -414,7 +418,7 @@ module String {
|
||||||
private class StripSummary extends SimpleSummarizedCallable {
|
private class StripSummary extends SimpleSummarizedCallable {
|
||||||
StripSummary() { this = ["strip", "lstrip", "rstrip"] + ["", "!"] }
|
StripSummary() { this = ["strip", "lstrip", "rstrip"] + ["", "!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,7 +429,7 @@ module String {
|
||||||
private class NextSummary extends SimpleSummarizedCallable {
|
private class NextSummary extends SimpleSummarizedCallable {
|
||||||
NextSummary() { this = ["next", "succ"] + ["", "!"] }
|
NextSummary() { this = ["next", "succ"] + ["", "!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -436,7 +440,7 @@ module String {
|
||||||
private class PartitionSummary extends SimpleSummarizedCallable {
|
private class PartitionSummary extends SimpleSummarizedCallable {
|
||||||
PartitionSummary() { this = ["partition", "rpartition"] }
|
PartitionSummary() { this = ["partition", "rpartition"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue.Element[0,1,2]" and
|
output = "ReturnValue.Element[0,1,2]" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -449,7 +453,7 @@ module String {
|
||||||
private class ReplaceSummary extends SimpleSummarizedCallable {
|
private class ReplaceSummary extends SimpleSummarizedCallable {
|
||||||
ReplaceSummary() { this = "replace" }
|
ReplaceSummary() { this = "replace" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = ["ReturnValue", "Argument[self]"] and
|
output = ["ReturnValue", "Argument[self]"] and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -463,7 +467,7 @@ module String {
|
||||||
private class ReverseSummary extends SimpleSummarizedCallable {
|
private class ReverseSummary extends SimpleSummarizedCallable {
|
||||||
ReverseSummary() { this = ["reverse", "reverse!"] }
|
ReverseSummary() { this = ["reverse", "reverse!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,7 +487,7 @@ module String {
|
||||||
private class ScanBlockSummary extends ScanSummary {
|
private class ScanBlockSummary extends ScanSummary {
|
||||||
ScanBlockSummary() { this = "scan_with_block" and exists(mc.getBlock()) }
|
ScanBlockSummary() { this = "scan_with_block" and exists(mc.getBlock()) }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
output =
|
output =
|
||||||
|
@ -500,7 +504,7 @@ module String {
|
||||||
private class ScanNoBlockSummary extends ScanSummary {
|
private class ScanNoBlockSummary extends ScanSummary {
|
||||||
ScanNoBlockSummary() { this = "scan_no_block" and not exists(mc.getBlock()) }
|
ScanNoBlockSummary() { this = "scan_no_block" and not exists(mc.getBlock()) }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
// scan(pattern) -> array
|
// scan(pattern) -> array
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
|
@ -523,7 +527,7 @@ module String {
|
||||||
private class ScrubBlockSummary extends ScrubSummary {
|
private class ScrubBlockSummary extends ScrubSummary {
|
||||||
ScrubBlockSummary() { this = "scrub_block" and exists(mc.getBlock()) }
|
ScrubBlockSummary() { this = "scrub_block" and exists(mc.getBlock()) }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
|
@ -542,7 +546,7 @@ module String {
|
||||||
private class ScrubNoBlockSummary extends ScrubSummary {
|
private class ScrubNoBlockSummary extends ScrubSummary {
|
||||||
ScrubNoBlockSummary() { this = "scrub_no_block" and not exists(mc.getBlock()) }
|
ScrubNoBlockSummary() { this = "scrub_no_block" and not exists(mc.getBlock()) }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
preservesValue = false and
|
preservesValue = false and
|
||||||
|
@ -557,7 +561,7 @@ module String {
|
||||||
private class ShellescapeSummary extends SimpleSummarizedCallable {
|
private class ShellescapeSummary extends SimpleSummarizedCallable {
|
||||||
ShellescapeSummary() { this = "shellescape" }
|
ShellescapeSummary() { this = "shellescape" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -568,7 +572,7 @@ module String {
|
||||||
private class ShellSplitSummary extends SimpleSummarizedCallable {
|
private class ShellSplitSummary extends SimpleSummarizedCallable {
|
||||||
ShellSplitSummary() { this = "shellsplit" }
|
ShellSplitSummary() { this = "shellsplit" }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "ReturnValue.Element[?]" and
|
output = "ReturnValue.Element[?]" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
@ -581,7 +585,7 @@ module String {
|
||||||
private class SliceSummary extends SimpleSummarizedCallable {
|
private class SliceSummary extends SimpleSummarizedCallable {
|
||||||
SliceSummary() { this = ["slice", "slice!", "split", "[]"] }
|
SliceSummary() { this = ["slice", "slice!", "split", "[]"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -592,7 +596,7 @@ module String {
|
||||||
private class SqueezeSummary extends SimpleSummarizedCallable {
|
private class SqueezeSummary extends SimpleSummarizedCallable {
|
||||||
SqueezeSummary() { this = ["squeeze", "squeeze!"] }
|
SqueezeSummary() { this = ["squeeze", "squeeze!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -603,7 +607,7 @@ module String {
|
||||||
private class ToStrSummary extends SimpleSummarizedCallable {
|
private class ToStrSummary extends SimpleSummarizedCallable {
|
||||||
ToStrSummary() { this = ["to_str", "to_s"] }
|
ToStrSummary() { this = ["to_str", "to_s"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -614,7 +618,7 @@ module String {
|
||||||
private class TrSummary extends SimpleSummarizedCallable {
|
private class TrSummary extends SimpleSummarizedCallable {
|
||||||
TrSummary() { this = ["tr", "tr_s"] + ["", "!"] }
|
TrSummary() { this = ["tr", "tr_s"] + ["", "!"] }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
input = "Argument[1]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[1]" and output = "ReturnValue" and preservesValue = false
|
||||||
|
@ -646,7 +650,7 @@ module String {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: if second arg ('exclusive') is true, the first arg is excluded
|
// TODO: if second arg ('exclusive') is true, the first arg is excluded
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
taintIdentityFlow(input, output, preservesValue)
|
taintIdentityFlow(input, output, preservesValue)
|
||||||
or
|
or
|
||||||
input = ["Argument[self]", "Argument[0]"] and
|
input = ["Argument[self]", "Argument[0]"] and
|
||||||
|
@ -668,7 +672,7 @@ module String {
|
||||||
mc.getArgument(1).getConstantValue().isBoolean(true)
|
mc.getArgument(1).getConstantValue().isBoolean(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[self]" and
|
input = "Argument[self]" and
|
||||||
output = "Argument[block].Parameter[0]" and
|
output = "Argument[block].Parameter[0]" and
|
||||||
preservesValue = false
|
preservesValue = false
|
||||||
|
|
|
@ -48,7 +48,7 @@ private class SummarizedCallableFromModel extends SummarizedCallable {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
exists(string kind | ModelOutput::relevantSummaryModel(type, path, input, output, kind) |
|
exists(string kind | ModelOutput::relevantSummaryModel(type, path, input, output, kind) |
|
||||||
kind = "value" and
|
kind = "value" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -70,8 +70,8 @@ private module API = Specific::API;
|
||||||
|
|
||||||
private module DataFlow = Specific::DataFlow;
|
private module DataFlow = Specific::DataFlow;
|
||||||
|
|
||||||
private import Specific::AccessPathSyntax
|
|
||||||
private import ApiGraphModelsExtensions as Extensions
|
private import ApiGraphModelsExtensions as Extensions
|
||||||
|
private import codeql.dataflow.internal.AccessPathSyntax
|
||||||
|
|
||||||
/** Module containing hooks for providing input data to be interpreted as a model. */
|
/** Module containing hooks for providing input data to be interpreted as a model. */
|
||||||
module ModelInput {
|
module ModelInput {
|
||||||
|
@ -327,29 +327,29 @@ predicate isRelevantFullPath(string type, string path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A string from a CSV row that should be parsed as an access path. */
|
/** A string from a CSV row that should be parsed as an access path. */
|
||||||
private class AccessPathRange extends AccessPath::Range {
|
private predicate accessPathRange(string s) {
|
||||||
AccessPathRange() {
|
isRelevantFullPath(_, s)
|
||||||
isRelevantFullPath(_, this)
|
or
|
||||||
or
|
exists(string type | isRelevantType(type) |
|
||||||
exists(string type | isRelevantType(type) |
|
summaryModel(type, _, s, _, _) or
|
||||||
summaryModel(type, _, this, _, _) or
|
summaryModel(type, _, _, s, _)
|
||||||
summaryModel(type, _, _, this, _)
|
)
|
||||||
)
|
or
|
||||||
or
|
typeVariableModel(_, s)
|
||||||
typeVariableModel(_, this)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import AccessPath<accessPathRange/1>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a successor of `node` in the API graph.
|
* Gets a successor of `node` in the API graph.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
|
API::Node getSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
|
||||||
// API graphs use the same label for arguments and parameters. An edge originating from a
|
// API graphs use the same label for arguments and parameters. An edge originating from a
|
||||||
// use-node represents an argument, and an edge originating from a def-node represents a parameter.
|
// use-node represents an argument, and an edge originating from a def-node represents a parameter.
|
||||||
// We just map both to the same thing.
|
// We just map both to the same thing.
|
||||||
token.getName() = ["Argument", "Parameter"] and
|
token.getName() = ["Argument", "Parameter"] and
|
||||||
result = node.getParameter(AccessPath::parseIntUnbounded(token.getAnArgument()))
|
result = node.getParameter(parseIntUnbounded(token.getAnArgument()))
|
||||||
or
|
or
|
||||||
token.getName() = "ReturnValue" and
|
token.getName() = "ReturnValue" and
|
||||||
result = node.getReturn()
|
result = node.getReturn()
|
||||||
|
@ -362,11 +362,9 @@ API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||||
* Gets an API-graph successor for the given invocation.
|
* Gets an API-graph successor for the given invocation.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken token) {
|
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathTokenBase token) {
|
||||||
token.getName() = "Argument" and
|
token.getName() = "Argument" and
|
||||||
result =
|
result = invoke.getParameter(parseIntWithArity(token.getAnArgument(), invoke.getNumArgument()))
|
||||||
invoke
|
|
||||||
.getParameter(AccessPath::parseIntWithArity(token.getAnArgument(), invoke.getNumArgument()))
|
|
||||||
or
|
or
|
||||||
token.getName() = "ReturnValue" and
|
token.getName() = "ReturnValue" and
|
||||||
result = invoke.getReturn()
|
result = invoke.getReturn()
|
||||||
|
@ -378,10 +376,12 @@ API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken to
|
||||||
/**
|
/**
|
||||||
* Holds if `invoke` invokes a call-site filter given by `token`.
|
* Holds if `invoke` invokes a call-site filter given by `token`.
|
||||||
*/
|
*/
|
||||||
pragma[inline]
|
bindingset[token]
|
||||||
private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, AccessPathToken token) {
|
private predicate invocationMatchesCallSiteFilter(
|
||||||
|
Specific::InvokeNode invoke, AccessPathTokenBase token
|
||||||
|
) {
|
||||||
token.getName() = "WithArity" and
|
token.getName() = "WithArity" and
|
||||||
invoke.getNumArgument() = AccessPath::parseIntUnbounded(token.getAnArgument())
|
invoke.getNumArgument() = parseIntUnbounded(token.getAnArgument())
|
||||||
or
|
or
|
||||||
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
|
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,13 @@
|
||||||
* It must export the following members:
|
* It must export the following members:
|
||||||
* ```ql
|
* ```ql
|
||||||
* class Unit // a unit type
|
* class Unit // a unit type
|
||||||
* module AccessPathSyntax // a re-export of the AccessPathSyntax module
|
|
||||||
* class InvokeNode // a type representing an invocation connected to the API graph
|
* class InvokeNode // a type representing an invocation connected to the API graph
|
||||||
* module API // the API graph module
|
* module API // the API graph module
|
||||||
* predicate isPackageUsed(string package)
|
* predicate isPackageUsed(string package)
|
||||||
* API::Node getExtraNodeFromPath(string package, string type, string path, int n)
|
* API::Node getExtraNodeFromPath(string package, string type, string path, int n)
|
||||||
* API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token)
|
* API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token)
|
||||||
* API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token)
|
* API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathTokenBase token)
|
||||||
* predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToken token)
|
* predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathTokenBase token)
|
||||||
* InvokeNode getAnInvocationOf(API::Node node)
|
* InvokeNode getAnInvocationOf(API::Node node)
|
||||||
* predicate isExtraValidTokenNameInIdentifyingAccessPath(string name)
|
* predicate isExtraValidTokenNameInIdentifyingAccessPath(string name)
|
||||||
* predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name)
|
* predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name)
|
||||||
|
@ -21,13 +20,12 @@
|
||||||
|
|
||||||
private import codeql.ruby.AST
|
private import codeql.ruby.AST
|
||||||
private import ApiGraphModels
|
private import ApiGraphModels
|
||||||
|
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
|
private import codeql.dataflow.internal.AccessPathSyntax
|
||||||
// Re-export libraries needed by ApiGraphModels.qll
|
// Re-export libraries needed by ApiGraphModels.qll
|
||||||
import codeql.ruby.ApiGraphs
|
import codeql.ruby.ApiGraphs
|
||||||
import codeql.ruby.dataflow.internal.AccessPathSyntax as AccessPathSyntax
|
|
||||||
import codeql.ruby.DataFlow::DataFlow as DataFlow
|
import codeql.ruby.DataFlow::DataFlow as DataFlow
|
||||||
private import AccessPathSyntax
|
private import FlowSummaryImpl::Public
|
||||||
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
|
||||||
private import codeql.ruby.dataflow.internal.FlowSummaryImpl::Public
|
|
||||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
|
@ -140,7 +138,7 @@ private predicate methodMatchedByName(AccessPath path, string methodName) {
|
||||||
* Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`.
|
* Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
|
||||||
token.getName() = "Member" and
|
token.getName() = "Member" and
|
||||||
result = node.getMember(token.getAnArgument())
|
result = node.getMember(token.getAnArgument())
|
||||||
or
|
or
|
||||||
|
@ -152,13 +150,13 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||||
or
|
or
|
||||||
token.getName() = "Parameter" and
|
token.getName() = "Parameter" and
|
||||||
exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos |
|
exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos |
|
||||||
argPos = FlowSummaryImplSpecific::parseParamBody(token.getAnArgument()) and
|
token.getAnArgument() = FlowSummaryImpl::Input::encodeArgumentPosition(argPos) and
|
||||||
DataFlowDispatch::parameterMatch(paramPos, argPos) and
|
DataFlowDispatch::parameterMatch(paramPos, argPos) and
|
||||||
result = node.getParameterAtPosition(paramPos)
|
result = node.getParameterAtPosition(paramPos)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(DataFlow::ContentSet contents |
|
exists(DataFlow::ContentSet contents |
|
||||||
SummaryComponent::content(contents) = FlowSummaryImplSpecific::interpretComponentSpecific(token) and
|
token.getName() = FlowSummaryImpl::Input::encodeContent(contents, token.getAnArgument()) and
|
||||||
result = node.getContents(contents)
|
result = node.getContents(contents)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -167,10 +165,10 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
|
||||||
* Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`.
|
* Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token) {
|
API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathTokenBase token) {
|
||||||
token.getName() = "Argument" and
|
token.getName() = "Argument" and
|
||||||
exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos |
|
exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos |
|
||||||
paramPos = FlowSummaryImplSpecific::parseArgBody(token.getAnArgument()) and
|
token.getAnArgument() = FlowSummaryImpl::Input::encodeParameterPosition(paramPos) and
|
||||||
DataFlowDispatch::parameterMatch(paramPos, argPos) and
|
DataFlowDispatch::parameterMatch(paramPos, argPos) and
|
||||||
result = node.getArgumentAtPosition(argPos)
|
result = node.getArgumentAtPosition(argPos)
|
||||||
)
|
)
|
||||||
|
@ -199,7 +197,7 @@ API::Node getAFuzzySuccessor(API::Node node) {
|
||||||
* Holds if `invoke` matches the Ruby-specific call site filter in `token`.
|
* Holds if `invoke` matches the Ruby-specific call site filter in `token`.
|
||||||
*/
|
*/
|
||||||
bindingset[token]
|
bindingset[token]
|
||||||
predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToken token) {
|
predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathTokenBase token) {
|
||||||
token.getName() = "WithBlock" and
|
token.getName() = "WithBlock" and
|
||||||
exists(invoke.getBlock())
|
exists(invoke.getBlock())
|
||||||
or
|
or
|
||||||
|
|
|
@ -22,7 +22,7 @@ module Utils {
|
||||||
.getExpr()
|
.getExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import TypeTrackerSpecific
|
private import TypeTrackerSpecific
|
||||||
|
private import codeql.util.Boolean
|
||||||
|
|
||||||
cached
|
cached
|
||||||
private module Cached {
|
private module Cached {
|
||||||
|
|
|
@ -1,16 +1,7 @@
|
||||||
private import codeql.ruby.AST as Ast
|
|
||||||
private import codeql.ruby.CFG as Cfg
|
|
||||||
private import Cfg::CfgNodes
|
|
||||||
private import codeql.ruby.dataflow.FlowSummary
|
|
||||||
private import codeql.ruby.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
|
||||||
private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic
|
private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic
|
||||||
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
|
||||||
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
|
||||||
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
|
||||||
private import codeql.ruby.dataflow.internal.AccessPathSyntax
|
|
||||||
private import internal.TypeTrackingImpl as TypeTrackingImpl
|
private import internal.TypeTrackingImpl as TypeTrackingImpl
|
||||||
import codeql.util.Boolean
|
deprecated import codeql.util.Boolean
|
||||||
|
|
||||||
deprecated class Node = DataFlowPublic::Node;
|
deprecated class Node = DataFlowPublic::Node;
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,6 @@ private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic
|
||||||
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||||
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific
|
|
||||||
private import codeql.ruby.dataflow.internal.AccessPathSyntax
|
|
||||||
|
|
||||||
/** Holds if there is direct flow from `param` to a return. */
|
/** Holds if there is direct flow from `param` to a return. */
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
|
@ -170,30 +168,30 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Summaries and their stacks
|
// Summaries and their stacks
|
||||||
class SummaryComponent = FlowSummary::SummaryComponent;
|
class SummaryComponent = FlowSummaryImpl::Private::SummaryComponent;
|
||||||
|
|
||||||
class SummaryComponentStack = FlowSummary::SummaryComponentStack;
|
class SummaryComponentStack = FlowSummaryImpl::Private::SummaryComponentStack;
|
||||||
|
|
||||||
predicate singleton = FlowSummary::SummaryComponentStack::singleton/1;
|
predicate singleton = FlowSummaryImpl::Private::SummaryComponentStack::singleton/1;
|
||||||
|
|
||||||
predicate push = FlowSummary::SummaryComponentStack::push/2;
|
predicate push = FlowSummaryImpl::Private::SummaryComponentStack::push/2;
|
||||||
|
|
||||||
// Relating content to summaries
|
// Relating content to summaries
|
||||||
predicate content = FlowSummary::SummaryComponent::content/1;
|
predicate content = FlowSummaryImpl::Private::SummaryComponent::content/1;
|
||||||
|
|
||||||
predicate withoutContent = FlowSummary::SummaryComponent::withoutContent/1;
|
predicate withoutContent = FlowSummaryImpl::Private::SummaryComponent::withoutContent/1;
|
||||||
|
|
||||||
predicate withContent = FlowSummary::SummaryComponent::withContent/1;
|
predicate withContent = FlowSummaryImpl::Private::SummaryComponent::withContent/1;
|
||||||
|
|
||||||
predicate return = FlowSummary::SummaryComponent::return/0;
|
predicate return = FlowSummaryImpl::Private::SummaryComponent::return/0;
|
||||||
|
|
||||||
// Callables
|
// Callables
|
||||||
class SummarizedCallable = FlowSummary::SummarizedCallable;
|
class SummarizedCallable = FlowSummaryImpl::Private::SummarizedCallableImpl;
|
||||||
|
|
||||||
// Relating nodes to summaries
|
// Relating nodes to summaries
|
||||||
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
|
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
|
||||||
exists(DataFlowDispatch::ParameterPosition pos, DataFlowPrivate::ArgumentNode n |
|
exists(DataFlowDispatch::ParameterPosition pos, DataFlowPrivate::ArgumentNode n |
|
||||||
arg = FlowSummary::SummaryComponent::argument(pos) and
|
arg = FlowSummaryImpl::Private::SummaryComponent::argument(pos) and
|
||||||
argumentPositionMatch(call.asExpr(), n, pos)
|
argumentPositionMatch(call.asExpr(), n, pos)
|
||||||
|
|
|
|
||||||
isPostUpdate = false and result = n
|
isPostUpdate = false and result = n
|
||||||
|
@ -204,7 +202,7 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||||
|
|
||||||
Node parameterOf(Node callable, SummaryComponent param) {
|
Node parameterOf(Node callable, SummaryComponent param) {
|
||||||
exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos |
|
exists(DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos |
|
||||||
param = FlowSummary::SummaryComponent::parameter(apos) and
|
param = FlowSummaryImpl::Private::SummaryComponent::parameter(apos) and
|
||||||
DataFlowDispatch::parameterMatch(ppos, apos) and
|
DataFlowDispatch::parameterMatch(ppos, apos) and
|
||||||
result
|
result
|
||||||
.(DataFlowPrivate::ParameterNodeImpl)
|
.(DataFlowPrivate::ParameterNodeImpl)
|
||||||
|
@ -213,13 +211,15 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||||
}
|
}
|
||||||
|
|
||||||
Node returnOf(Node callable, SummaryComponent return) {
|
Node returnOf(Node callable, SummaryComponent return) {
|
||||||
return = FlowSummary::SummaryComponent::return() and
|
return = FlowSummaryImpl::Private::SummaryComponent::return() and
|
||||||
result.(DataFlowPrivate::ReturnNode).(DataFlowPrivate::NodeImpl).getCfgScope() =
|
result.(DataFlowPrivate::ReturnNode).(DataFlowPrivate::NodeImpl).getCfgScope() =
|
||||||
callable.asExpr().getExpr()
|
callable.asExpr().getExpr()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relating callables to nodes
|
// Relating callables to nodes
|
||||||
Node callTo(SummarizedCallable callable) { result.asExpr().getExpr() = callable.getACallSimple() }
|
Node callTo(SummarizedCallable callable) {
|
||||||
|
result.asExpr().getExpr() = callable.(FlowSummary::SummarizedCallable).getACallSimple()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow<SummaryTypeTrackerInput>;
|
private module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow<SummaryTypeTrackerInput>;
|
||||||
|
|
|
@ -4,7 +4,6 @@ private import ruby
|
||||||
private import codeql.ruby.dataflow.FlowSummary
|
private import codeql.ruby.dataflow.FlowSummary
|
||||||
private import codeql.ruby.dataflow.internal.DataFlowPrivate
|
private import codeql.ruby.dataflow.internal.DataFlowPrivate
|
||||||
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||||
private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific
|
|
||||||
private import codeql.ruby.frameworks.core.Gem
|
private import codeql.ruby.frameworks.core.Gem
|
||||||
private import codeql.ruby.frameworks.data.ModelsAsData
|
private import codeql.ruby.frameworks.data.ModelsAsData
|
||||||
private import codeql.ruby.frameworks.data.internal.ApiGraphModelsExtensions
|
private import codeql.ruby.frameworks.data.internal.ApiGraphModelsExtensions
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
import ruby
|
import ruby
|
||||||
|
import codeql.dataflow.internal.AccessPathSyntax
|
||||||
import codeql.ruby.ast.internal.TreeSitter
|
import codeql.ruby.ast.internal.TreeSitter
|
||||||
import codeql.ruby.dataflow.internal.AccessPathSyntax
|
import codeql.ruby.frameworks.data.internal.ApiGraphModels as ApiGraphModels
|
||||||
import codeql.ruby.frameworks.data.internal.ApiGraphModels
|
|
||||||
import codeql.ruby.ApiGraphs
|
import codeql.ruby.ApiGraphs
|
||||||
import TestUtilities.InlineExpectationsTest
|
import TestUtilities.InlineExpectationsTest
|
||||||
|
|
||||||
class AccessPathFromExpectation extends AccessPath::Range {
|
private predicate accessPathRange(string s) { hasExpectationWithValue(_, s) }
|
||||||
AccessPathFromExpectation() { hasExpectationWithValue(_, this) }
|
|
||||||
}
|
import AccessPath<accessPathRange/1>
|
||||||
|
|
||||||
API::Node evaluatePath(AccessPath path, int n) {
|
API::Node evaluatePath(AccessPath path, int n) {
|
||||||
path instanceof AccessPathFromExpectation and
|
|
||||||
n = 1 and
|
n = 1 and
|
||||||
exists(AccessPathToken token | token = path.getToken(0) |
|
exists(AccessPathToken token | token = path.getToken(0) |
|
||||||
token.getName() = "Member" and
|
token.getName() = "Member" and
|
||||||
|
@ -23,9 +22,9 @@ API::Node evaluatePath(AccessPath path, int n) {
|
||||||
result = token.getAnArgument().(API::EntryPoint).getANode()
|
result = token.getAnArgument().(API::EntryPoint).getANode()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
result = getSuccessorFromNode(evaluatePath(path, n - 1), path.getToken(n - 1))
|
result = ApiGraphModels::getSuccessorFromNode(evaluatePath(path, n - 1), path.getToken(n - 1))
|
||||||
or
|
or
|
||||||
result = getSuccessorFromInvoke(evaluatePath(path, n - 1), path.getToken(n - 1))
|
result = ApiGraphModels::getSuccessorFromInvoke(evaluatePath(path, n - 1), path.getToken(n - 1))
|
||||||
or
|
or
|
||||||
// TODO this is a workaround, support parsing of Method['[]'] instead
|
// TODO this is a workaround, support parsing of Method['[]'] instead
|
||||||
path.getToken(n - 1).getName() = "MethodBracket" and
|
path.getToken(n - 1).getName() = "MethodBracket" and
|
||||||
|
|
|
@ -16,7 +16,7 @@ abstract private class Summary extends SimpleSummarizedCallable {
|
||||||
bindingset[this]
|
bindingset[this]
|
||||||
Summary() { any() }
|
Summary() { any() }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
this.propagates(input, output) and preservesValue = true
|
this.propagates(input, output) and preservesValue = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,12 @@ import codeql.ruby.ApiGraphs
|
||||||
import codeql.ruby.dataflow.FlowSummary
|
import codeql.ruby.dataflow.FlowSummary
|
||||||
import codeql.ruby.TaintTracking
|
import codeql.ruby.TaintTracking
|
||||||
import codeql.ruby.dataflow.internal.FlowSummaryImpl
|
import codeql.ruby.dataflow.internal.FlowSummaryImpl
|
||||||
import codeql.ruby.dataflow.internal.AccessPathSyntax
|
|
||||||
import codeql.ruby.frameworks.data.ModelsAsData
|
import codeql.ruby.frameworks.data.ModelsAsData
|
||||||
import TestUtilities.InlineFlowTest
|
import TestUtilities.InlineFlowTest
|
||||||
import PathGraph
|
import PathGraph
|
||||||
|
|
||||||
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
|
query predicate invalidSpecComponent(SummarizedCallable sc, string s, string c) {
|
||||||
(sc.propagatesFlowExt(s, _, _) or sc.propagatesFlowExt(_, s, _)) and
|
(sc.propagatesFlow(s, _, _) or sc.propagatesFlow(_, s, _)) and
|
||||||
Private::External::invalidSpecComponent(s, c)
|
Private::External::invalidSpecComponent(s, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ private class SummarizedCallableIdentity extends SummarizedCallable {
|
||||||
|
|
||||||
override MethodCall getACall() { result.getMethodName() = this }
|
override MethodCall getACall() { result.getMethodName() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "ReturnValue" and
|
output = "ReturnValue" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -36,7 +35,7 @@ private class SummarizedCallableApplyBlock extends SummarizedCallable {
|
||||||
|
|
||||||
override MethodCall getACall() { result.getMethodName() = this }
|
override MethodCall getACall() { result.getMethodName() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[0]" and
|
input = "Argument[0]" and
|
||||||
output = "Argument[block].Parameter[0]" and
|
output = "Argument[block].Parameter[0]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
@ -52,7 +51,7 @@ private class SummarizedCallableApplyLambda extends SummarizedCallable {
|
||||||
|
|
||||||
override MethodCall getACall() { result.getMethodName() = this }
|
override MethodCall getACall() { result.getMethodName() = this }
|
||||||
|
|
||||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
|
||||||
input = "Argument[1]" and
|
input = "Argument[1]" and
|
||||||
output = "Argument[0].Parameter[0]" and
|
output = "Argument[0].Parameter[0]" and
|
||||||
preservesValue = true
|
preservesValue = true
|
||||||
|
|
|
@ -140,6 +140,9 @@ signature module InputSig {
|
||||||
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
|
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
|
||||||
*/
|
*/
|
||||||
class ContentSet {
|
class ContentSet {
|
||||||
|
/** Gets a textual representation of this element. */
|
||||||
|
string toString();
|
||||||
|
|
||||||
/** Gets a content that may be stored into when storing into this set. */
|
/** Gets a content that may be stored into when storing into this set. */
|
||||||
Content getAStoreContent();
|
Content getAStoreContent();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An access path token such as `Argument[1]` or `ReturnValue`.
|
||||||
|
*/
|
||||||
|
class AccessPathTokenBase extends string {
|
||||||
|
bindingset[this]
|
||||||
|
AccessPathTokenBase() { exists(this) }
|
||||||
|
|
||||||
|
bindingset[this]
|
||||||
|
private string getPart(int part) {
|
||||||
|
result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets the name of the token, such as `Member` from `Member[x]` */
|
||||||
|
bindingset[this]
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
bindingset[this]
|
||||||
|
string getArgumentList() { result = this.getPart(2) }
|
||||||
|
|
||||||
|
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
|
||||||
|
bindingset[this]
|
||||||
|
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
|
||||||
|
|
||||||
|
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
|
||||||
|
bindingset[this]
|
||||||
|
string getAnArgument() { result = this.getArgument(_) }
|
||||||
|
|
||||||
|
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
|
||||||
|
bindingset[this]
|
||||||
|
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
|
||||||
|
}
|
||||||
|
|
||||||
|
final private class AccessPathTokenBaseFinal = AccessPathTokenBase;
|
||||||
|
|
||||||
|
signature predicate accessPathRangeSig(string s);
|
||||||
|
|
||||||
|
/** Companion module to the `AccessPath` class. */
|
||||||
|
module AccessPath<accessPathRangeSig/1 accessPathRange> {
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
final class AccessPath extends string {
|
||||||
|
AccessPath() { accessPathRange(this) }
|
||||||
|
|
||||||
|
/** 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 path token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
|
||||||
|
*/
|
||||||
|
class AccessPathToken extends AccessPathTokenBaseFinal {
|
||||||
|
AccessPathToken() { this = getRawToken(_, _) }
|
||||||
|
|
||||||
|
/** Gets the name of the token, such as `Member` from `Member[x]` */
|
||||||
|
pragma[nomagic]
|
||||||
|
string getName() { result = super.getName() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the argument list, such as `1,2` from `Member[1,2]`,
|
||||||
|
* or has no result if there are no arguments.
|
||||||
|
*/
|
||||||
|
pragma[nomagic]
|
||||||
|
string getArgumentList() { result = super.getArgumentList() }
|
||||||
|
|
||||||
|
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
|
||||||
|
pragma[nomagic]
|
||||||
|
string getArgument(int n) { result = super.getArgument(n) }
|
||||||
|
|
||||||
|
/** 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`. */
|
||||||
|
pragma[nomagic]
|
||||||
|
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
|
||||||
|
}
|
||||||
|
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче