mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
JS: Add flow summaries for maps and sets
This commit is contained in:
@@ -16,7 +16,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for `Set.add()` method, which adds an element to a Set.
|
||||
*/
|
||||
private class SetAdd extends PreCallGraphStep {
|
||||
private class SetAdd extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call = obj.getAMethodCall("add") and
|
||||
@@ -29,7 +29,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for the `Set` constructor, which copies any elements from the first argument into the resulting set.
|
||||
*/
|
||||
private class SetConstructor extends PreCallGraphStep {
|
||||
private class SetConstructor extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
@@ -49,7 +49,7 @@ private module CollectionDataFlow {
|
||||
* 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.
|
||||
*/
|
||||
private class ForOfStep extends PreCallGraphStep {
|
||||
private class ForOfStep extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node e, string prop) {
|
||||
exists(ForOfStmt forOf |
|
||||
obj = forOf.getIterationDomain().flow() and
|
||||
@@ -73,7 +73,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for a call to `forEach` on a Set or Map.
|
||||
*/
|
||||
private class SetMapForEach extends PreCallGraphStep {
|
||||
private class SetMapForEach extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "forEach" and
|
||||
@@ -88,7 +88,7 @@ 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 PreCallGraphStep {
|
||||
private class MapGet extends LegacyPreCallGraphStep {
|
||||
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "get" and
|
||||
@@ -108,7 +108,7 @@ 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 PreCallGraphStep {
|
||||
class MapSet extends LegacyPreCallGraphStep {
|
||||
override predicate storeStep(DataFlow::Node element, DataFlow::SourceNode obj, string prop) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call = obj.getAMethodCall("set") and
|
||||
@@ -121,7 +121,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for a call to `values` on a Map or a Set.
|
||||
*/
|
||||
private class MapAndSetValues extends PreCallGraphStep {
|
||||
private class MapAndSetValues extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
@@ -138,7 +138,7 @@ private module CollectionDataFlow {
|
||||
/**
|
||||
* A step for a call to `keys` on a Set.
|
||||
*/
|
||||
private class SetKeys extends PreCallGraphStep {
|
||||
private class SetKeys extends LegacyPreCallGraphStep {
|
||||
override predicate loadStoreStep(
|
||||
DataFlow::Node pred, DataFlow::SourceNode succ, string fromProp, string toProp
|
||||
) {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
private import AmbiguousCoreMethods
|
||||
private import Arrays2
|
||||
private import AsyncAwait
|
||||
private import Maps2
|
||||
private import Promises2
|
||||
private import Sets2
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Contains flow summaries and steps modelling flow through `Map` objects.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.dataflow.FlowSummary
|
||||
private import FlowSummaryUtil
|
||||
|
||||
private DataFlow::SourceNode mapConstructorRef() { result = DataFlow::globalVarRef("Map") }
|
||||
|
||||
class MapConstructor extends SummarizedCallable {
|
||||
MapConstructor() { this = "Map constructor" }
|
||||
|
||||
override DataFlow::InvokeNode getACallSimple() {
|
||||
result = mapConstructorRef().getAnInstantiation()
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
preservesValue = true and
|
||||
(
|
||||
input = "Argument[0]." + ["ArrayElement", "SetElement", "IteratorElement"] + ".Member[0]" and
|
||||
output = "ReturnValue.MapKey"
|
||||
or
|
||||
input = "Argument[0]." + ["ArrayElement", "SetElement", "IteratorElement"] + ".Member[1]" and
|
||||
output = "ReturnValue.MapValue"
|
||||
or
|
||||
input = ["Argument[0].WithMapKey", "Argument[0].WithMapValue"] and
|
||||
output = "ReturnValue"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A read step for `Map#get`.
|
||||
*
|
||||
* This is implemented as a step instead of a flow summary, as we currently do not expose a MaD syntax
|
||||
* for map values with a known key.
|
||||
*/
|
||||
class MapGetStep extends DataFlow::AdditionalFlowStep {
|
||||
override predicate readStep(
|
||||
DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ
|
||||
) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "get" and
|
||||
call.getNumArgument() = 1 and
|
||||
pred = call.getReceiver() and
|
||||
succ = call
|
||||
|
|
||||
contents = DataFlow::ContentSet::mapValueFromKey(call.getArgument(0).getStringValue())
|
||||
or
|
||||
not exists(call.getArgument(0).getStringValue()) and
|
||||
contents = DataFlow::ContentSet::mapValueAll()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A read step for `Map#set`.
|
||||
*
|
||||
* This is implemented as a step instead of a flow summary, as we currently do not expose a MaD syntax
|
||||
* for map values with a known key.
|
||||
*/
|
||||
class MapSetStep extends DataFlow::AdditionalFlowStep {
|
||||
override predicate storeStep(
|
||||
DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ
|
||||
) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "set" and
|
||||
call.getNumArgument() = 2 and
|
||||
pred = call.getArgument(1) and
|
||||
succ.(DataFlow::ExprPostUpdateNode).getPreUpdateNode() = call.getReceiver()
|
||||
|
|
||||
contents = DataFlow::ContentSet::mapValueFromKey(call.getArgument(0).getStringValue())
|
||||
or
|
||||
not exists(call.getArgument(0).getStringValue()) and
|
||||
contents = DataFlow::ContentSet::mapValueWithUnknownKey()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class MapGet extends SummarizedCallable {
|
||||
MapGet() { this = "Map#get" }
|
||||
|
||||
override DataFlow::MethodCallNode getACallSimple() {
|
||||
none() and // Disabled for now - need MaD syntax for known map values
|
||||
result.getMethodName() = "get" and
|
||||
result.getNumArgument() = 1
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
preservesValue = true and
|
||||
input = "Argument[this].MapValue" and
|
||||
output = "ReturnValue"
|
||||
}
|
||||
}
|
||||
|
||||
class MapSet extends SummarizedCallable {
|
||||
MapSet() { this = "Map#set" }
|
||||
|
||||
override DataFlow::MethodCallNode getACallSimple() {
|
||||
result.getMethodName() = "set" and
|
||||
result.getNumArgument() = 2
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
preservesValue = true and
|
||||
input = ["Argument[this].WithMapKey", "Argument[this].WithMapValue"] and
|
||||
output = "ReturnValue"
|
||||
or
|
||||
preservesValue = true and
|
||||
none() and // Disabled for now - need MaD syntax for known map values
|
||||
(
|
||||
input = "Argument[0]" and
|
||||
output = "Argument[this].MapKey"
|
||||
or
|
||||
input = "Argument[1]" and
|
||||
output = "Argument[this].MapValue"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Contains flow summaries and steps modelling flow through `Set` objects.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
private import semmle.javascript.dataflow.FlowSummary
|
||||
private import FlowSummaryUtil
|
||||
|
||||
private DataFlow::SourceNode setConstructorRef() { result = DataFlow::globalVarRef("Set") }
|
||||
|
||||
class SetConstructor extends SummarizedCallable {
|
||||
SetConstructor() { this = "Set constructor" }
|
||||
|
||||
override DataFlow::InvokeNode getACallSimple() {
|
||||
result = setConstructorRef().getAnInstantiation()
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
preservesValue = true and
|
||||
(
|
||||
input = "Argument[0]." + ["ArrayElement", "SetElement", "IteratorElement"] and
|
||||
output = "ReturnValue.SetElement"
|
||||
or
|
||||
input = "Argument[0].MapKey" and
|
||||
output = "ReturnValue.SetElement.Member[0]"
|
||||
or
|
||||
input = "Argument[0].MapValue" and
|
||||
output = "ReturnValue.SetElement.Member[1]"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SetAdd extends SummarizedCallable {
|
||||
SetAdd() { this = "Set#add" }
|
||||
|
||||
override DataFlow::MethodCallNode getACallSimple() {
|
||||
result.getMethodName() = "add" and
|
||||
result.getNumArgument() = 1
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
preservesValue = true and
|
||||
input = "Argument[0]" and
|
||||
output = "Argument[this].SetElement"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user