add more dataflow steps for Arrays

This commit is contained in:
Erik Krogh Kristensen
2020-03-31 11:18:39 +02:00
parent a02213e745
commit 25aea900b6
5 changed files with 112 additions and 3 deletions

View File

@@ -96,10 +96,55 @@ module ArrayTaintTracking {
* Classes and predicates for modelling data-flow for arrays.
*/
private module ArrayDataFlow {
private import DataFlow::PseudoProperties
/**
* Gets a pseudo-field representing an element inside an array.
* 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 string arrayElement() { result = "$arrayElement$" }
private class ArrayFrom extends DataFlow::AdditionalFlowStep, DataFlow::CallNode {
ArrayFrom() { this = DataFlow::globalVarRef("Array").getAMemberCall("from") }
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()
}
}
/**
* A step modelling an array copy where the spread operator is used.
* The result is essentially array concatenation.
*
* 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
}
override predicate loadStoreStep(
DataFlow::Node pred, DataFlow::Node succ, string fromProp, string toProp
) {
pred = spreadArgument and
succ = base and
fromProp = arrayLikeElement() and
toProp = arrayElement()
}
}
/**
* A step for storing an element on an array using `arr.push(e)` or `arr.unshift(e)`.
@@ -112,7 +157,7 @@ private module ArrayDataFlow {
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
prop = arrayElement() and
(element = this.getAnArgument() or element = this.getASpreadArgument()) and
element = this.getAnArgument() and
obj = this.getReceiver().getALocalSource()
}
}
@@ -260,4 +305,20 @@ private module ArrayDataFlow {
succ = this
}
}
/**
* A step for modelling `for of` iteration on arrays.
*/
private class ForOfStep extends DataFlow::AdditionalFlowStep, DataFlow::ValueNode {
ForOfStmt forOf;
DataFlow::Node element;
ForOfStep() { this.asExpr() = forOf.getIterationDomain() }
override predicate loadStep(DataFlow::Node obj, DataFlow::Node e, string prop) {
obj = this and
e = DataFlow::lvalueNode(forOf.getLValue()) and
prop = arrayElement()
}
}
}

View File

@@ -574,6 +574,22 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
}
}
/**
* A collection of pseudo-properties that are used in multiple files.
* For use with load/store steps in `DataFlow::AdditionalFlowStep` and TypeTracking.
*/
module PseudoProperties {
/**
* Gets a pseudo-field representing an elements inside an `Array`.
*/
string arrayElement() { result = "$arrayElement$" }
/**
* Gets a pseudo-property representing elements inside some array-like object.
*/
string arrayLikeElement() { result = arrayElement() }
}
/**
* A data flow node that should be considered a source for some specific configuration,
* in addition to any other sources that configuration may recognize.

View File

@@ -620,6 +620,16 @@ class ArrayCreationNode extends DataFlow::ValueNode, DataFlow::SourceNode {
result = this.(ArrayLiteralNode).getSize() or
result = this.(ArrayConstructorInvokeNode).getSize()
}
/**
* Gets a data flow node corresponding to an array of values being passed as
* individual arguments to this array creation.
*/
DataFlow::Node getASpreadArgument() {
exists(SpreadElement arg | arg = getAnElement().getEnclosingExpr() |
result = DataFlow::valueNode(arg.getOperand())
)
}
}
/**