mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
JS: Add flow summaries for maps and sets
This commit is contained in:
@@ -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