mirror of
https://github.com/github/codeql.git
synced 2026-04-25 08:45:14 +02:00
JS: More re-export logic to handle subclass export
This commit is contained in:
@@ -106,14 +106,14 @@ module TypeGraphExport<GraphExportSig<API::Node> S, shouldContainTypeSig/1 shoul
|
||||
shouldContainTypeEx(type1) and
|
||||
exists(API::Node node |
|
||||
// A relevant type is exported directly
|
||||
ModelOutput::getATypeNode(type1).getAValueReachableFromSource() = node.asSink() and
|
||||
Specific::sourceFlowsToSink(ModelOutput::getATypeNode(type1), node) and
|
||||
ExportedGraph::pathToNode(type2, path, node)
|
||||
or
|
||||
// Something that leads to a relevant type, but didn't finish its access path, is exported
|
||||
exists(string midType, string midPath, string remainingPath, string prefix, API::Node source |
|
||||
Shared::typeModel(type1, midType, midPath) and
|
||||
partiallyEvaluatedModel(midType, midPath, source, remainingPath) and
|
||||
source.getAValueReachableFromSource() = node.asSink() and
|
||||
Specific::sourceFlowsToSink(source, node) and
|
||||
ExportedGraph::pathToNode(type2, prefix, node) and
|
||||
path = join(prefix, remainingPath)
|
||||
)
|
||||
|
||||
@@ -379,3 +379,28 @@ predicate apiGraphHasEdge(API::Node pred, string path, API::Node succ) {
|
||||
path = ""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value of `source` is exposed at `sink`.
|
||||
*/
|
||||
bindingset[source]
|
||||
predicate sourceFlowsToSink(API::Node source, API::Node sink) {
|
||||
source.getAValueReachableFromSource() = sink.asSink()
|
||||
or
|
||||
// Handle the case of an upstream class being the base class of an exposed own class
|
||||
//
|
||||
// class Foo extends external.BaseClass {}
|
||||
//
|
||||
// Here we want to ensure that `Instance(Foo)` is seen as subtype of `Instance(external.BaseClass)`.
|
||||
//
|
||||
// Although we have a dedicated sink node for `Instance(Foo)` we don't have dedicate source node for `Instance(external.BaseClass)`.
|
||||
//
|
||||
// However, there is always an `Instance` edge from the base class expression (`external.BaseClass`)
|
||||
// to the receiver node in subclass constructor (the implicit constructor of `Foo`), which always exists.
|
||||
// So we use the constructor receiver as the representative for `Instance(external.BaseClass)`.
|
||||
// (This will get simplified when migrating to Ruby-style API graphs, as both sides will have explicit API nodes).
|
||||
exists(DataFlow::ClassNode cls |
|
||||
source.asSource() = cls.getConstructor().getReceiver() and
|
||||
sink = API::Internal::getClassInstance(cls)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ typeModel
|
||||
| (subclass).D.prototype.d | (subclass).D.prototype | Member[d] |
|
||||
| upstream-lib | (reexport).func | ReturnValue |
|
||||
| upstream-lib | reexport | Member[lib] |
|
||||
| upstream-lib.Type | (subclass).D.prototype | |
|
||||
| upstream-lib.XYZ | reexport | Member[x].Member[y].Member[z] |
|
||||
| upstream-lib.XYZ | reexport | Member[xy].Member[z] |
|
||||
summaryModel
|
||||
|
||||
@@ -12,8 +12,6 @@ export class C extends B {
|
||||
|
||||
import * as upstream from "upstream-lib";
|
||||
|
||||
// TODO: needs to emit type model: [upstream.Type; (subclass).D.prototype; ""]
|
||||
// The getAValueReachableFromSource() logic does not handle the base class -> instance step
|
||||
export class D extends upstream.Type {
|
||||
d() {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user