mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
Merge pull request #3169 from erik-krogh/Maps
Approved by asgerf, esbena
This commit is contained in:
@@ -12,6 +12,7 @@ import semmle.javascript.Base64
|
||||
import semmle.javascript.CFG
|
||||
import semmle.javascript.Classes
|
||||
import semmle.javascript.Closure
|
||||
import semmle.javascript.Collections
|
||||
import semmle.javascript.Comments
|
||||
import semmle.javascript.Concepts
|
||||
import semmle.javascript.Constants
|
||||
|
||||
@@ -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)`.
|
||||
@@ -110,10 +155,10 @@ private module ArrayDataFlow {
|
||||
this.getMethodName() = "unshift"
|
||||
}
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
(element = this.getAnArgument() or element = this.getASpreadArgument()) and
|
||||
obj = this.getReceiver().getALocalSource()
|
||||
element = this.getAnArgument() and
|
||||
obj.getAMethodCall() = this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,10 +188,10 @@ private module ArrayDataFlow {
|
||||
element = this
|
||||
}
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
element = this.(DataFlow::PropWrite).getRhs() and
|
||||
this = obj.(DataFlow::SourceNode).getAPropertyWrite()
|
||||
this = obj.getAPropertyWrite()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +234,7 @@ private module ArrayDataFlow {
|
||||
element = getCallback(0).getParameter(0)
|
||||
}
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
this.getMethodName() = "map" and
|
||||
prop = arrayElement() and
|
||||
element = this.getCallback(0).getAReturn() and
|
||||
@@ -209,7 +254,7 @@ private module ArrayDataFlow {
|
||||
private class ArrayCreationStep extends DataFlow::AdditionalFlowStep, DataFlow::Node {
|
||||
ArrayCreationStep() { this instanceof DataFlow::ArrayCreationNode }
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
element = this.(DataFlow::ArrayCreationNode).getAnElement() and
|
||||
obj = this
|
||||
@@ -223,10 +268,10 @@ private module ArrayDataFlow {
|
||||
private class ArraySpliceStep extends DataFlow::AdditionalFlowStep, DataFlow::MethodCallNode {
|
||||
ArraySpliceStep() { this.getMethodName() = "splice" }
|
||||
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::Node obj, string prop) {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
prop = arrayElement() and
|
||||
element = getArgument(2) and
|
||||
obj = this.getReceiver().getALocalSource()
|
||||
this = obj.getAMethodCall()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
278
javascript/ql/src/semmle/javascript/Collections.qll
Normal file
278
javascript/ql/src/semmle/javascript/Collections.qll
Normal file
@@ -0,0 +1,278 @@
|
||||
/**
|
||||
* Provides predicates and classes for working with the standard library collection implementations.
|
||||
* Currently [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) and
|
||||
* [Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) are implemented.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
private import semmle.javascript.dataflow.internal.StepSummary
|
||||
private import DataFlow::PseudoProperties
|
||||
|
||||
/**
|
||||
* A pseudo-property used in a data-flow/type-tracking step for collections.
|
||||
*
|
||||
* By extending `TypeTrackingPseudoProperty` the class enables the use of the collection related pseudo-properties in type-tracking predicates.
|
||||
*/
|
||||
private class PseudoProperty extends TypeTrackingPseudoProperty {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.)
|
||||
*/
|
||||
abstract private class CollectionFlowStep extends DataFlow::AdditionalFlowStep {
|
||||
final override predicate step(DataFlow::Node pred, DataFlow::Node succ) { none() }
|
||||
|
||||
final override predicate step(
|
||||
DataFlow::Node p, DataFlow::Node s, DataFlow::FlowLabel pl, DataFlow::FlowLabel sl
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` of the object `pred` should be loaded into `succ`.
|
||||
*/
|
||||
predicate load(DataFlow::Node pred, DataFlow::Node succ, PseudoProperty prop) { none() }
|
||||
|
||||
final override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
this.load(pred, succ, prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
|
||||
*/
|
||||
predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, PseudoProperty prop) { none() }
|
||||
|
||||
final override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
this.store(pred, succ, prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `prop` should be copied from the object `pred` to the object `succ`.
|
||||
*/
|
||||
predicate loadStore(DataFlow::Node pred, DataFlow::Node succ, PseudoProperty prop) { none() }
|
||||
|
||||
final override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
this.loadStore(pred, succ, prop, prop)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`.
|
||||
*/
|
||||
predicate loadStore(
|
||||
DataFlow::Node pred, DataFlow::Node succ, PseudoProperty loadProp, PseudoProperty storeProp
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
final override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
|
||||
) {
|
||||
this.loadStore(pred, succ, loadProp, storeProp)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides predicates and clases for type-tracking collections.
|
||||
*/
|
||||
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 |
|
||||
summary = LoadStep(field) and
|
||||
step.load(pred, result, field) and
|
||||
not field = mapValueUnknownKey() // prune unknown reads in type-tracking
|
||||
or
|
||||
summary = StoreStep(field) and
|
||||
step.store(pred, result, field)
|
||||
or
|
||||
summary = CopyStep(field) and
|
||||
step.loadStore(pred, result, field)
|
||||
or
|
||||
exists(PseudoProperty toField | summary = LoadStoreStep(field, toField) |
|
||||
step.loadStore(pred, result, field, toField)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the result from a single step through a collection, from `pred` with tracker `t2` to `result` with tracker `t`.
|
||||
*/
|
||||
pragma[inline]
|
||||
DataFlow::SourceNode collectionStep(
|
||||
DataFlow::SourceNode pred, DataFlow::TypeTracker t, DataFlow::TypeTracker t2
|
||||
) {
|
||||
exists(DataFlow::Node mid, StepSummary summary | pred.flowsTo(mid) and t = t2.append(summary) |
|
||||
result = collectionStep(mid, summary)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A module for data-flow steps related standard library collection implementations.
|
||||
*/
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
pred = this.getArgument(0) and
|
||||
succ = this 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.
|
||||
*/
|
||||
// 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())
|
||||
}
|
||||
|
||||
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
|
||||
) {
|
||||
pred = this and
|
||||
succ = element 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()]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `set` method on a Map.
|
||||
*
|
||||
* If the key of the call to `set` has a known string value,
|
||||
* then the value will be stored into a pseudo-property corresponding to the known string value.
|
||||
* 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()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
pred = this.getReceiver() and
|
||||
succ = this 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
|
||||
) {
|
||||
pred = this.getReceiver() and
|
||||
succ = this and
|
||||
fromProp = setElement() and
|
||||
toProp = iteratorElement()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +176,7 @@ module PromiseTypeTracking {
|
||||
summary = StoreStep(field) and
|
||||
step.store(pred, result, field)
|
||||
or
|
||||
summary = LoadStoreStep(field) and
|
||||
summary = CopyStep(field) and
|
||||
step.loadStore(pred, result, field)
|
||||
)
|
||||
}
|
||||
@@ -232,9 +232,9 @@ abstract private class PromiseFlowStep extends DataFlow::AdditionalFlowStep {
|
||||
/**
|
||||
* Holds if `pred` should be stored in the object `succ` under the property `prop`.
|
||||
*/
|
||||
predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
|
||||
predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
|
||||
|
||||
final override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
final override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
this.store(pred, succ, prop)
|
||||
}
|
||||
|
||||
@@ -246,6 +246,12 @@ abstract private class PromiseFlowStep extends DataFlow::AdditionalFlowStep {
|
||||
final override predicate loadStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
this.loadStore(pred, succ, prop)
|
||||
}
|
||||
|
||||
final override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::Node succ, string loadProp, string storeProp
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -267,7 +273,7 @@ private module PromiseFlow {
|
||||
|
||||
PromiseDefitionStep() { this = promise }
|
||||
|
||||
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
prop = valueProp() and
|
||||
pred = promise.getResolveParameter().getACall().getArgument(0) and
|
||||
succ = this
|
||||
@@ -296,7 +302,7 @@ private module PromiseFlow {
|
||||
|
||||
CreationStep() { this = promise }
|
||||
|
||||
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
prop = valueProp() and
|
||||
pred = promise.getValue() and
|
||||
succ = this
|
||||
@@ -362,7 +368,7 @@ private module PromiseFlow {
|
||||
succ = this
|
||||
}
|
||||
|
||||
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
prop = valueProp() and
|
||||
pred = getCallback([0 .. 1]).getAReturn() and
|
||||
succ = this
|
||||
@@ -396,7 +402,7 @@ private module PromiseFlow {
|
||||
succ = this
|
||||
}
|
||||
|
||||
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
prop = errorProp() and
|
||||
pred = getCallback(0).getExceptionalReturn() and
|
||||
succ = this
|
||||
@@ -424,7 +430,7 @@ private module PromiseFlow {
|
||||
succ = this
|
||||
}
|
||||
|
||||
override predicate store(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
override predicate store(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
prop = errorProp() and
|
||||
pred = getCallback(0).getExceptionalReturn() and
|
||||
succ = this
|
||||
|
||||
@@ -244,8 +244,11 @@ abstract class Configuration extends string {
|
||||
* EXPERIMENTAL. This API may change in the future.
|
||||
*
|
||||
* 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 isAdditionalStoreStep(DataFlow::Node pred, DataFlow::Node succ, string prop) { none() }
|
||||
predicate isAdditionalStoreStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL. This API may change in the future.
|
||||
@@ -540,9 +543,10 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
|
||||
* EXPERIMENTAL. This API may change in the future.
|
||||
*
|
||||
* 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::Node succ, string prop) { none() }
|
||||
predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) { none() }
|
||||
|
||||
/**
|
||||
* EXPERIMENTAL. This API may change in the future.
|
||||
@@ -574,6 +578,71 @@ abstract class AdditionalFlowStep extends DataFlow::Node {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A collection of pseudo-properties that are used in multiple files.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
module PseudoProperties {
|
||||
bindingset[s]
|
||||
private string pseudoProperty(string s) { result = "$" + s + "$" }
|
||||
|
||||
bindingset[s, v]
|
||||
private string pseudoProperty(string s, string v) { result = "$" + s + "|" + v + "$" }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of elements in a `Set`
|
||||
*/
|
||||
string setElement() { result = pseudoProperty("setElement") }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of elements in a JavaScript iterator.
|
||||
*/
|
||||
string iteratorElement() { result = pseudoProperty("iteratorElement") }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of elements in an `Array`.
|
||||
*/
|
||||
string arrayElement() { result = pseudoProperty("arrayElement") }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of elements in some array-like object. (Set, Array, or Iterator).
|
||||
*/
|
||||
string arrayLikeElement() { result = [setElement(), iteratorElement(), arrayElement()] }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of map values, where the key is unknown.
|
||||
*/
|
||||
string mapValueUnknownKey() { result = pseudoProperty("mapValueUnknownKey") }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of all the values in a map.
|
||||
*/
|
||||
string mapValueAll() { result = pseudoProperty("allMapValues") }
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of a map value where the key is `key`.
|
||||
* The string value of the `key` is encoded in the result, and there is only a result if the string value of `key` is known.
|
||||
*/
|
||||
pragma[inline]
|
||||
string mapValueKnownKey(DataFlow::Node key) {
|
||||
result = pseudoProperty("mapValue", any(string s | key.mayHaveStringValue(s)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a pseudo-property for the location of a map value where the key is `key`.
|
||||
*/
|
||||
pragma[inline]
|
||||
string mapValue(DataFlow::Node key) {
|
||||
result = mapValueKnownKey(key)
|
||||
or
|
||||
not exists(mapValueKnownKey(key)) and
|
||||
result = mapValueUnknownKey()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that should be considered a source for some specific configuration,
|
||||
* in addition to any other sources that configuration may recognize.
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -603,15 +603,10 @@ module TaintTracking {
|
||||
* 3) A `URLSearchParams` object (either `url.searchParams` or `new URLSearchParams(input)`) has a tainted value,
|
||||
* which can be accessed using a `get` or `getAll` call. (See getableUrlPseudoProperty())
|
||||
*/
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
|
||||
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
|
||||
succ = this and
|
||||
(
|
||||
(
|
||||
prop = "searchParams" or
|
||||
prop = "hash" or
|
||||
prop = "search" or
|
||||
prop = hiddenUrlPseudoProperty()
|
||||
) and
|
||||
prop = ["searchParams", "hash", "search", hiddenUrlPseudoProperty()] and
|
||||
exists(DataFlow::NewNode newUrl | succ = newUrl |
|
||||
newUrl = DataFlow::globalVarRef("URL").getAnInstantiation() and
|
||||
pred = newUrl.getArgument(0)
|
||||
|
||||
@@ -53,7 +53,11 @@ class TypeTracker extends TTypeTracker {
|
||||
TypeTracker append(StepSummary step) {
|
||||
step = LevelStep() and result = this
|
||||
or
|
||||
step = LoadStoreStep(prop) and result = this
|
||||
exists(string toProp | step = LoadStoreStep(prop, toProp) |
|
||||
result = MkTypeTracker(hasCall, toProp)
|
||||
)
|
||||
or
|
||||
step = CopyStep(prop) and result = this
|
||||
or
|
||||
step = CallStep() and result = MkTypeTracker(true, prop)
|
||||
or
|
||||
@@ -213,7 +217,11 @@ class TypeBackTracker extends TTypeBackTracker {
|
||||
TypeBackTracker prepend(StepSummary step) {
|
||||
step = LevelStep() and result = this
|
||||
or
|
||||
step = LoadStoreStep(prop) and result = this
|
||||
exists(string fromProp | step = LoadStoreStep(fromProp, prop) |
|
||||
result = MkTypeBackTracker(hasReturn, fromProp)
|
||||
)
|
||||
or
|
||||
step = CopyStep(prop) and result = this
|
||||
or
|
||||
step = CallStep() and hasReturn = false and result = this
|
||||
or
|
||||
|
||||
@@ -24,6 +24,11 @@ class OptionalPropertyName extends string {
|
||||
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() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,7 +40,10 @@ newtype TStepSummary =
|
||||
ReturnStep() or
|
||||
StoreStep(PropertyName prop) or
|
||||
LoadStep(PropertyName prop) or
|
||||
LoadStoreStep(PropertyName prop)
|
||||
CopyStep(PropertyName prop) or
|
||||
LoadStoreStep(PropertyName fromProp, PropertyName toProp) {
|
||||
exists(TypeTrackingPseudoProperty prop | fromProp = prop and toProp = prop.getLoadStoreToProp())
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Use `TypeTracker` or `TypeBackTracker` instead.
|
||||
@@ -55,7 +63,11 @@ class StepSummary extends TStepSummary {
|
||||
or
|
||||
exists(string prop | this = LoadStep(prop) | result = "load " + prop)
|
||||
or
|
||||
exists(string prop | this = LoadStoreStep(prop) | result = "in " + prop)
|
||||
exists(string prop | this = CopyStep(prop) | result = "copy " + prop)
|
||||
or
|
||||
exists(string fromProp, string toProp | this = LoadStoreStep(fromProp, toProp) |
|
||||
result = "load " + fromProp + " and store to " + toProp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user