JS: Instantiate flow summary library

This commit is contained in:
Asger F
2023-10-04 19:58:49 +02:00
parent 8dc0800526
commit 60101f5e6a
7 changed files with 552 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
/** Provides classes and predicates for defining flow summaries. */
private import javascript
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as Impl
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImplSpecific
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
private import semmle.javascript.dataflow.internal.DataFlowPrivate
class SummaryComponent = Impl::Public::SummaryComponent;
/** Provides predicates for constructing summary components. */
module SummaryComponent {
private import Impl::Public::SummaryComponent as SC
predicate parameter = SC::parameter/1;
predicate argument = SC::argument/1;
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(MkThisParameter()) }
/** Gets a summary component that represents the return value of a call. */
SummaryComponent return() { result = SC::return(MkNormalReturnKind()) }
/** Gets a summary component that represents the exception thrown from a call. */
SummaryComponent exceptionalReturn() { result = SC::return(MkExceptionalReturnKind()) }
}
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 the return value of a call. */
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
/** Gets a singleton stack representing the exception thrown from a call. */
SummaryComponentStack exceptionalReturn() {
result = singleton(SummaryComponent::exceptionalReturn())
}
}
/** A callable with a flow summary, identified by a unique string. */
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
bindingset[this]
SummarizedCallable() { any() }
/**
* Same as
*
* ```ql
* propagatesFlow(
* SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
* )
* ```
*
* but uses an external (string) representation of the input and output stacks.
*/
pragma[nomagic]
predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() }
/**
* Gets the synthesized parameter that results from an input specification
* that starts with `Argument[s]` for this library callable.
*/
DataFlow::ParameterNode getParameter(string s) {
exists(ParameterPosition pos |
DataFlowImplCommon::parameterNode(result, MkLibraryCallable(this), pos) and
s = getParameterPosition(pos)
)
}
}
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;

View File

@@ -5,6 +5,7 @@
*/
private import javascript
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as FlowSummaryImpl
cached
private module Cached {
/**
@@ -48,6 +49,7 @@ private module Cached {
} or
TConstructorThisArgumentNode(InvokeExpr e) { e instanceof NewExpr or e instanceof SuperCall } or
TConstructorThisPostUpdate(Constructor ctor) or
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
}
import Cached

View File

@@ -5,11 +5,24 @@ private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
private import semmle.javascript.dataflow.internal.Contents::Private
private import semmle.javascript.dataflow.internal.VariableCapture
private import semmle.javascript.dataflow.internal.sharedlib.DataFlowImplCommon as DataFlowImplCommon
private import sharedlib.FlowSummaryImpl as FlowSummaryImpl
private class Node = DataFlow::Node;
class PostUpdateNode = DataFlow::PostUpdateNode;
class FlowSummaryNode extends DataFlow::Node, TFlowSummaryNode {
FlowSummaryImpl::Private::SummaryNode getSummaryNode() { this = TFlowSummaryNode(result) }
/** Gets the summarized callable that this node belongs to. */
FlowSummaryImpl::Public::SummarizedCallable getSummarizedCallable() {
result = this.getSummaryNode().getSummarizedCallable()
}
cached
override string toString() { result = this.getSummaryNode().toString() }
}
cached
newtype TReturnKind =
MkNormalReturnKind() or
@@ -33,6 +46,8 @@ private predicate returnNodeImpl(DataFlow::Node node, ReturnKind kind) {
// See the models for AsyncAwait and Generator.
not fun.isAsyncOrGenerator()
)
or
FlowSummaryImpl::Private::summaryReturnNode(node.(FlowSummaryNode).getSummaryNode(), kind)
}
private DataFlow::Node getAnOutNodeImpl(DataFlowCall call, ReturnKind kind) {
@@ -45,6 +60,8 @@ private DataFlow::Node getAnOutNodeImpl(DataFlowCall call, ReturnKind kind) {
kind = MkExceptionalReturnKind() and result = call.asBoundCall(_).getExceptionalReturn()
or
kind = MkNormalReturnKind() and result = call.asAccessorCall().(DataFlow::PropRead)
or
FlowSummaryImpl::Private::summaryOutNode(call, result.(FlowSummaryNode).getSummaryNode(), kind)
}
class ReturnNode extends DataFlow::Node {
@@ -86,6 +103,9 @@ predicate postUpdatePair(Node pre, Node post) {
pre = TThisNode(constructor) and
post = TConstructorThisPostUpdate(constructor)
)
or
FlowSummaryImpl::Private::summaryPostUpdateNode(post.(FlowSummaryNode).getSummaryNode(),
pre.(FlowSummaryNode).getSummaryNode())
}
class CastNode extends DataFlow::Node instanceof EmptyType { }
@@ -93,6 +113,7 @@ class CastNode extends DataFlow::Node instanceof EmptyType { }
cached
newtype TDataFlowCallable =
MkSourceCallable(StmtContainer container) or
MkLibraryCallable(LibraryCallable callable)
/**
* A callable entity. This is a wrapper around either a `StmtContainer` or a `LibraryCallable`.
@@ -122,6 +143,18 @@ class DataFlowCallable extends TDataFlowCallable {
LibraryCallable asLibraryCallable() { this = MkLibraryCallable(result) }
}
/** A callable defined in library code, identified by a unique string. */
abstract class LibraryCallable extends string {
bindingset[this]
LibraryCallable() { any() }
/** Gets a call to this library callable. */
DataFlow::InvokeNode getACall() { none() }
/** Same as `getACall()` except this does not depend on the call graph or API graph. */
DataFlow::InvokeNode getACallSimple() { none() }
}
private predicate isParameterNodeImpl(Node p, DataFlowCallable c, ParameterPosition pos) {
p = c.asSourceCallable().(Function).getParameter(pos.asPositional()).flow()
or
@@ -130,6 +163,12 @@ private predicate isParameterNodeImpl(Node p, DataFlowCallable c, ParameterPosit
pos.isFunctionSelfReference() and p = TFunctionSelfReferenceNode(c.asSourceCallable())
or
pos.isArgumentsArray() and p = TReflectiveParametersNode(c.asSourceCallable())
or
exists(FlowSummaryNode summaryNode |
summaryNode = p and
FlowSummaryImpl::Private::summaryParameterNode(summaryNode.getSummaryNode(), pos) and
c.asLibraryCallable() = summaryNode.getSummarizedCallable()
)
}
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
@@ -165,6 +204,8 @@ private predicate isArgumentNodeImpl(Node n, DataFlowCall call, ArgumentPosition
or
// argument to setter (TODO: this has no post-update node)
pos.asPositional() = 0 and n = call.asAccessorCall().(DataFlow::PropWrite).getRhs()
or
FlowSummaryImpl::Private::summaryArgumentNode(call, n.(FlowSummaryNode).getSummaryNode(), pos)
}
predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos) {
@@ -173,6 +214,10 @@ predicate isArgumentNode(ArgumentNode n, DataFlowCall call, ArgumentPosition pos
DataFlowCallable nodeGetEnclosingCallable(Node node) {
result.asSourceCallable() = node.getContainer()
or
result.asLibraryCallable() = node.(FlowSummaryNode).getSummarizedCallable()
or
result.asLibraryCallable() = node.(FlowSummaryIntermediateAwaitStoreNode).getSummarizedCallable()
}
private newtype TDataFlowType =
@@ -189,6 +234,8 @@ DataFlowType getNodeType(Node node) { result = TTodoDataFlowType() and exists(no
predicate nodeIsHidden(Node node) {
DataFlow::PathNode::shouldNodeBeHidden(node)
or
node instanceof FlowSummaryNode
}
predicate neverSkipInPathGraph(Node node) {
@@ -232,6 +279,11 @@ private newtype TDataFlowCall =
node = TValueNode(any(PropAccess p)) or
node = TPropNode(any(PropertyPattern p))
} or
MkSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
) {
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
}
class DataFlowCall extends TDataFlowCall {
DataFlowCallable getEnclosingCallable() { none() } // Overridden in subclass
@@ -246,6 +298,13 @@ class DataFlowCall extends TDataFlowCall {
DataFlow::InvokeNode asBoundCall(int boundArgs) { this = MkBoundCall(result, boundArgs) }
predicate isSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable enclosingCallable,
FlowSummaryImpl::Private::SummaryNode receiver
) {
this = MkSummaryCall(enclosingCallable, receiver)
}
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
@@ -334,6 +393,23 @@ private class AccessorCall extends DataFlowCall, MkAccessorCall {
ref.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
}
class SummaryCall extends DataFlowCall, MkSummaryCall {
private FlowSummaryImpl::Public::SummarizedCallable enclosingCallable;
private FlowSummaryImpl::Private::SummaryNode receiver;
SummaryCall() { this = MkSummaryCall(enclosingCallable, receiver) }
override DataFlowCallable getEnclosingCallable() {
result.asLibraryCallable() = enclosingCallable
}
override string toString() {
result = "[summary] call to " + receiver + " in " + enclosingCallable
}
/** Gets the receiver node. */
FlowSummaryImpl::Private::SummaryNode getReceiver() { result = receiver }
}
private int getMaxArity() {
// TODO: account for flow summaries
@@ -416,6 +492,11 @@ DataFlowCallable viableCallable(DataFlowCall node) {
)
or
result.asSourceCallableNotExterns() = node.asAccessorCall().getAnAccessorCallee().getFunction()
or
exists(LibraryCallable callable |
result = MkLibraryCallable(callable) and
node.asOrdinaryCall() = [callable.getACall(), callable.getACallSimple()]
)
}
/**
@@ -455,6 +536,9 @@ private predicate valuePreservingStep(Node node1, Node node2) {
or
node2 = FlowSteps::getThrowTarget(node1)
or
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1.(FlowSummaryNode).getSummaryNode(),
node2.(FlowSummaryNode).getSummaryNode(), true)
or
// Step from post-update nodes to local sources of the pre-update node. This emulates how JS usually tracks side effects.
exists(PostUpdateNode postUpdate |
node1 = postUpdate and
@@ -480,6 +564,9 @@ predicate localMustFlowStep(Node node1, Node node2) { node1 = node2.getImmediate
predicate jumpStep(Node node1, Node node2) {
valuePreservingStep(node1, node2) and
node1.getContainer() != node2.getContainer()
or
FlowSummaryImpl::Private::Steps::summaryJumpStep(node1.(FlowSummaryNode).getSummaryNode(),
node2.(FlowSummaryNode).getSummaryNode())
}
/**
@@ -497,6 +584,13 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
not exists(read.getPropertyName()) and
c = ContentSet::arrayElement()
)
or
exists(ContentSet contentSet |
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(),
contentSet, node2.(FlowSummaryNode).getSummaryNode())
|
c = contentSet
)
}
/** Gets the post-update node for which `node` is the corresponding pre-update node. */
@@ -523,6 +617,10 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
// Target the post-update node if one exists (for object literals we do not generate post-update nodes)
node2 = tryGetPostUpdate(write.getBase())
)
or
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
node2.(FlowSummaryNode).getSummaryNode()) and
)
}
/**
@@ -531,6 +629,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
* in `x.f = newValue`.
*/
predicate clearsContent(Node n, ContentSet c) {
FlowSummaryImpl::Private::Steps::summaryClearsContent(n.(FlowSummaryNode).getSummaryNode(), c)
}
/**
@@ -538,6 +637,7 @@ predicate clearsContent(Node n, ContentSet c) {
* at node `n`.
*/
predicate expectsContent(Node n, ContentSet c) {
FlowSummaryImpl::Private::Steps::summaryExpectsContent(n.(FlowSummaryNode).getSummaryNode(), c)
}
/**
@@ -557,6 +657,7 @@ int accessPathLimit() { result = 5 }
* by default as a heuristic.
*/
predicate allowParameterReturnInSelf(ParameterNode p) {
FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p)
}
class LambdaCallKind = Unit;
@@ -568,6 +669,8 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */
predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) {
call.isSummaryCall(_, receiver.(FlowSummaryNode).getSummaryNode()) and exists(kind)
or
receiver = call.asOrdinaryCall().getCalleeNode() and exists(kind)
}

