Merge pull request #5478 from asgerf/js/shared-flow-step

Approved by erik-krogh
This commit is contained in:
CodeQL CI
2021-03-24 10:58:30 +00:00
committed by GitHub
31 changed files with 627 additions and 466 deletions

View File

@@ -87,11 +87,6 @@ private DataFlow::Node goodRandom(DataFlow::TypeTracker t, DataFlow::SourceNode
or
exists(DataFlow::TypeTracker t2 | t = t2.smallstep(goodRandom(t2, source), result))
or
// re-using the collection steps for `Set`.
exists(DataFlow::TypeTracker t2 |
result = CollectionsTypeTracking::collectionStep(goodRandom(t2, source), t, t2)
)
or
InsecureRandomness::isAdditionalTaintStep(goodRandom(t.continue(), source), result) and
// bit shifts and multiplication by powers of two are generally used for constructing larger numbers from smaller numbers.
not exists(BinaryExpr binop | binop = result.asExpr() |

View File

@@ -15,17 +15,17 @@ predicate relevantStep(DataFlow::Node pred, DataFlow::Node succ) {
(
TaintTracking::sharedTaintStep(pred, succ)
or
any(DataFlow::AdditionalFlowStep cfg).step(pred, succ)
DataFlow::SharedFlowStep::step(pred, succ)
or
any(DataFlow::AdditionalFlowStep cfg).step(pred, succ, _, _)
DataFlow::SharedFlowStep::step(pred, succ, _, _)
or
any(DataFlow::AdditionalFlowStep cfg).loadStep(pred, succ, _)
DataFlow::SharedFlowStep::loadStep(pred, succ, _)
or
any(DataFlow::AdditionalFlowStep cfg).storeStep(pred, succ, _)
DataFlow::SharedFlowStep::storeStep(pred, succ, _)
or
any(DataFlow::AdditionalFlowStep cfg).loadStoreStep(pred, succ, _, _)
DataFlow::SharedFlowStep::loadStoreStep(pred, succ, _, _)
or
any(DataFlow::AdditionalFlowStep cfg).loadStoreStep(pred, succ, _)
DataFlow::SharedFlowStep::loadStoreStep(pred, succ, _)
) and
not pred.getFile() instanceof IgnoredFile and
not succ.getFile() instanceof IgnoredFile

View File

@@ -84,16 +84,17 @@ private module ArrayDataFlow {
* A step modelling the creation of an Array using the `Array.from(x)` method.
* The step copies the elements of the argument (set, array, or iterator elements) into the resulting array.
*/
private class ArrayFrom extends DataFlow::AdditionalFlowStep, DataFlow::CallNode {
ArrayFrom() { this = DataFlow::globalVarRef("Array").getAMemberCall("from") }
private class ArrayFrom extends DataFlow::SharedFlowStep {
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
) {
pred = this.getArgument(0) and
succ = this and
fromProp = arrayLikeElement() and
toProp = arrayElement()
exists(DataFlow::CallNode call |
call = DataFlow::globalVarRef("Array").getAMemberCall("from") and
pred = call.getArgument(0) and
succ = call and
fromProp = arrayLikeElement() and
toProp = arrayElement()
)
}
}
@@ -103,55 +104,45 @@ private module ArrayDataFlow {
*
* Such a step can occur both with the `push` and `unshift` methods, or when creating a new array.
*/
private class ArrayCopySpread extends DataFlow::AdditionalFlowStep {
DataFlow::Node spreadArgument; // the spread argument containing the elements to be copied.
DataFlow::Node base; // the object where the elements should be copied to.
ArrayCopySpread() {
exists(DataFlow::MethodCallNode mcn | mcn = this |
mcn.getMethodName() = ["push", "unshift"] and
spreadArgument = mcn.getASpreadArgument() and
base = mcn.getReceiver().getALocalSource()
)
or
spreadArgument = this.(DataFlow::ArrayCreationNode).getASpreadArgument() and
base = this
}
private class ArrayCopySpread extends DataFlow::SharedFlowStep {
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
) {
pred = spreadArgument and
succ = base and
fromProp = arrayLikeElement() and
toProp = arrayElement()
toProp = arrayElement() and
(
exists(DataFlow::MethodCallNode mcn |
mcn.getMethodName() = ["push", "unshift"] and
pred = mcn.getASpreadArgument() and
succ = mcn.getReceiver().getALocalSource()
)
or
pred = succ.(DataFlow::ArrayCreationNode).getASpreadArgument()
)
}
}
/**
* A step for storing an element on an array using `arr.push(e)` or `arr.unshift(e)`.
*/
private class ArrayAppendStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
ArrayAppendStep() {
this.getMethodName() = "push" or
this.getMethodName() = "unshift"
}
private class ArrayAppendStep extends DataFlow::SharedFlowStep {
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
prop = arrayElement() and
element = this.getAnArgument() and
obj.getAMethodCall() = this
exists(DataFlow::MethodCallNode call |
call.getMethodName() = ["push", "unshift"] and
element = call.getAnArgument() and
obj.getAMethodCall() = call
)
}
}
/**
* A step for reading/writing an element from an array inside a for-loop.
* E.g. a read from `foo[i]` to `bar` in `for(var i = 0; i < arr.length; i++) {bar = foo[i]}`.
* A node that reads or writes an element from an array inside a for-loop.
*/
private class ArrayIndexingStep extends DataFlow::AdditionalFlowStep, DataFlow::Node {
private class ArrayIndexingAccess extends DataFlow::Node {
DataFlow::PropRef read;
ArrayIndexingStep() {
ArrayIndexingAccess() {
read = this and
TTNumber() =
unique(InferredType type | type = read.getPropertyNameExpr().flow().analyze().getAType()) and
@@ -162,17 +153,27 @@ private module ArrayDataFlow {
i.getVariable().getADefinition().(VariableDeclarator).getDeclStmt() = init
)
}
}
/**
* A step for reading/writing an element from an array inside a for-loop.
* E.g. a read from `foo[i]` to `bar` in `for(var i = 0; i < arr.length; i++) {bar = foo[i]}`.
*/
private class ArrayIndexingStep extends DataFlow::SharedFlowStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
prop = arrayElement() and
obj = this.(DataFlow::PropRead).getBase() and
element = this
exists(ArrayIndexingAccess access |
prop = arrayElement() and
obj = access.(DataFlow::PropRead).getBase() and
element = access
)
}
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
prop = arrayElement() and
element = this.(DataFlow::PropWrite).getRhs() and
this = obj.getAPropertyWrite()
exists(ArrayIndexingAccess access |
prop = arrayElement() and
element = access.(DataFlow::PropWrite).getRhs() and
access = obj.getAPropertyWrite()
)
}
}
@@ -180,16 +181,14 @@ private module ArrayDataFlow {
* A step for retrieving an element from an array using `.pop()` or `.shift()`.
* E.g. `array.pop()`.
*/
private class ArrayPopStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
ArrayPopStep() {
getMethodName() = "pop" or
getMethodName() = "shift"
}
private class ArrayPopStep extends DataFlow::SharedFlowStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
prop = arrayElement() and
obj = this.getReceiver() and
element = this
exists(DataFlow::MethodCallNode call |
call.getMethodName() = ["pop", "shift"] and
prop = arrayElement() and
obj = call.getReceiver() and
element = call
)
}
}
@@ -235,12 +234,12 @@ private module ArrayDataFlow {
/**
* A step for creating an array and storing the elements in the array.
*/
private class ArrayCreationStep extends DataFlow::AdditionalFlowStep, DataFlow::ArrayCreationNode {
private class ArrayCreationStep extends DataFlow::SharedFlowStep {
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
exists(int i |
element = this.getElement(i) and
obj = this and
if this = any(PromiseAllCreation c).getArrayNode()
exists(DataFlow::ArrayCreationNode array, int i |
element = array.getElement(i) and
obj = array and
if array = any(PromiseAllCreation c).getArrayNode()
then prop = arrayElement(i)
else prop = arrayElement()
)
@@ -251,13 +250,14 @@ private module ArrayDataFlow {
* A step modelling that `splice` can insert elements into an array.
* For example in `array.splice(i, del, e)`: if `e` is tainted, then so is `array
*/
private class ArraySpliceStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
ArraySpliceStep() { this.getMethodName() = "splice" }
private class ArraySpliceStep extends DataFlow::SharedFlowStep {
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
prop = arrayElement() and
element = getArgument(2) and
this = obj.getAMethodCall()
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "splice" and
prop = arrayElement() and
element = call.getArgument(2) and
call = obj.getAMethodCall()
)
}
}
@@ -265,42 +265,27 @@ private module ArrayDataFlow {
* A step for modelling `concat`.
* For example in `e = arr1.concat(arr2, arr3)`: if any of the `arr` is tainted, then so is `e`.
*/
private class ArrayConcatStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
ArrayConcatStep() { this.getMethodName() = "concat" }
private class ArrayConcatStep extends DataFlow::SharedFlowStep {
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = arrayElement() and
(pred = this.getReceiver() or pred = this.getAnArgument()) and
succ = this
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "concat" and
prop = arrayElement() and
(pred = call.getReceiver() or pred = call.getAnArgument()) and
succ = call
)
}
}
/**
* A step for modelling that elements from an array `arr` also appear in the result from calling `slice`/`splice`/`filter`.
*/
private class ArraySliceStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
ArraySliceStep() {
this.getMethodName() = "slice" or
this.getMethodName() = "splice" or
this.getMethodName() = "filter"
}
private class ArraySliceStep extends DataFlow::SharedFlowStep {
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
prop = arrayElement() and
pred = this.getReceiver() and
succ = this
}
}
/**
* A step for modelling `for of` iteration on arrays.
*/
private class ForOfStep extends PreCallGraphStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node e, string prop) {
exists(ForOfStmt forOf |
obj = forOf.getIterationDomain().flow() and
e = DataFlow::lvalueNode(forOf.getLValue()) and
prop = arrayElement()
exists(DataFlow::MethodCallNode call |
call.getMethodName() = ["slice", "splice", "filter"] and
prop = arrayElement() and
pred = call.getReceiver() and
succ = call
)
}
}

View File

@@ -6,32 +6,30 @@
import javascript
private import semmle.javascript.dataflow.internal.StepSummary
private import semmle.javascript.dataflow.internal.PreCallGraphStep
private import DataFlow::PseudoProperties
/**
* A pseudo-property used in a data-flow/type-tracking step for collections.
* DEPRECATED. Exists only to support other deprecated elements.
*
* By extending `TypeTrackingPseudoProperty` the class enables the use of the collection related pseudo-properties in type-tracking predicates.
* Type-tracking now automatically determines the set of pseudo-properties to include
* ased on which properties are contributed by `SharedTaintStep`s.
*/
private class PseudoProperty extends TypeTrackingPseudoProperty {
deprecated private class PseudoProperty extends string {
PseudoProperty() {
this = [arrayLikeElement(), "1"] or // the "1" is required for the `ForOfStep`.
this = any(CollectionDataFlow::MapSet step).getAPseudoProperty()
}
override PseudoProperty getLoadStoreToProp() {
exists(CollectionFlowStep step | step.loadStore(_, _, this, result))
this =
[
mapValue(any(DataFlow::CallNode c | c.getCalleeName() = "set").getArgument(0)),
mapValueAll()
]
}
}
/**
* An `AdditionalFlowStep` used to model a data-flow step related to standard library collections.
*
* The `loadStep`/`storeStep`/`loadStoreStep` methods are overloaded such that the new predicates
* `load`/`store`/`loadStore` can be used in the `CollectionsTypeTracking` module.
* (Thereby avoiding naming conflicts with a "cousin" `AdditionalFlowStep` implementation.)
* DEPRECATED. Use `SharedFlowStep` or `SharedTaintTrackingStep` instead.
*/
abstract class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
abstract deprecated class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
final override predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
final override predicate step(
@@ -84,27 +82,28 @@ abstract class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
}
/**
* Provides predicates and clases for type-tracking collections.
* DEPRECATED. These steps are now included in the default type tracking steps,
* in most cases one can simply use those instead.
*/
module CollectionsTypeTracking {
deprecated module CollectionsTypeTracking {
/**
* Gets the result from a single step through a collection, from `pred` to `result` summarized by `summary`.
*/
pragma[inline]
DataFlow::SourceNode collectionStep(DataFlow::Node pred, StepSummary summary) {
exists(CollectionFlowStep step, PseudoProperty field |
exists(PseudoProperty field |
summary = LoadStep(field) and
step.load(pred, result, field) and
DataFlow::SharedTypeTrackingStep::loadStep(pred, result, field) and
not field = mapValueUnknownKey() // prune unknown reads in type-tracking
or
summary = StoreStep(field) and
step.store(pred, result, field)
DataFlow::SharedTypeTrackingStep::storeStep(pred, result, field)
or
summary = CopyStep(field) and
step.loadStore(pred, result, field)
DataFlow::SharedTypeTrackingStep::loadStoreStep(pred, result, field)
or
exists(PseudoProperty toField | summary = LoadStoreStep(field, toField) |
step.loadStore(pred, result, field, toField)
DataFlow::SharedTypeTrackingStep::loadStoreStep(pred, result, field, toField)
)
)
}
@@ -129,74 +128,71 @@ private module CollectionDataFlow {
/**
* A step for `Set.add()` method, which adds an element to a Set.
*/
private class SetAdd extends CollectionFlowStep, DataFlow::MethodCallNode {
SetAdd() { this.getMethodName() = "add" }
override predicate store(DataFlow::Node element, DataFlow::SourceNode obj, PseudoProperty prop) {
this = obj.getAMethodCall() and
element = this.getArgument(0) and
prop = setElement()
private class SetAdd extends PreCallGraphStep {
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
exists(DataFlow::MethodCallNode call |
call = obj.getAMethodCall("add") and
element = call.getArgument(0) and
prop = setElement()
)
}
}
/**
* A step for the `Set` constructor, which copies any elements from the first argument into the resulting set.
*/
private class SetConstructor extends CollectionFlowStep, DataFlow::NewNode {
SetConstructor() { this = DataFlow::globalVarRef("Set").getAnInstantiation() }
override predicate loadStore(
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty fromProp, PseudoProperty toProp
private class SetConstructor extends PreCallGraphStep {
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
) {
pred = this.getArgument(0) and
succ = this and
fromProp = arrayLikeElement() and
toProp = setElement()
exists(DataFlow::NewNode invoke |
invoke = DataFlow::globalVarRef("Set").getAnInstantiation() and
pred = invoke.getArgument(0) and
succ = invoke and
fromProp = arrayLikeElement() and
toProp = setElement()
)
}
}
/**
* A step for a `for of` statement on a Map, Set, or Iterator.
* For Sets and iterators the l-value are the elements of the set/iterator.
* For Maps the l-value is a tuple containing a key and a value.
* A step for modelling `for of` iteration on arrays, maps, sets, and iterators.
*
* For sets and iterators the l-value are the elements of the set/iterator.
* For maps the l-value is a tuple containing a key and a value.
*/
// This is partially duplicated behavior with the `for of` step for Arrays (`ArrayDataFlow::ForOfStep`).
// This duplication is required for the type-tracking steps defined in `CollectionsTypeTracking`.
private class ForOfStep extends CollectionFlowStep, DataFlow::ValueNode {
ForOfStmt forOf;
DataFlow::Node element;
ForOfStep() {
this.asExpr() = forOf.getIterationDomain() and
element = DataFlow::lvalueNode(forOf.getLValue())
private class ForOfStep extends PreCallGraphStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node e, string prop) {
exists(ForOfStmt forOf |
obj = forOf.getIterationDomain().flow() and
e = DataFlow::lvalueNode(forOf.getLValue()) and
prop = arrayLikeElement()
)
}
override predicate load(DataFlow::Node obj, DataFlow::Node e, PseudoProperty prop) {
obj = this and
e = element and
prop = arrayLikeElement()
}
override predicate loadStore(
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty fromProp, PseudoProperty toProp
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
) {
pred = this and
succ = element and
fromProp = mapValueAll() and
toProp = "1"
exists(ForOfStmt forOf |
pred = forOf.getIterationDomain().flow() and
succ = DataFlow::lvalueNode(forOf.getLValue()) and
fromProp = mapValueAll() and
toProp = "1"
)
}
}
/**
* A step for a call to `forEach` on a Set or Map.
*/
private class SetMapForEach extends CollectionFlowStep, DataFlow::MethodCallNode {
SetMapForEach() { this.getMethodName() = "forEach" }
override predicate load(DataFlow::Node obj, DataFlow::Node element, PseudoProperty prop) {
obj = this.getReceiver() and
element = this.getCallback(0).getParameter(0) and
prop = [setElement(), mapValueAll()]
private class SetMapForEach extends PreCallGraphStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "forEach" and
obj = call.getReceiver() and
element = call.getCallback(0).getParameter(0) and
prop = [setElement(), mapValueAll()]
)
}
}
@@ -204,14 +200,15 @@ private module CollectionDataFlow {
* A call to the `get` method on a Map.
* If the key of the call to `get` has a known string value, then only the value corresponding to that key will be retrieved. (The known string value is encoded as part of the pseudo-property)
*/
private class MapGet extends CollectionFlowStep, DataFlow::MethodCallNode {
MapGet() { this.getMethodName() = "get" }
override predicate load(DataFlow::Node obj, DataFlow::Node element, PseudoProperty prop) {
obj = this.getReceiver() and
element = this and
// reading the join of known and unknown values
(prop = mapValue(this.getArgument(0)) or prop = mapValueUnknownKey())
private class MapGet extends PreCallGraphStep {
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "get" and
obj = call.getReceiver() and
element = call and
// reading the join of known and unknown values
(prop = mapValue(call.getArgument(0)) or prop = mapValueUnknownKey())
)
}
}
@@ -223,56 +220,47 @@ private module CollectionDataFlow {
* Otherwise the value will be stored into a pseudo-property corresponding to values with unknown keys.
* The value will additionally be stored into a pseudo-property corresponding to all values.
*/
class MapSet extends CollectionFlowStep, DataFlow::MethodCallNode {
MapSet() { this.getMethodName() = "set" }
override predicate store(DataFlow::Node element, DataFlow::SourceNode obj, PseudoProperty prop) {
this = obj.getAMethodCall() and
element = this.getArgument(1) and
prop = getAPseudoProperty()
class MapSet extends PreCallGraphStep {
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
exists(DataFlow::MethodCallNode call |
call = obj.getAMethodCall("set") and
element = call.getArgument(1) and
prop = [mapValue(call.getArgument(0)), mapValueAll()]
)
}
/**
* Gets a pseudo-property used to store an element in a map.
* The pseudo-property represents both values where the key is a known string value (which is encoded in the pseudo-property),
* and values where the key is unknown.
*
* Additionally, all elements are stored into the pseudo-property `mapValueAll()`.
*
* The return-type is `string` as this predicate is used to define which pseudo-properties exist.
*/
string getAPseudoProperty() { result = [mapValue(this.getArgument(0)), mapValueAll()] }
}
/**
* A step for a call to `values` on a Map or a Set.
*/
private class MapAndSetValues extends CollectionFlowStep, DataFlow::MethodCallNode {
MapAndSetValues() { this.getMethodName() = "values" }
override predicate loadStore(
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty fromProp, PseudoProperty toProp
private class MapAndSetValues extends PreCallGraphStep {
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
) {
pred = this.getReceiver() and
succ = this and
fromProp = [mapValueAll(), setElement()] and
toProp = iteratorElement()
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "values" and
pred = call.getReceiver() and
succ = call and
fromProp = [mapValueAll(), setElement()] and
toProp = iteratorElement()
)
}
}
/**
* A step for a call to `keys` on a Set.
*/
private class SetKeys extends CollectionFlowStep, DataFlow::MethodCallNode {
SetKeys() { this.getMethodName() = "keys" }
override predicate loadStore(
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty fromProp, PseudoProperty toProp
private class SetKeys extends PreCallGraphStep {
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
) {
pred = this.getReceiver() and
succ = this and
fromProp = setElement() and
toProp = iteratorElement()
exists(DataFlow::MethodCallNode call |
call.getMethodName() = "keys" and
pred = call.getReceiver() and
succ = call and
fromProp = setElement() and
toProp = iteratorElement()
)
}
}
}

View File

@@ -305,6 +305,7 @@ module AccessPath {
* }
* ```
*/
pragma[inline]
DataFlow::Node getAReferenceTo(Root root, string path) {
path = fromReference(result, root) and
not root.isGlobal()
@@ -327,6 +328,7 @@ module AccessPath {
* })(NS = NS || {});
* ```
*/
pragma[inline]
DataFlow::Node getAReferenceTo(string path) {
path = fromReference(result, DataFlow::globalAccessPathRootPseudoNode())
}
@@ -347,6 +349,7 @@ module AccessPath {
* }
* ```
*/
pragma[inline]
DataFlow::Node getAnAssignmentTo(Root root, string path) {
path = fromRhs(result, root) and
not root.isGlobal()
@@ -367,6 +370,7 @@ module AccessPath {
* })(foo = foo || {});
* ```
*/
pragma[inline]
DataFlow::Node getAnAssignmentTo(string path) {
path = fromRhs(result, DataFlow::globalAccessPathRootPseudoNode())
}
@@ -376,6 +380,7 @@ module AccessPath {
*
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
*/
pragma[inline]
DataFlow::Node getAReferenceOrAssignmentTo(string path) {
result = getAReferenceTo(path)
or
@@ -387,6 +392,7 @@ module AccessPath {
*
* See `getAReferenceTo` and `getAnAssignmentTo` for more details.
*/
pragma[inline]
DataFlow::Node getAReferenceOrAssignmentTo(Root root, string path) {
result = getAReferenceTo(root, path)
or

View File

@@ -214,13 +214,6 @@ module PromiseTypeTracking {
result = PromiseTypeTracking::promiseStep(mid, summary)
)
}
/**
* A class enabling the use of the `resolveField` as a pseudo-property in type-tracking predicates.
*/
private class ResolveFieldAsTypeTrackingProperty extends TypeTrackingPseudoProperty {
ResolveFieldAsTypeTrackingProperty() { this = Promises::valueProp() }
}
}
private import semmle.javascript.dataflow.internal.PreCallGraphStep

View File

@@ -74,23 +74,13 @@ private class ArrayIterationCallbackAsPartialInvoke extends DataFlow::PartialInv
* A flow step propagating the exception thrown from a callback to a method whose name coincides
* a built-in Array iteration method, such as `forEach` or `map`.
*/
private class IteratorExceptionStep extends DataFlow::MethodCallNode, DataFlow::AdditionalFlowStep {
IteratorExceptionStep() {
exists(string name | name = getMethodName() |
name = "forEach" or
name = "each" or
name = "map" or
name = "filter" or
name = "some" or
name = "every" or
name = "fold" or
name = "reduce"
)
}
private class IteratorExceptionStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = getAnArgument().(DataFlow::FunctionNode).getExceptionalReturn() and
succ = this.getExceptionalReturn()
exists(DataFlow::MethodCallNode call |
call.getMethodName() = ["forEach", "each", "map", "filter", "some", "every", "fold", "reduce"] and
pred = call.getAnArgument().(DataFlow::FunctionNode).getExceptionalReturn() and
succ = call.getExceptionalReturn()
)
}
}

View File

@@ -544,6 +544,12 @@ abstract class LabeledBarrierGuardNode extends BarrierGuardNode {
}
/**
* DEPRECATED. Subclasses should extend `SharedFlowStep` instead, unless the subclass
* is part of a query, in which case it should be moved into the `isAdditionalFlowStep` predicate
* of the relevant data-flow configuration.
* Other uses of the predicate in this class should instead reference the predicates in the
* `SharedFlowStep::` module, such as `SharedFlowStep::step`.
*
* A data flow edge that should be added to all data flow configurations in
* addition to standard data flow edges.
*
@@ -551,19 +557,19 @@ abstract class LabeledBarrierGuardNode extends BarrierGuardNode {
* of the standard library. Override `Configuration::isAdditionalFlowStep`
* for analysis-specific flow steps.
*/
cached
abstract class AdditionalFlowStep extends DataFlow::Node {
deprecated class AdditionalFlowStep = LegacyAdditionalFlowStep;
// Internal version of AdditionalFlowStep that we can reference without deprecation warnings.
abstract private class LegacyAdditionalFlowStep extends DataFlow::Node {
/**
* Holds if `pred` &rarr; `succ` should be considered a data flow edge.
*/
cached
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
/**
* Holds if `pred` &rarr; `succ` should be considered a data flow edge
* transforming values with label `predlbl` to have label `succlbl`.
*/
cached
predicate step(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
DataFlow::FlowLabel succlbl
@@ -577,7 +583,6 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
* The object `succ` must be a `DataFlow::SourceNode` for the object wherein the value is stored.
*/
cached
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
/**
@@ -585,7 +590,6 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
*
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
cached
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
@@ -593,7 +597,6 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
*
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
cached
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
@@ -601,12 +604,10 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
*
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
*/
cached
predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
loadProp = storeProp and
loadStoreStep(pred, succ, loadProp)
none()
}
}
@@ -636,28 +637,133 @@ class SharedFlowStep extends Unit {
) {
none()
}
/**
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
* The object `succ` must be a `DataFlow::SourceNode` for the object wherein the value is stored.
*/
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
/**
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
*/
predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
none()
}
}
/**
* Contributes subclasses of `SharedFlowStep` to `AdditionalFlowStep`.
*
* This is a placeholder until we migrate to the `SharedFlowStep` class and deprecate `AdditionalFlowStep`.
* Contains predicates for accessing the steps contributed by `SharedFlowStep` subclasses.
*/
private class SharedStepAsAdditionalFlowStep extends AdditionalFlowStep {
SharedStepAsAdditionalFlowStep() {
any(SharedFlowStep st).step(_, this) or
any(SharedFlowStep st).step(_, this, _, _)
cached
module SharedFlowStep {
cached
private module Internal {
// Forces this to be part of the `FlowSteps` stage.
// We use a public predicate in a private module to avoid warnings about this being unused.
cached
predicate forceStage() { Stages::FlowSteps::ref() }
}
/**
* Holds if `pred` &rarr; `succ` should be considered a data flow edge.
*/
cached
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
any(SharedFlowStep s).step(pred, succ)
}
/**
* Holds if `pred` &rarr; `succ` should be considered a data flow edge
* transforming values with label `predlbl` to have label `succlbl`.
*/
cached
predicate step(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
DataFlow::FlowLabel succlbl
) {
any(SharedFlowStep s).step(pred, succ, predlbl, succlbl)
}
/**
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
* The object `succ` must be a `DataFlow::SourceNode` for the object wherein the value is stored.
*/
cached
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(SharedFlowStep s).storeStep(pred, succ, prop)
}
/**
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
*/
cached
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(SharedFlowStep s).loadStep(pred, succ, prop)
}
/**
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
*/
cached
predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(SharedFlowStep s).loadStoreStep(pred, succ, prop)
}
/**
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
*/
cached
predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
any(SharedFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
}
}
/**
* Contributes subclasses of `AdditionalFlowStep` to `SharedFlowStep`.
*/
private class AdditionalFlowStepAsSharedStep extends SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
any(SharedFlowStep st).step(pred, succ) and succ = this
any(LegacyAdditionalFlowStep s).step(pred, succ)
}
override predicate step(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::FlowLabel predlbl,
DataFlow::FlowLabel succlbl
) {
any(SharedFlowStep st).step(pred, succ, predlbl, succlbl) and succ = this
any(LegacyAdditionalFlowStep s).step(pred, succ, predlbl, succlbl)
}
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(LegacyAdditionalFlowStep s).storeStep(pred, succ, prop)
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(LegacyAdditionalFlowStep s).loadStep(pred, succ, prop)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(LegacyAdditionalFlowStep s).loadStoreStep(pred, succ, prop)
}
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
any(LegacyAdditionalFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
}
}
@@ -666,7 +772,7 @@ private class SharedStepAsAdditionalFlowStep extends AdditionalFlowStep {
*
* A pseudo-property represents the location where some value is stored in an object.
*
* For use with load/store steps in `DataFlow::AdditionalFlowStep` and TypeTracking.
* For use with load/store steps in `DataFlow::SharedFlowStep` and TypeTracking.
*/
module PseudoProperties {
bindingset[s]
@@ -782,13 +888,12 @@ abstract class AdditionalSink extends DataFlow::Node {
* Additional flow step to model flow from import specifiers into the SSA variable
* corresponding to the imported variable.
*/
private class FlowStepThroughImport extends AdditionalFlowStep, DataFlow::ValueNode {
override ImportSpecifier astNode;
private class FlowStepThroughImport extends SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
Stages::FlowSteps::ref() and
pred = this and
succ = DataFlow::ssaDefinitionNode(SSA::definition(astNode))
exists(ImportSpecifier specifier |
pred = DataFlow::valueNode(specifier) and
succ = DataFlow::ssaDefinitionNode(SSA::definition(specifier))
)
}
}
@@ -1202,7 +1307,7 @@ private predicate reachesReturn(
private predicate isAdditionalLoadStep(
DataFlow::Node pred, DataFlow::Node succ, string prop, DataFlow::Configuration cfg
) {
any(AdditionalFlowStep s).loadStep(pred, succ, prop)
SharedFlowStep::loadStep(pred, succ, prop)
or
cfg.isAdditionalLoadStep(pred, succ, prop)
}
@@ -1213,7 +1318,7 @@ private predicate isAdditionalLoadStep(
private predicate isAdditionalStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string prop, DataFlow::Configuration cfg
) {
any(AdditionalFlowStep s).storeStep(pred, succ, prop)
SharedFlowStep::storeStep(pred, succ, prop)
or
cfg.isAdditionalStoreStep(pred, succ, prop)
}
@@ -1225,13 +1330,13 @@ private predicate isAdditionalLoadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp,
DataFlow::Configuration cfg
) {
any(AdditionalFlowStep s).loadStoreStep(pred, succ, loadProp, storeProp)
SharedFlowStep::loadStoreStep(pred, succ, loadProp, storeProp)
or
cfg.isAdditionalLoadStoreStep(pred, succ, loadProp, storeProp)
or
loadProp = storeProp and
(
any(AdditionalFlowStep s).loadStoreStep(pred, succ, loadProp)
SharedFlowStep::loadStoreStep(pred, succ, loadProp)
or
cfg.isAdditionalLoadStoreStep(pred, succ, loadProp)
)

View File

@@ -828,13 +828,13 @@ module TaintTracking {
/**
* A taint propagating data flow edge arising from URL parameter parsing.
*/
private class UrlSearchParamsTaintStep extends DataFlow::AdditionalFlowStep, DataFlow::ValueNode {
private class UrlSearchParamsTaintStep extends DataFlow::SharedFlowStep {
/**
* Holds if `succ` is a `URLSearchParams` providing access to the
* parameters encoded in `pred`.
*/
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
isUrlSearchParams(succ, pred) and succ = this
isUrlSearchParams(succ, pred)
}
/**
@@ -847,17 +847,14 @@ module TaintTracking {
* which can be accessed using a `get` or `getAll` call. (See getableUrlPseudoProperty())
*/
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
succ = this and
(
prop = ["searchParams", "hash", "search", hiddenUrlPseudoProperty()] and
exists(DataFlow::NewNode newUrl | succ = newUrl |
newUrl = DataFlow::globalVarRef("URL").getAnInstantiation() and
pred = newUrl.getArgument(0)
)
or
prop = getableUrlPseudoProperty() and
isUrlSearchParams(succ, pred)
prop = ["searchParams", "hash", "search", hiddenUrlPseudoProperty()] and
exists(DataFlow::NewNode newUrl | succ = newUrl |
newUrl = DataFlow::globalVarRef("URL").getAnInstantiation() and
pred = newUrl.getArgument(0)
)
or
prop = getableUrlPseudoProperty() and
isUrlSearchParams(succ, pred)
}
/**
@@ -869,7 +866,6 @@ module TaintTracking {
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
succ = this and
loadProp = hiddenUrlPseudoProperty() and
storeProp = getableUrlPseudoProperty() and
exists(DataFlow::PropRead read | read = succ |
@@ -884,7 +880,6 @@ module TaintTracking {
* This step is used to load the value stored in the pseudo-property `getableUrlPseudoProperty()`.
*/
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
succ = this and
prop = getableUrlPseudoProperty() and
// this is a call to `get` or `getAll` on a `URLSearchParams` object
exists(string m, DataFlow::MethodCallNode call | call = succ |

View File

@@ -102,7 +102,7 @@ private module NodeTracking {
predicate localFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
pred = succ.getAPredecessor()
or
any(DataFlow::AdditionalFlowStep afs).step(pred, succ)
DataFlow::SharedFlowStep::step(pred, succ)
or
localExceptionStep(pred, succ)
}

View File

@@ -9,6 +9,7 @@
private import javascript
private import internal.FlowSteps
private import internal.StepSummary
private import internal.Unit
private import semmle.javascript.internal.CachedStages
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)
@@ -328,6 +329,89 @@ module TypeBackTracker {
}
/**
* A data flow edge that should be followed by type tracking.
*
* Unlike `SharedFlowStep`, this type of edge does not affect
* the local data flow graph, and is not used by data-flow configurations.
*
* Note: For performance reasons, all subclasses of this class should be part
* of the standard library. For query-specific steps, consider including the
* custom steps in the type-tracking predicate itself.
*/
class SharedTypeTrackingStep extends Unit {
/**
* Holds if type-tracking should step from `pred` to `succ`.
*/
predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
/**
* Holds if type-tracking should step from `pred` into the `prop` property of `succ`.
*/
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
/**
* Holds if type-tracking should step from the `prop` property of `pred` to `succ`.
*/
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
/**
* Holds if type-tracking should step from the `prop` property of `pred` to the same property in `succ`.
*/
predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
/**
* Holds if type-tracking should step from the `loadProp` property of `pred` to the `storeProp` property in `succ`.
*/
predicate loadStoreStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string loadProp, string storeProp
) {
none()
}
}
/** Provides access to the steps contributed by subclasses of `SharedTypeTrackingStep`. */
module SharedTypeTrackingStep {
/**
* Holds if type-tracking should step from `pred` to `succ`.
*/
predicate step(DataFlow::Node pred, DataFlow::Node succ) {
any(SharedTypeTrackingStep s).step(pred, succ)
}
/**
* Holds if type-tracking should step from `pred` into the `prop` property of `succ`.
*/
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(SharedTypeTrackingStep s).storeStep(pred, succ, prop)
}
/**
* Holds if type-tracking should step from the `prop` property of `pred` to `succ`.
*/
predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(SharedTypeTrackingStep s).loadStep(pred, succ, prop)
}
/**
* Holds if type-tracking should step from the `prop` property of `pred` to the same property in `succ`.
*/
predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(SharedTypeTrackingStep s).loadStoreStep(pred, succ, prop)
}
/**
* Holds if type-tracking should step from the `loadProp` property of `pred` to the `storeProp` property in `succ`.
*/
predicate loadStoreStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string loadProp, string storeProp
) {
any(SharedTypeTrackingStep s).loadStoreStep(pred, succ, loadProp, storeProp)
}
}
/**
* DEPRECATED. Use `SharedTypeTrackingStep` instead.
*
* A data flow edge that should be followed by type tracking.
*
* Unlike `AdditionalFlowStep`, this type of edge does not affect
@@ -337,7 +421,10 @@ module TypeBackTracker {
* of the standard library. For query-specific steps, consider including the
* custom steps in the type-tracking predicate itself.
*/
abstract class AdditionalTypeTrackingStep extends DataFlow::Node {
deprecated class AdditionalTypeTrackingStep = LegacyTypeTrackingStep;
// Internal version of AdditionalTypeTrackingStep that we can reference without deprecation warnings.
abstract private class LegacyTypeTrackingStep extends DataFlow::Node {
/**
* Holds if type-tracking should step from `pred` to `succ`.
*/
@@ -358,3 +445,21 @@ abstract class AdditionalTypeTrackingStep extends DataFlow::Node {
*/
predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
}
private class LegacyStepAsSharedTypeTrackingStep extends SharedTypeTrackingStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
any(LegacyTypeTrackingStep s).step(pred, succ)
}
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(LegacyTypeTrackingStep s).storeStep(pred, succ, prop)
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
any(LegacyTypeTrackingStep s).loadStep(pred, succ, prop)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(LegacyTypeTrackingStep s).loadStoreStep(pred, succ, prop)
}
}

View File

@@ -39,9 +39,9 @@ predicate localFlowStep(
) {
pred = succ.getAPredecessor() and predlbl = succlbl
or
any(DataFlow::AdditionalFlowStep afs).step(pred, succ) and predlbl = succlbl
DataFlow::SharedFlowStep::step(pred, succ) and predlbl = succlbl
or
any(DataFlow::AdditionalFlowStep afs).step(pred, succ, predlbl, succlbl)
DataFlow::SharedFlowStep::step(pred, succ, predlbl, succlbl)
or
exists(boolean vp | configuration.isAdditionalFlowStep(pred, succ, vp) |
vp = true and

View File

@@ -16,7 +16,7 @@ private class Unit extends TUnit {
* Internal extension point for adding flow edges prior to call graph construction
* and type tracking.
*
* Steps added here will be added to both `AdditionalFlowStep` and `AdditionalTypeTrackingStep`.
* Steps added here will be added to both `SharedFlowStep` and `SharedTypeTrackingStep`.
*
* Contributing steps that rely on type tracking will lead to negative recursion.
*/
@@ -40,6 +40,15 @@ class PreCallGraphStep extends Unit {
* Holds if there is a step from the `prop` property of `pred` to the same property in `succ`.
*/
predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
/**
* Holds if there is a step from the `loadProp` property of `pred` to the `storeProp` property in `succ`.
*/
predicate loadStoreStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string loadProp, string storeProp
) {
none()
}
}
module PreCallGraphStep {
@@ -75,62 +84,61 @@ module PreCallGraphStep {
predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
any(PreCallGraphStep s).loadStoreStep(pred, succ, prop)
}
}
private class NodeWithPreCallGraphStep extends DataFlow::Node {
NodeWithPreCallGraphStep() {
PreCallGraphStep::step(this, _)
or
PreCallGraphStep::storeStep(this, _, _)
or
PreCallGraphStep::loadStep(this, _, _)
or
PreCallGraphStep::loadStoreStep(this, _, _)
/**
* Holds if there is a step from the `loadProp` property of `pred` to the `storeProp` property in `succ`.
*/
predicate loadStoreStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string loadProp, string storeProp
) {
any(PreCallGraphStep s).loadStoreStep(pred, succ, loadProp, storeProp)
}
}
private class AdditionalFlowStepFromPreCallGraph extends NodeWithPreCallGraphStep,
DataFlow::AdditionalFlowStep {
private class SharedFlowStepFromPreCallGraph extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = this and
PreCallGraphStep::step(this, succ)
PreCallGraphStep::step(pred, succ)
}
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
pred = this and
PreCallGraphStep::storeStep(this, succ, prop)
PreCallGraphStep::storeStep(pred, succ, prop)
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
pred = this and
PreCallGraphStep::loadStep(this, succ, prop)
PreCallGraphStep::loadStep(pred, succ, prop)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
pred = this and
PreCallGraphStep::loadStoreStep(this, succ, prop)
PreCallGraphStep::loadStoreStep(pred, succ, prop)
}
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
) {
PreCallGraphStep::loadStoreStep(pred, succ, loadProp, storeProp)
}
}
private class AdditionalTypeTrackingStepFromPreCallGraph extends NodeWithPreCallGraphStep,
DataFlow::AdditionalTypeTrackingStep {
private class SharedTypeTrackingStepFromPreCallGraph extends DataFlow::SharedTypeTrackingStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = this and
PreCallGraphStep::step(this, succ)
PreCallGraphStep::step(pred, succ)
}
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
pred = this and
PreCallGraphStep::storeStep(this, succ, prop)
PreCallGraphStep::storeStep(pred, succ, prop)
}
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
pred = this and
PreCallGraphStep::loadStep(this, succ, prop)
PreCallGraphStep::loadStep(pred, succ, prop)
}
override predicate loadStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
pred = this and
PreCallGraphStep::loadStoreStep(this, succ, prop)
PreCallGraphStep::loadStoreStep(pred, succ, prop)
}
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::SourceNode succ, string loadProp, string storeProp
) {
PreCallGraphStep::loadStoreStep(pred, succ, loadProp, storeProp)
}
}

View File

@@ -1,50 +1,66 @@
import javascript
private import semmle.javascript.dataflow.TypeTracking
private import semmle.javascript.internal.CachedStages
private import FlowSteps
class PropertyName extends string {
PropertyName() {
this = any(DataFlow::PropRef pr).getPropertyName()
or
AccessPath::isAssignedInUniqueFile(this)
or
exists(AccessPath::getAnAssignmentTo(_, this))
or
this instanceof TypeTrackingPseudoProperty
cached
private module Cached {
cached
module Public {
cached
predicate forceStage() { Stages::TypeTracking::ref() }
cached
class PropertyName extends string {
cached
PropertyName() {
this = any(DataFlow::PropRef pr).getPropertyName()
or
AccessPath::isAssignedInUniqueFile(this)
or
exists(AccessPath::getAnAssignmentTo(_, this))
or
SharedTypeTrackingStep::loadStep(_, _, this)
or
SharedTypeTrackingStep::storeStep(_, _, this)
or
SharedTypeTrackingStep::loadStoreStep(_, _, this, _)
or
SharedTypeTrackingStep::loadStoreStep(_, _, _, this)
}
}
/**
* A description of a step on an inter-procedural data flow path.
*/
cached
newtype TStepSummary =
LevelStep() or
CallStep() or
ReturnStep() or
StoreStep(PropertyName prop) or
LoadStep(PropertyName prop) or
CopyStep(PropertyName prop) or
LoadStoreStep(PropertyName fromProp, PropertyName toProp) {
SharedTypeTrackingStep::loadStoreStep(_, _, fromProp, toProp)
}
}
/**
* INTERNAL: Use `SourceNode.track()` or `SourceNode.backtrack()` instead.
*/
cached
predicate step(DataFlow::SourceNode pred, DataFlow::SourceNode succ, StepSummary summary) {
exists(DataFlow::Node mid | pred.flowsTo(mid) | StepSummary::smallstep(mid, succ, summary))
}
}
import Cached::Public
class OptionalPropertyName extends string {
OptionalPropertyName() { this instanceof PropertyName or this = "" }
}
/**
* A pseudo-property that can be used in type-tracking.
*/
abstract class TypeTrackingPseudoProperty extends string {
bindingset[this]
TypeTrackingPseudoProperty() { any() }
/**
* Gets a property name that `this` can be copied to in a `LoadStoreStep(this, result)`.
*/
string getLoadStoreToProp() { none() }
}
/**
* A description of a step on an inter-procedural data flow path.
*/
newtype TStepSummary =
LevelStep() or
CallStep() or
ReturnStep() or
StoreStep(PropertyName prop) or
LoadStep(PropertyName prop) or
CopyStep(PropertyName prop) or
LoadStoreStep(PropertyName fromProp, PropertyName toProp) {
exists(TypeTrackingPseudoProperty prop | fromProp = prop and toProp = prop.getLoadStoreToProp())
}
/**
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
*
@@ -75,10 +91,7 @@ module StepSummary {
/**
* INTERNAL: Use `SourceNode.track()` or `SourceNode.backtrack()` instead.
*/
cached
predicate step(DataFlow::SourceNode pred, DataFlow::SourceNode succ, StepSummary summary) {
exists(DataFlow::Node mid | pred.flowsTo(mid) | smallstep(mid, succ, summary))
}
predicate step = Cached::step/3;
/**
* INTERNAL: Use `TypeBackTracker.smallstep()` instead.
@@ -111,17 +124,22 @@ module StepSummary {
basicLoadStep(pred, succ, prop) and
summary = LoadStep(prop)
or
any(AdditionalTypeTrackingStep st).storeStep(pred, succ, prop) and
SharedTypeTrackingStep::storeStep(pred, succ, prop) and
summary = StoreStep(prop)
or
any(AdditionalTypeTrackingStep st).loadStep(pred, succ, prop) and
SharedTypeTrackingStep::loadStep(pred, succ, prop) and
summary = LoadStep(prop)
or
any(AdditionalTypeTrackingStep st).loadStoreStep(pred, succ, prop) and
SharedTypeTrackingStep::loadStoreStep(pred, succ, prop) and
summary = CopyStep(prop)
)
or
any(AdditionalTypeTrackingStep st).step(pred, succ) and
exists(string fromProp, string toProp |
SharedTypeTrackingStep::loadStoreStep(pred, succ, fromProp, toProp) and
summary = LoadStoreStep(fromProp, toProp)
)
or
SharedTypeTrackingStep::step(pred, succ) and
summary = LevelStep()
or
// Store to global access path

View File

@@ -194,26 +194,22 @@ module EventDispatch {
}
/**
* A taint-step that models data-flow between event handlers and event dispatchers.
* A flow-step that models data-flow between event handlers and event dispatchers.
*/
private class EventEmitterTaintStep extends DataFlow::AdditionalFlowStep {
EventRegistration reg;
EventDispatch dispatch;
EventEmitterTaintStep() {
this = dispatch and
reg = dispatch.getAReceiver() and
not dispatch.getChannel() != reg.getChannel()
}
private class EventEmitterFlowStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(int i | i >= 0 |
pred = dispatch.getSentItem(i) and
succ = reg.getReceivedItem(i)
exists(EventRegistration reg, EventDispatch dispatch |
reg = dispatch.getAReceiver() and
not dispatch.getChannel() != reg.getChannel()
|
exists(int i | i >= 0 |
pred = dispatch.getSentItem(i) and
succ = reg.getReceivedItem(i)
)
or
dispatch = reg.getAReturnDispatch() and
pred = reg.getAReturnedValue() and
succ = dispatch
)
or
dispatch = reg.getAReturnDispatch() and
pred = reg.getAReturnedValue() and
succ = dispatch
}
}

View File

@@ -6,6 +6,7 @@ import javascript
private import semmle.javascript.DynamicPropertyAccess
private import semmle.javascript.dataflow.internal.StepSummary
private import semmle.javascript.dataflow.internal.CallGraphs
private import DataFlow::PseudoProperties as PseudoProperties
module HTTP {
/**
@@ -689,33 +690,30 @@ module HTTP {
isDecoratedCall(result, candidate)
}
private string mapValueProp() {
result = [PseudoProperties::mapValueAll(), PseudoProperties::mapValueUnknownKey()]
}
/**
* A collection that contains one or more route potential handlers.
*/
private class ContainerCollection extends HTTP::RouteHandlerCandidateContainer::Range {
private class ContainerCollection extends HTTP::RouteHandlerCandidateContainer::Range,
DataFlow::NewNode {
ContainerCollection() {
this = DataFlow::globalVarRef("Map").getAnInstantiation() and // restrict to Map for now
exists(
CollectionFlowStep store, DataFlow::Node storeTo, DataFlow::Node input,
RouteHandlerCandidate candidate
|
this.flowsTo(storeTo) and
store.store(input, storeTo, _) and
candidate.flowsTo(input)
exists(DataFlow::Node use |
DataFlow::SharedTypeTrackingStep::storeStep(use, this, mapValueProp()) and
use.getALocalSource() instanceof RouteHandlerCandidate
)
}
override DataFlow::SourceNode getRouteHandler(DataFlow::SourceNode access) {
result instanceof RouteHandlerCandidate and
exists(
DataFlow::Node input, TypeTrackingPseudoProperty key, CollectionFlowStep store,
CollectionFlowStep load, DataFlow::Node storeTo, DataFlow::Node loadFrom
|
this.flowsTo(storeTo) and
store.store(input, storeTo, key) and
exists(DataFlow::Node input, string key, DataFlow::Node loadFrom |
getAPossiblyDecoratedHandler(result).flowsTo(input) and
DataFlow::SharedTypeTrackingStep::storeStep(input, this, key) and
ref(this).flowsTo(loadFrom) and
load.load(loadFrom, access, key)
DataFlow::SharedTypeTrackingStep::loadStep(loadFrom, access,
[key, PseudoProperties::mapValueAll()])
)
}
}

View File

@@ -164,22 +164,15 @@ private module Immutable {
/**
* A dataflow step for an immutable collection.
*/
class ImmutableConstructionStep extends DataFlow::AdditionalFlowStep {
ImmutableConstructionStep() { this = [loadStep(_, _), storeStep(_, _), step(_)] }
class ImmutableConstructionStep extends DataFlow::SharedFlowStep {
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
this = loadStep(pred, prop) and
succ = this
succ = loadStep(pred, prop)
}
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
this = storeStep(pred, prop) and
succ = this
succ = storeStep(pred, prop)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
this = step(pred) and
succ = this
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) { succ = step(pred) }
}
}

View File

@@ -360,9 +360,9 @@ module LodashUnderscore {
/**
* A data flow step propagating an exception thrown from a callback to a Lodash/Underscore function.
*/
private class ExceptionStep extends DataFlow::CallNode, DataFlow::AdditionalFlowStep {
ExceptionStep() {
exists(string name | this = member(name).getACall() |
private class ExceptionStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call, string name |
// Members ending with By, With, or While indicate that they are a variant of
// another function that takes a callback.
name.matches("%By") or
@@ -386,13 +386,12 @@ module LodashUnderscore {
name = "replace" or
name = "some" or
name = "transform"
|
call = member(name).getACall() and
pred = call.getAnArgument().(DataFlow::FunctionNode).getExceptionalReturn() and
succ = call.getExceptionalReturn()
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = getAnArgument().(DataFlow::FunctionNode).getExceptionalReturn() and
succ = this.getExceptionalReturn()
}
}
/**

View File

@@ -126,37 +126,28 @@ module NextJS {
/**
* A step modelling the flow from the server-computed props object to the default exported function that renders the page.
*/
class NextJSStaticPropsStep extends DataFlow::AdditionalFlowStep, DataFlow::FunctionNode {
Module pageModule;
NextJSStaticPropsStep() {
pageModule = getAPagesModule() and
this = pageModule.getAnExportedValue("default").getAFunctionValue()
}
class NextJSStaticPropsStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = getAPropsSource(pageModule) and
succ = this.getParameter(0)
exists(Module pageModule, DataFlow::FunctionNode function |
pageModule = getAPagesModule() and
function = pageModule.getAnExportedValue("default").getAFunctionValue() and
pred = getAPropsSource(pageModule) and
succ = function.getParameter(0)
)
}
}
/**
* A step modelling the flow from the server-computed props object to the default exported React component that renders the page.
*/
class NextJSStaticReactComponentPropsStep extends DataFlow::AdditionalFlowStep,
DataFlow::ValueNode {
Module pageModule;
ReactComponent component;
NextJSStaticReactComponentPropsStep() {
pageModule = getAPagesModule() and
this.getAstNode() = component and
this = pageModule.getAnExportedValue("default").getALocalSource()
}
class NextJSStaticReactComponentPropsStep extends DataFlow::SharedFlowStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
pred = getAPropsSource(pageModule) and
succ = component.getADirectPropsAccess()
exists(Module pageModule, ReactComponent component |
pageModule = getAPagesModule() and
pageModule.getAnExportedValue("default").getALocalSource() = DataFlow::valueNode(component) and
pred = getAPropsSource(pageModule) and
succ = component.getADirectPropsAccess()
)
}
}

View File

@@ -219,7 +219,7 @@ module Stages {
or
AccessPath::DominatingPaths::hasDominatingWrite(_)
or
any(DataFlow::AdditionalFlowStep s).step(_, _)
DataFlow::SharedFlowStep::step(_, _)
}
}

View File

@@ -255,15 +255,12 @@ module ExternalAPIUsedWithUntrustedData {
not exists(DataFlow::Node arg |
arg = this.getAnArgument() and not arg instanceof DeepObjectSink
|
TaintTracking::sharedTaintStep(arg, _)
or
exists(DataFlow::AdditionalFlowStep s |
s.step(arg, _) or
s.step(arg, _, _, _) or
s.loadStep(arg, _, _) or
s.storeStep(arg, _, _) or
s.loadStoreStep(arg, _, _)
)
TaintTracking::sharedTaintStep(arg, _) or
DataFlow::SharedFlowStep::step(arg, _) or
DataFlow::SharedFlowStep::step(arg, _, _, _) or
DataFlow::SharedFlowStep::loadStep(arg, _, _) or
DataFlow::SharedFlowStep::storeStep(arg, _, _) or
DataFlow::SharedFlowStep::loadStoreStep(arg, _, _)
)
}

View File

@@ -100,10 +100,10 @@ module PrototypePollutingAssignment {
// users wouldn't bother to call Object.create in that case.
result = DataFlow::globalVarRef("Object").getAMemberCall("create")
or
// Allow use of AdditionalFlowSteps to track a bit further
// Allow use of SharedFlowSteps to track a bit further
exists(DataFlow::Node mid |
prototypeLessObject(t.continue()).flowsTo(mid) and
any(DataFlow::AdditionalFlowStep s).step(mid, result)
DataFlow::SharedFlowStep::step(mid, result)
)
or
exists(DataFlow::TypeTracker t2 | result = prototypeLessObject(t2).track(t2, t))

View File

@@ -26,3 +26,6 @@ typeTracking
| tst.js:2:16:2:23 | source() | tst.js:37:14:37:14 | e |
| tst.js:2:16:2:23 | source() | tst.js:45:14:45:14 | e |
| tst.js:2:16:2:23 | source() | tst.js:53:8:53:21 | map.get("key") |
| tst.js:2:16:2:23 | source() | tst.js:59:8:59:22 | map2.get("foo") |
| tst.js:2:16:2:23 | source() | tst.js:64:8:64:26 | map3.get(unknown()) |
| tst.js:2:16:2:23 | source() | tst.js:69:8:69:26 | map3.get(unknown()) |

View File

@@ -22,10 +22,6 @@ DataFlow::SourceNode trackSource(DataFlow::TypeTracker t, DataFlow::SourceNode s
start = result
or
exists(DataFlow::TypeTracker t2 | t = t2.step(trackSource(t2, start), result))
or
exists(DataFlow::TypeTracker t2 |
result = CollectionsTypeTracking::collectionStep(trackSource(t2, start), t, t2)
)
}
query DataFlow::SourceNode typeTracking(DataFlow::Node start) {

View File

@@ -54,17 +54,17 @@
sink(map.get("nonExistingKey")); // OK.
// unknown write, known read
var map2 = new map();
var map2 = new Map();
map2.set(unknown(), source);
sink(map2.get("foo")); // NOT OK (for data-flow). OK for type-tracking.
sink(map2.get("foo")); // NOT OK (for data-flow).
// unknown write, unknown read
var map3 = new map();
var map3 = new Map();
map3.set(unknown(), source);
sink(map3.get(unknown())); // NOT OK (for data-flow). OK for type-tracking.
sink(map3.get(unknown())); // NOT OK (for data-flow).
// known write, unknown read
var map4 = new map();
var map4 = new Map();
map4.set("foo", source);
sink(map3.get(unknown())); // NOT OK (for data-flow). OK for type-tracking.
sink(map3.get(unknown())); // NOT OK (for data-flow).
})();

View File

@@ -9,7 +9,7 @@ query predicate clientRequest_getADataNode(Electron::ElectronClientRequest cr, D
query predicate clientRequest(Electron::ElectronClientRequest cr) { any() }
query predicate ipcFlow(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::AdditionalFlowStep afs | afs.step(pred, succ))
DataFlow::SharedFlowStep::step(pred, succ)
}
query predicate remoteFlowSources(RemoteFlowSource source) { any() }

View File

@@ -1,4 +1,4 @@
taintSteps
flowSteps
| customEmitter.js:5:20:5:24 | "bar" | customEmitter.js:6:19:6:22 | data |
| customEmitter.js:12:21:12:25 | "baz" | customEmitter.js:13:23:13:26 | data |
| customEmitter.js:12:21:12:25 | "baz" | customEmitter.js:22:14:22:18 | yData |

View File

@@ -1,7 +1,7 @@
import javascript
query predicate taintSteps(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::AdditionalFlowStep step | step.step(pred, succ))
query predicate flowSteps(DataFlow::Node pred, DataFlow::Node succ) {
DataFlow::SharedFlowStep::step(pred, succ)
}
query predicate eventEmitter(EventEmitter e) { any() }

View File

@@ -1,5 +1,5 @@
import javascript
query predicate test_AdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::AdditionalFlowStep step | step.step(pred, succ) | any())
DataFlow::SharedFlowStep::step(pred, succ)
}

View File

@@ -21,7 +21,7 @@ serverSend
serverReceive
| server.js:7:3:9:4 | ws.on(' ... );\\n\\t\\t}) |
| sockjs.js:9:5:12:6 | conn.on ... \\n }) |
taintStep
flowSteps
| browser.js:5:15:5:32 | 'Hi from browser!' | server.js:7:38:7:44 | message |
| browser.js:21:13:21:18 | 'test' | sockjs.js:9:31:9:37 | message |
| client.js:7:11:7:27 | 'Hi from client!' | server.js:7:38:7:44 | message |

View File

@@ -12,8 +12,8 @@ query ServerWebSocket::SendNode serverSend() { any() }
query ServerWebSocket::ReceiveNode serverReceive() { any() }
query predicate taintStep(DataFlow::Node pred, DataFlow::Node succ) {
any(DataFlow::AdditionalFlowStep s).step(pred, succ)
query predicate flowSteps(DataFlow::Node pred, DataFlow::Node succ) {
DataFlow::SharedFlowStep::step(pred, succ)
}
query RemoteFlowSource remoteFlow() { any() }