Merge pull request #14573 from hvitved/flow-summary-impl-param

Move `FlowSummaryImpl.qll` to `dataflow` pack
This commit is contained in:
Tom Hvitved 2023-12-14 12:24:15 +01:00 коммит произвёл GitHub
Родитель 8f0e0b6559 098afb935b
Коммит c8b4a215bc
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
110 изменённых файлов: 4820 добавлений и 13159 удалений

Просмотреть файл

@ -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))) }
}
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше