Merge pull request #18125 from asgerf/jss/summary-type-tracker

JS: Derive type-tracking steps from flow summaries
This commit is contained in:
Asger F
2024-12-03 12:40:56 +01:00
committed by GitHub
20 changed files with 289 additions and 65 deletions

View File

@@ -12,6 +12,7 @@ dependencies:
codeql/ssa: ${workspace}
codeql/threat-models: ${workspace}
codeql/tutorial: ${workspace}
codeql/typetracking: ${workspace}
codeql/util: ${workspace}
codeql/xml: ${workspace}
codeql/yaml: ${workspace}

View File

@@ -11,19 +11,18 @@ abstract class SummarizedCallable extends LibraryCallable, Impl::Public::Summari
bindingset[this]
SummarizedCallable() { any() }
// TODO: rename 'propagatesFlowExt' and/or override 'propagatesFlow' directly
/**
* Holds if data may flow from `input` to `output` through this callable.
*
* `preservesValue` indicates whether this is a value-preserving step or a taint-step.
*/
pragma[nomagic]
predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() }
predicate propagatesFlow(string input, string output, boolean preservesValue) { none() }
override predicate propagatesFlow(
string input, string output, boolean preservesValue, string model
) {
this.propagatesFlowExt(input, output, preservesValue) and model = this
this.propagatesFlow(input, output, preservesValue) and model = this
}
/**

View File

@@ -1,6 +1,8 @@
import javascript
private import semmle.javascript.dataflow.TypeTracking
private import semmle.javascript.internal.CachedStages
private import semmle.javascript.dataflow.internal.Contents as Contents
private import sharedlib.SummaryTypeTracker as SummaryTypeTracker
private import FlowSteps
cached
@@ -29,6 +31,8 @@ private module Cached {
SharedTypeTrackingStep::loadStoreStep(_, _, _, this)
or
this = DataFlow::PseudoProperties::arrayLikeElement()
or
this instanceof Contents::Private::PropertyName
}
}
@@ -46,6 +50,12 @@ private module Cached {
LoadStoreStep(PropertyName fromProp, PropertyName toProp) {
SharedTypeTrackingStep::loadStoreStep(_, _, fromProp, toProp)
or
exists(DataFlow::ContentSet loadContent, DataFlow::ContentSet storeContent |
SummaryTypeTracker::basicLoadStoreStep(_, _, loadContent, storeContent) and
fromProp = loadContent.asPropertyName() and
toProp = storeContent.asPropertyName()
)
or
summarizedLoadStoreStep(_, _, fromProp, toProp)
} or
WithoutPropStep(PropertySet props) { SharedTypeTrackingStep::withoutPropStep(_, _, props) }
@@ -205,6 +215,21 @@ private module Cached {
succ = getACallbackSource(parameter).getParameter(i) and
summary = ReturnStep()
)
or
SummaryTypeTracker::levelStepNoCall(pred, succ) and summary = LevelStep()
or
exists(DataFlow::ContentSet content |
SummaryTypeTracker::basicLoadStep(pred, succ, content) and
summary = LoadStep(content.asPropertyName())
or
SummaryTypeTracker::basicStoreStep(pred, succ, content) and
summary = StoreStep(content.asPropertyName())
)
or
exists(DataFlow::ContentSet loadContent, DataFlow::ContentSet storeContent |
SummaryTypeTracker::basicLoadStoreStep(pred, succ, loadContent, storeContent) and
summary = LoadStoreStep(loadContent.asPropertyName(), storeContent.asPropertyName())
)
}
}

View File

@@ -0,0 +1,83 @@
private import semmle.javascript.Locations
private import codeql.typetracking.internal.SummaryTypeTracker
private import semmle.javascript.dataflow.internal.DataFlowPrivate as DataFlowPrivate
private import semmle.javascript.dataflow.FlowSummary as FlowSummary
private import FlowSummaryImpl as FlowSummaryImpl
private import DataFlowArg
private module SummaryFlowConfig implements Input {
import JSDataFlow
import FlowSummaryImpl::Public
import FlowSummaryImpl::Private
import FlowSummaryImpl::Private::SummaryComponent
class Content = DataFlow::ContentSet;
class ContentFilter extends Unit {
ContentFilter() { none() }
}
ContentFilter getFilterFromWithoutContentStep(Content content) { none() }
ContentFilter getFilterFromWithContentStep(Content content) { none() }
predicate singleton = SummaryComponentStack::singleton/1;
predicate push = SummaryComponentStack::push/2;
SummaryComponent return() {
result = SummaryComponent::return(DataFlowPrivate::MkNormalReturnKind())
}
Node argumentOf(Node call, SummaryComponent arg, boolean isPostUpdate) {
// Note: we cannot rely on DataFlowPrivate::DataFlowCall here because that depends on the call graph.
exists(ArgumentPosition apos, ParameterPosition ppos, Node argNode |
arg = argument(ppos) and
parameterMatch(ppos, apos) and
(
argNode = call.(DataFlow::InvokeNode).getArgument(apos.asPositional())
or
apos.isThis() and
argNode = call.(DataFlow::CallNode).getReceiver()
)
|
isPostUpdate = true and result = argNode.getPostUpdateNode()
or
isPostUpdate = false and result = argNode
)
}
Node parameterOf(Node callable, SummaryComponent param) {
exists(ArgumentPosition apos, ParameterPosition ppos, DataFlow::FunctionNode function |
param = parameter(apos) and
parameterMatch(ppos, apos) and
callable = function
|
result = function.getParameter(ppos.asPositional())
or
ppos.isThis() and
result = function.getReceiver()
)
}
Node returnOf(Node callable, SummaryComponent return) {
return = return() and
result = callable.(DataFlow::FunctionNode).getReturnNode()
}
class SummarizedCallable instanceof SummarizedCallableImpl {
predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
super.propagatesFlow(input, output, preservesValue, _)
}
string toString() { result = super.toString() }
}
Node callTo(SummarizedCallable callable) {
result = callable.(FlowSummary::SummarizedCallable).getACallSimple()
}
}
import SummaryFlow<SummaryFlowConfig>

View File

@@ -31,7 +31,7 @@ class At extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "at" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this].ArrayElement" and
output = "ReturnValue"
@@ -45,7 +45,7 @@ class Concat extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "concat" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this,0..].ArrayElement" and
output = "ReturnValue.ArrayElement"
@@ -61,7 +61,7 @@ class Slice extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "slice" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this].ArrayElement" and
output = "ReturnValue.ArrayElement"
@@ -80,7 +80,7 @@ class Entries extends SummarizedCallable {
result.getNumArgument() = 0
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this]." + ["MapKey", "SetElement"] and
@@ -97,7 +97,7 @@ class ForEach extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "forEach" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
/*
* array.forEach(callbackfn, thisArg)
@@ -128,7 +128,7 @@ class Keys extends SummarizedCallable {
result.getNumArgument() = 0
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this]." + ["MapKey", "SetElement"] and
output = "ReturnValue.IteratorElement"
@@ -143,7 +143,7 @@ class Values extends SummarizedCallable {
result.getNumArgument() = 0
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this]." + ["ArrayElement", "SetElement", "MapValue"] and
output = "ReturnValue.IteratorElement"

View File

@@ -99,7 +99,7 @@ class ArrayConstructorSummary extends SummarizedCallable {
result = arrayConstructorRef().getAnInvocation()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0..]" and
output = "ReturnValue.ArrayElement"
@@ -123,7 +123,7 @@ class Join extends SummarizedCallable {
result.getNumArgument() = [0, 1]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = false and
input = "Argument[this].ArrayElement" and
output = "ReturnValue"
@@ -135,7 +135,7 @@ class CopyWithin extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "copyWithin" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this].WithArrayElement" and
output = "ReturnValue"
@@ -154,7 +154,7 @@ class FlowIntoCallback extends SummarizedCallable {
result.getMethodName() = ["every", "findIndex", "findLastIndex", "some"]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this].ArrayElement" and
@@ -171,7 +171,7 @@ class Filter extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "filter" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this].ArrayElement" and
@@ -198,7 +198,7 @@ class Fill extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "fill" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0..]" and
output = ["ReturnValue.ArrayElement", "Argument[this].ArrayElement"]
@@ -210,7 +210,7 @@ class FindLike extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = ["find", "findLast"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this].ArrayElement" and
@@ -229,7 +229,7 @@ class FindLibrary extends SummarizedCallable {
result = DataFlow::moduleImport(["array.prototype.find", "array-find"]).getACall()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0].ArrayElement" and
@@ -257,7 +257,7 @@ class Flat extends SummarizedCallable {
)
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this]" + concat(int n | n in [0 .. depth] | ".ArrayElement")
@@ -277,7 +277,7 @@ class FlatMap extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "flatMap" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this].ArrayElement" and
@@ -309,7 +309,7 @@ class From1Arg extends SummarizedCallable {
result = arrayFromCall() and result.getNumArgument() = 1
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0].WithArrayElement" and
@@ -346,7 +346,7 @@ class FromManyArg extends SummarizedCallable {
result.getNumArgument() > 1
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0]." + ["ArrayElement", "SetElement", "IteratorElement"] and
@@ -380,7 +380,7 @@ class Map extends SummarizedCallable {
result.getMethodName() = "map"
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this].ArrayElement" and
@@ -405,7 +405,7 @@ class Of extends SummarizedCallable {
result = arrayConstructorRef().getAMemberCall("of")
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0..]" and
output = "ReturnValue.ArrayElement"
@@ -417,7 +417,7 @@ class Pop extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "pop" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this].ArrayElement" and
output = "ReturnValue"
@@ -429,7 +429,7 @@ class PushLike extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = ["push", "unshift"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0..]" and
output = "Argument[this].ArrayElement"
@@ -441,7 +441,7 @@ class ReduceLike extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = ["reduce", "reduceRight"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
/*
* Signatures:
@@ -470,7 +470,7 @@ class Reverse extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = ["reverse", "toReversed"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this].ArrayElement" and
output = "ReturnValue.ArrayElement"
@@ -482,7 +482,7 @@ class Shift extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "shift" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this].ArrayElement[0]" and
output = "ReturnValue"
@@ -500,7 +500,7 @@ class Sort extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = ["sort", "toSorted"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this].ArrayElement" and
@@ -517,7 +517,7 @@ class Splice extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "splice" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this].ArrayElement" and
@@ -534,7 +534,7 @@ class ToSpliced extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "toSpliced" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this].ArrayElement" and
@@ -551,7 +551,7 @@ class ArrayCoercionPackage extends FunctionalPackageSummary {
override string getAPackageName() { result = ["arrify", "array-ify"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0].WithArrayElement" and
@@ -573,7 +573,7 @@ class ArrayCopyingPackage extends FunctionalPackageSummary {
override string getAPackageName() { result = ["array-union", "array-uniq", "uniq"] }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0..].ArrayElement" and
output = "ReturnValue.ArrayElement"
@@ -587,7 +587,7 @@ class ArrayFlatteningPackage extends FunctionalPackageSummary {
result = ["array-flatten", "arr-flatten", "flatten", "array.prototype.flat"]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
// TODO: properly support these. For the moment we're just adding parity with the old model
preservesValue = false and
input = "Argument[0..]" and

View File

@@ -40,7 +40,7 @@ private class ExceptionFlowSummary extends SummarizedCallable, LibraryCallableIn
isCallback(result.getAnArgument().getALocalSource())
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0..].ReturnValue[exception]" and
output = "ReturnValue[exception]"

View File

@@ -16,7 +16,7 @@ class IteratorNext extends SummarizedCallable {
result.getNumArgument() = 0
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[this].IteratorElement" and

View File

@@ -12,7 +12,7 @@ private class JsonStringifySummary extends SummarizedCallable {
override DataFlow::InvokeNode getACall() { result instanceof JsonStringifyCall }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = false and
input = ["Argument[0]", "Argument[0].AnyMemberDeep"] and
output = "ReturnValue"

View File

@@ -15,7 +15,7 @@ class MapConstructor extends SummarizedCallable {
result = mapConstructorRef().getAnInstantiation()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0]." + ["ArrayElement", "SetElement", "IteratorElement"] + ".Member[0]" and
@@ -87,7 +87,7 @@ class MapGet extends SummarizedCallable {
result.getNumArgument() = 1
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[this].MapValue" and
output = "ReturnValue"
@@ -102,7 +102,7 @@ class MapSet extends SummarizedCallable {
result.getNumArgument() = 2
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = ["Argument[this].WithMapKey", "Argument[this].WithMapValue"] and
output = "ReturnValue"

View File

@@ -29,7 +29,7 @@ private class PromiseConstructor extends SummarizedCallable {
none()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
// TODO: when FlowSummaryImpl.qll supports these summaries, remove the workaround in PromiseConstructorWorkaround
@@ -58,7 +58,7 @@ module PromiseConstructorWorkaround {
promiseConstructorRef().getAnInstantiation().getCallback(0).getParameter(0).getACall()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0]" and
output = "Argument[function].Member[resolve-value]"
@@ -73,7 +73,7 @@ module PromiseConstructorWorkaround {
promiseConstructorRef().getAnInstantiation().getCallback(0).getParameter(1).getACall()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0]" and
output = "Argument[function].Member[reject-value]"
@@ -87,7 +87,7 @@ module PromiseConstructorWorkaround {
result = promiseConstructorRef().getAnInstantiation()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0].Parameter[0].Member[resolve-value]" and
@@ -111,7 +111,7 @@ private class PromiseThen2Arguments extends SummarizedCallable {
result.getNumArgument() = 2
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0,1].ReturnValue" and output = "ReturnValue.Awaited"
@@ -133,7 +133,7 @@ private class PromiseThen1Argument extends SummarizedCallable {
result.getNumArgument() = 1
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0].ReturnValue" and output = "ReturnValue.Awaited"
@@ -152,7 +152,7 @@ private class PromiseCatch extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "catch" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0].ReturnValue" and output = "ReturnValue.Awaited"
@@ -171,7 +171,7 @@ private class PromiseFinally extends SummarizedCallable {
override InstanceCall getACallSimple() { result.getMethodName() = "finally" }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0].ReturnValue.Awaited[error]" and output = "ReturnValue.Awaited[error]"
@@ -190,7 +190,7 @@ private class PromiseResolve extends SummarizedCallable {
result = promiseConstructorRef().getAMemberCall("resolve")
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0]" and
output = "ReturnValue.Awaited"
@@ -204,7 +204,7 @@ private class PromiseReject extends SummarizedCallable {
result = promiseConstructorRef().getAMemberCall("reject")
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0]" and
output = "ReturnValue.Awaited[error]"
@@ -218,7 +218,7 @@ private class PromiseAll extends SummarizedCallable {
result = promiseConstructorRef().getAMemberCall("all")
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
exists(string content | content = getAnArrayContent() |
input = "Argument[0]." + content + ".Awaited" and
@@ -242,7 +242,7 @@ private class PromiseAnyLike extends SummarizedCallable {
result = promiseConstructorRef().getAMemberCall(["any", "race", "firstFulfilled"])
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0].ArrayElement" and
output = "ReturnValue.Awaited"
@@ -258,7 +258,7 @@ private class PromiseAllSettled extends SummarizedCallable {
result = DataFlow::moduleImport("promise.allsettled").getACall()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
exists(string content | content = getAnArrayContent() |
input = "Argument[0]." + content + ".Awaited" and
@@ -277,7 +277,7 @@ private class BluebirdMapSeries extends SummarizedCallable {
result = promiseConstructorRef().getAMemberCall("mapSeries")
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0].Awaited.ArrayElement.Awaited" and
@@ -310,7 +310,7 @@ private class PromiseWithResolversLike extends SummarizedCallable {
result = promiseConstructorRef().getAMemberCall(["withResolver", "withResolvers", "defer"])
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
// TODO: not currently supported by FlowSummaryImpl.qll

View File

@@ -15,7 +15,7 @@ class SetConstructor extends SummarizedCallable {
result = setConstructorRef().getAnInstantiation()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
input = "Argument[0]." + ["ArrayElement", "SetElement", "IteratorElement"] and
@@ -38,7 +38,7 @@ class SetAdd extends SummarizedCallable {
result.getNumArgument() = 1
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
input = "Argument[0]" and
output = "Argument[this].SetElement"

View File

@@ -15,7 +15,7 @@ private class StringReplaceNoWildcard extends SummarizedCallable {
override StringReplaceCall getACall() { not result.hasRegExpContainingWildcard() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = false and
(
input = "Argument[this]" and
@@ -39,7 +39,7 @@ private class StringReplaceWithWildcard extends SummarizedCallable {
override StringReplaceCall getACall() { result.hasRegExpContainingWildcard() }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = false and
(
input = "Argument[this]" and
@@ -60,7 +60,7 @@ class StringSplit extends SummarizedCallable {
not result.getArgument(0).getStringValue() = ["#", "?"]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = false and
input = "Argument[this]" and
output = "ReturnValue.ArrayElement"
@@ -85,7 +85,7 @@ class StringSplitHashOrQuestionMark extends SummarizedCallable {
result.getArgument(0).getStringValue() = ["#", "?"]
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = false and
(
input = "Argument[this].OptionalBarrier[split-url-suffix]" and

View File

@@ -1,6 +1,6 @@
import javascript
import testUtilities.ConsistencyChecking
import Summaries
import testUtilities.InlineSummaries
DataFlow::CallNode getACall(string name) {
result.getCalleeName() = name

View File

@@ -0,0 +1,19 @@
/**
* Test that fails with a compilation error if `getACallSimple` depends on the call graph.
* To do this, we add a negative dependency from the call graph to `getACallSimple`.
*/
import javascript
import semmle.javascript.dataflow.internal.StepSummary
import semmle.javascript.dataflow.FlowSummary
class NegativeDependency extends DataFlow::SharedTypeTrackingStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
exists(SummarizedCallable callable |
not exists(callable.getACallSimple()) and
node1 = node2
)
}
}
select "pass"

View File

@@ -0,0 +1,55 @@
function m0() {
const x = source("m0.1");
sink(x); // $ track=m0.1
}
function m1() {
const fn = mkSummary("Argument[0]", "ReturnValue");
const obj = source("m1.1");
sink(fn(obj)); // $ track=m1.1
sink(fn(obj.p));
sink(fn(obj).p);
sink(fn({ p: obj }));
sink(fn({ p: obj }).q);
}
function m2() {
const fn = mkSummary("Argument[0].Member[p]", "ReturnValue");
const obj = source("m2.1");
sink(fn(obj));
sink(fn(obj.p));
sink(fn(obj).p);
sink(fn({ p: obj })); // $ track=m2.1
sink(fn({ p: obj }).q);
}
function m3() {
const fn = mkSummary("Argument[0]", "ReturnValue.Member[p]");
const obj = source("m3.1");
sink(fn(obj));
sink(fn(obj.p));
sink(fn(obj).p); // $ track=m3.1
sink(fn({ p: obj }));
sink(fn({ p: obj }).q);
}
function m4() {
const fn = mkSummary("Argument[0].Member[p]", "ReturnValue.Member[q]");
const obj = source("m4.1");
sink(fn(obj));
sink(fn(obj.p));
sink(fn(obj).p);
sink(fn({ p: obj }));
sink(fn({ p: obj }).q); // $ track=m4.1
}
function m5() {
// Store and read to a property that isn't mentioned anywhere in the source code.
const store = mkSummary("Argument[0]", "ReturnValue.Member[propOnlyMentionedInSummary]");
const read = mkSummary("Argument[0].Member[propOnlyMentionedInSummary]", "ReturnValue");
sink(read(store(source("m5.1")))); // $ track=m5.1
sink(read(source("m5.1")));
sink(store(source("m5.1")));
sink(store(read(source("m5.1"))));
}

View File

@@ -0,0 +1,2 @@
testFailures
failures

View File

@@ -0,0 +1,39 @@
import javascript
import testUtilities.InlineSummaries
import testUtilities.InlineExpectationsTest
private DataFlow::SourceNode typeTrack(DataFlow::TypeTracker t, string name) {
t.start() and
exists(DataFlow::CallNode call |
call.getCalleeName() = "source" and
name = call.getArgument(0).getStringValue() and
result = call
)
or
exists(DataFlow::TypeTracker t2 | result = typeTrack(t2, name).track(t2, t))
}
DataFlow::SourceNode typeTrack(string name) {
result = typeTrack(DataFlow::TypeTracker::end(), name)
}
module TestConfig implements TestSig {
string getARelevantTag() { result = "track" }
predicate hasActualResult(Location location, string element, string tag, string value) {
element = "" and
tag = "track" and
exists(DataFlow::CallNode call, DataFlow::Node arg |
call.getCalleeName() = "sink" and
arg = call.getArgument(0) and
typeTrack(value).flowsTo(arg) and
location = arg.getLocation()
)
}
predicate hasOptionalResult(Location location, string element, string tag, string value) {
none()
}
}
import MakeTest<TestConfig>

View File

@@ -11,11 +11,11 @@ class MkSummary extends SummarizedCallable {
mkSummary.getLocation().getStartLine()
}
override DataFlow::InvokeNode getACall() {
override DataFlow::InvokeNode getACallSimple() {
result = mkSummary.flow().(DataFlow::CallNode).getAnInvocation()
}
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
override predicate propagatesFlow(string input, string output, boolean preservesValue) {
preservesValue = true and
(
// mkSummary(input, output)