Dynamic/JS: Add library for exporting models

This commit is contained in:
Asger F
2024-04-04 15:03:00 +02:00
parent bd1de179b3
commit acef9b7111
10 changed files with 392 additions and 2 deletions

View File

@@ -147,7 +147,11 @@ private predicate isPrivateAssignment(DataFlow::Node node) {
)
}
private predicate isPrivateLike(API::Node node) { isPrivateAssignment(node.asSink()) }
/**
* Holds if `node` is the sink node corresponding to the right-hand side of a private declaration,
* like a private field (`#field`) or class member with the `private` modifier.
*/
predicate isPrivateLike(API::Node node) { isPrivateAssignment(node.asSink()) }
bindingset[name]
private int getNameBadness(string name) {

View File

@@ -19,6 +19,7 @@
private import javascript
private import internal.ApiGraphModels as Shared
private import internal.ApiGraphModelsSpecific as Specific
private import semmle.javascript.endpoints.EndpointNaming as EndpointNaming
import Shared::ModelInput as ModelInput
import Shared::ModelOutput as ModelOutput
@@ -55,3 +56,98 @@ private class TaintStepFromSummary extends TaintTracking::SharedTaintStep {
summaryStepNodes(pred, succ, "taint")
}
}
/**
* Specifies which parts of the API graph to export in `ModelExport`.
*/
signature module ModelExportSig {
/**
* Holds if the exported model should contain `node`, if it is publicly accessible.
*
* This ensures that all ways to access `node` will be exported in type models.
*/
predicate shouldContain(API::Node node);
/**
* Holds if a named must be generated for `node` if it is to be included in the exported graph.
*/
default predicate mustBeNamed(API::Node node) { none() }
}
/**
* Module for exporting type models for a given set of nodes in the API graph.
*/
module ModelExport<ModelExportSig S> {
private import codeql.mad.dynamic.GraphExport
private module GraphExportConfig implements GraphExportSig<API::Node> {
predicate edge = Specific::apiGraphHasEdge/3;
predicate shouldContain = S::shouldContain/1;
predicate shouldNotContain(API::Node node) {
EndpointNaming::isPrivateLike(node)
or
node instanceof API::Use
}
predicate mustBeNamed(API::Node node) {
node.getAValueReachingSink() instanceof DataFlow::ClassNode
or
node = API::Internal::getClassInstance(_)
or
S::mustBeNamed(node)
}
predicate exposedName(API::Node node, string type, string path) {
node = API::moduleExport(type) and path = ""
}
predicate suggestedName(API::Node node, string type) {
exists(string package, string name |
(
EndpointNaming::sinkHasPrimaryName(node, package, name) and
not EndpointNaming::aliasDefinition(_, _, _, _, node)
or
EndpointNaming::aliasDefinition(_, _, package, name, node)
) and
type = EndpointNaming::renderName(package, name)
)
}
bindingset[host]
predicate hasTypeSummary(API::Node host, string path) {
exists(string methodName |
functionReturnsReceiver(host.getMember(methodName).getAValueReachingSink()) and
path = "Member[" + methodName + "].ReturnValue"
)
}
pragma[nomagic]
private predicate functionReturnsReceiver(DataFlow::FunctionNode func) {
getAReceiverRef(func).flowsTo(func.getReturnNode())
}
pragma[nomagic]
private DataFlow::MethodCallNode getAReceiverCall(DataFlow::FunctionNode func) {
result = getAReceiverRef(func).getAMethodCall()
}
pragma[nomagic]
private predicate callReturnsReceiver(DataFlow::MethodCallNode call) {
functionReturnsReceiver(call.getACallee().flow())
}
pragma[nomagic]
private DataFlow::SourceNode getAReceiverRef(DataFlow::FunctionNode func) {
result = func.getReceiver()
or
result = getAReceiverCall(func) and
callReturnsReceiver(result)
}
}
private module ExportedGraph = GraphExport<API::Node, GraphExportConfig>;
import ExportedGraph
}

View File

@@ -435,7 +435,7 @@ private API::Node getNodeFromType(string type) {
* Gets the API node identified by the first `n` tokens of `path` in the given `(type, path)` tuple.
*/
pragma[nomagic]
private API::Node getNodeFromPath(string type, AccessPath path, int n) {
API::Node getNodeFromPath(string type, AccessPath path, int n) {
isRelevantFullPath(type, path) and
(
n = 0 and

View File

@@ -353,3 +353,23 @@ module ModelOutputSpecific {
)
}
}
/**
* Holds if the edge `pred -> succ` labelled with `path` exists in the API graph.
*/
bindingset[pred]
predicate apiGraphHasEdge(API::Node pred, string path, API::Node succ) {
exists(string name | succ = pred.getMember(name) and path = "Member[" + name + "]")
or
succ = pred.getUnknownMember() and path = "AnyMember"
or
succ = pred.getInstance() and path = "Instance"
or
succ = pred.getReturn() and path = "ReturnValue"
or
exists(int n | succ = pred.getParameter(n) |
if pred instanceof API::Use then path = "Argument[" + n + "]" else path = "Parameter[" + n + "]"
)
or
succ = pred.getPromised() and path = "Awaited"
}