View File

@@ -0,0 +1,339 @@
/**
* Provides JS specific classes and predicates for defining flow summaries.
*/
private import javascript
private import semmle.javascript.dataflow.internal.DataFlowPrivate
private import semmle.javascript.dataflow.internal.Contents::Private
private import semmle.javascript.dataflow.FlowSummary as FlowSummary
private import sharedlib.DataFlowImplCommon
private import sharedlib.FlowSummaryImpl::Private as Private
private import sharedlib.FlowSummaryImpl::Public
import semmle.javascript.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
private class Node = DataFlow::Node;
/**
* 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;
/**
* Holds if a neutral model exists for `c` of kind `kind` and with provenance `provenance`.
* Note: Neutral models have not been implemented for Javascript.
*/
predicate neutralElement(NeutralCallableBase c, string kind, string provenance) { none() }
DataFlowCallable inject(SummarizedCallable c) { result.asLibraryCallable() = c }
/** Gets the parameter position representing a callback itself, if any. */
ArgumentPosition callbackSelfParameterPosition() { result.isFunctionSelfReference() }
/** Gets the synthesized data-flow call for `receiver`. */
SummaryCall summaryDataFlowCall(Private::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. */
bindingset[c, pos]
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 summary model exists for `c` with provenance `provenance`,
* which means that there is no flow through `c`.
* Note. Neutral models have not been implemented for JS.
*/
predicate neutralSummaryElement(FlowSummary::SummarizedCallable c, string provenance) { none() }
pragma[inline]
private SummaryComponent makeContentComponents(
Private::AccessPathToken token, string name, ContentSet contents
) {
token.getName() = name and
result = FlowSummary::SummaryComponent::content(contents)
or
token.getName() = "With" + name and
result = FlowSummary::SummaryComponent::withContent(contents)
or
token.getName() = "Without" + name and
result = FlowSummary::SummaryComponent::withoutContent(contents)
}
pragma[inline]
private SummaryComponent makePropertyContentComponents(
Private::AccessPathToken token, string name, PropertyName content
) {
result = makeContentComponents(token, name, ContentSet::property(content))
}
/**
* Gets the content set corresponding to `Awaited[arg]`.
*/
private ContentSet getPromiseContent(string arg) {
arg = "value" and result = ContentSet::promiseValue()
or
arg = "error" and result = ContentSet::promiseError()
}
pragma[nomagic]
private predicate positionName(ParameterPosition pos, string operand) {
operand = pos.asPositional().toString()
or
pos.isThis() and operand = "this"
or
pos.isFunctionSelfReference() and operand = "function"
or
pos.isArgumentsArray() and operand = "arguments-array"
or
operand = pos.asPositionalLowerBound() + ".."
}
/**
* Holds if `operand` desugars to the given `pos`. Only used for parsing.
*/
bindingset[operand]
private predicate desugaredPositionName(ParameterPosition pos, string operand) {
operand = "any" and
pos.asPositionalLowerBound() = 0
or
pos.asPositional() = AccessPathSyntax::AccessPath::parseInt(operand) // parse closed intervals
}
bindingset[operand]
private ParameterPosition parsePosition(string operand) {
positionName(result, operand) or desugaredPositionName(result, operand)
}
/**
* Gets the summary component for specification component `c`, if any.
*
* This covers all the JS-specific components of a flow summary.
*/
SummaryComponent interpretComponentSpecific(Private::AccessPathToken c) {
c.getName() = "Argument" and
result = FlowSummary::SummaryComponent::argument(parsePosition(c.getAnArgument()))
or
c.getName() = "Parameter" and
result = FlowSummary::SummaryComponent::parameter(parsePosition(c.getAnArgument()))
or
result = makePropertyContentComponents(c, "Member", c.getAnArgument())
or
result = makeContentComponents(c, "Awaited", getPromiseContent(c.getAnArgument()))
or
c.getNumArgument() = 0 and
result = makeContentComponents(c, "ArrayElement", ContentSet::arrayElement())
or
c.getAnArgument() = "?" and
result = makeContentComponents(c, "ArrayElement", ContentSet::arrayElementUnknown())
or
exists(int n |
n = c.getAnArgument().toInt() and
result = makeContentComponents(c, "ArrayElement", ContentSet::arrayElementKnown(n))
or
// ArrayElement[n!] refers to index n, and never the unknown content
c.getAnArgument().regexpCapture("(\\d+)!", 1).toInt() = n and
result = makePropertyContentComponents(c, "ArrayElement", n.toString())
or
// ArrayElement[n..] refers to index n or greater
n = AccessPathSyntax::AccessPath::parseLowerBound(c.getAnArgument()) and
result = makeContentComponents(c, "ArrayElement", ContentSet::arrayElementLowerBoundFromInt(n))
)
or
c.getNumArgument() = 0 and
result = makeContentComponents(c, "SetElement", ContentSet::setElement())
or
c.getNumArgument() = 0 and
result = makeContentComponents(c, "IteratorElement", ContentSet::iteratorElement())
or
c.getNumArgument() = 0 and
result = makeContentComponents(c, "IteratorError", ContentSet::iteratorError())
or
c.getNumArgument() = 0 and
result = makeContentComponents(c, "MapKey", ContentSet::mapKey())
or
//
// Note: although it is supported internally, we currently do not expose a syntax for MapValue with a known key
//
c.getNumArgument() = 0 and
result = makeContentComponents(c, "MapValue", ContentSet::mapValueAll())
or
c.getName() = "ReturnValue" and
c.getAnArgument() = "exception" and
result = SummaryComponent::return(MkExceptionalReturnKind())
}
private string getMadStringFromContentSetAux(ContentSet cs) {
cs = ContentSet::arrayElement() and
result = "ArrayElement"
or
cs = ContentSet::arrayElementUnknown() and
result = "ArrayElement[?]"
or
exists(int n |
cs = ContentSet::arrayElementLowerBound(n) and
result = "ArrayElement[" + n + "..]" and
n > 0 // n=0 is just 'ArrayElement'
or
cs = ContentSet::arrayElementKnown(n) and
result = "ArrayElement[" + n + "]"
or
n = cs.asPropertyName().toInt() and
n >= 0 and
result = "ArrayElement[" + n + "!]"
)
or
cs = ContentSet::mapValueAll() and result = "MapValue"
or
cs = ContentSet::mapKey() and result = "MapKey"
or
cs = ContentSet::setElement() and result = "SetElement"
or
cs = ContentSet::iteratorElement() and result = "IteratorElement"
or
cs = ContentSet::iteratorError() and result = "IteratorError"
or
exists(string awaitedArg |
cs = getPromiseContent(awaitedArg) and
result = "Awaited[" + awaitedArg + "]"
)
}
private string getMadStringFromContentSet(ContentSet cs) {
result = getMadStringFromContentSetAux(cs)
or
not exists(getMadStringFromContentSetAux(cs)) and
result = "Member[" + cs.asSingleton() + "]"
}
/** Gets the textual representation of a summary component in the format used for MaD models. */
string getMadRepresentationSpecific(SummaryComponent sc) {
exists(ContentSet cs |
sc = Private::TContentSummaryComponent(cs) and result = getMadStringFromContentSet(cs)
)
or
exists(ReturnKind rk |
sc = Private::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. */
bindingset[pos]
string getParameterPosition(ParameterPosition pos) { positionName(pos, result) and result != "any" }
/** Gets the textual representation of an argument position in the format used for flow summaries. */
bindingset[pos]
string getArgumentPosition(ArgumentPosition pos) { positionName(pos, result) and result != "any" }
/** 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"`. */
MkNormalReturnKind getReturnValueKind() { any() }
/**
* All definitions in this module are required by the shared implementation
* (for source/sink interpretation), but they are unused for JS, 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. */
StmtContainer 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
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */
bindingset[s]
ArgumentPosition parseParamBody(string s) {
s = "this" and result.isThis()
or
s = "function" and result.isFunctionSelfReference()
or
result.asPositional() = AccessPathSyntax::AccessPath::parseInt(s)
}
/** Gets the parameter position obtained by parsing `X` in `Argument[X]`. */
bindingset[s]
ParameterPosition parseArgBody(string s) {
result = parseParamBody(s) // Currently these are identical
}

View File

@@ -1,9 +1,12 @@
private import javascript
private import semmle.javascript.dataflow.internal.DataFlowPrivate
private import semmle.javascript.dataflow.internal.Contents::Public
private import semmle.javascript.dataflow.internal.sharedlib.FlowSummaryImpl as FlowSummaryImpl
cached
predicate defaultAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1.(FlowSummaryNode).getSummaryNode(),
node2.(FlowSummaryNode).getSummaryNode(), false)
}
/**

View File

@@ -0,0 +1,12 @@
private import javascript
// This file provides the input to FlowSummaryImpl.qll, which is shared via identical-files.json.
module Private {
import semmle.javascript.dataflow.internal.DataFlowPrivate
}
module Public {
import semmle.javascript.dataflow.internal.Contents::Public
class Node = DataFlow::Node;
}

View File

@@ -0,0 +1 @@
import semmle.javascript.dataflow.internal.FlowSummaryPrivate