Skip to content

Commit 6b2ebcc

Browse files
authored
Merge pull request #10276 from asgerf/mad-typedef-entry-points
Add TypeModel hook for adding MaD type-defs from CodeQL
2 parents 3410dd5 + 0cc3b8a commit 6b2ebcc

File tree

21 files changed

+345
-67
lines changed

21 files changed

+345
-67
lines changed

javascript/ql/lib/semmle/javascript/ApiGraphs.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1495,7 +1495,7 @@ module API {
14951495
/** Gets the EntryPoint associated with this label. */
14961496
API::EntryPoint getEntryPoint() { result = e }
14971497

1498-
override string toString() { result = "getASuccessor(Label::entryPoint(\"" + e + "\"))" }
1498+
override string toString() { result = "entryPoint(\"" + e + "\")" }
14991499
}
15001500

15011501
/** A label that gets a promised value. */

javascript/ql/lib/semmle/javascript/dataflow/Sources.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ private import javascript
1010
private import semmle.javascript.dataflow.TypeTracking
1111
private import semmle.javascript.internal.CachedStages
1212

13+
/**
14+
* An alias for `SourceNode`.
15+
*/
16+
class LocalSourceNode = SourceNode;
17+
1318
/**
1419
* A source node for local data flow, that is, a node from which local data flow is tracked.
1520
*

javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ private class Unit = Specific::Unit;
7272

7373
private module API = Specific::API;
7474

75+
private module DataFlow = Specific::DataFlow;
76+
7577
private import Specific::AccessPathSyntax
7678

7779
/** Module containing hooks for providing input data to be interpreted as a model. */
@@ -156,6 +158,22 @@ module ModelInput {
156158
abstract predicate row(string row);
157159
}
158160

161+
/**
162+
* A unit class for adding additional type model rows from CodeQL models.
163+
*/
164+
class TypeModel extends Unit {
165+
/**
166+
* Gets a data-flow node that is a source of the type `package;type`.
167+
*/
168+
DataFlow::Node getASource(string package, string type) { none() }
169+
170+
/**
171+
* Gets a data flow node that is a sink of the type `package;type`,
172+
* usually because it is an argument passed to a parameter of that type.
173+
*/
174+
DataFlow::Node getASink(string package, string type) { none() }
175+
}
176+
159177
/**
160178
* A unit class for adding additional type variable model rows.
161179
*/
@@ -368,6 +386,57 @@ private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, A
368386
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
369387
}
370388

389+
private class TypeModelUseEntry extends API::EntryPoint {
390+
private string package;
391+
private string type;
392+
393+
TypeModelUseEntry() {
394+
exists(any(TypeModel tm).getASource(package, type)) and
395+
this = "TypeModelUseEntry;" + package + ";" + type
396+
}
397+
398+
override DataFlow::LocalSourceNode getASource() {
399+
result = any(TypeModel tm).getASource(package, type)
400+
}
401+
402+
API::Node getNodeForType(string package_, string type_) {
403+
package = package_ and type = type_ and result = this.getANode()
404+
}
405+
}
406+
407+
private class TypeModelDefEntry extends API::EntryPoint {
408+
private string package;
409+
private string type;
410+
411+
TypeModelDefEntry() {
412+
exists(any(TypeModel tm).getASink(package, type)) and
413+
this = "TypeModelDefEntry;" + package + ";" + type
414+
}
415+
416+
override DataFlow::Node getASink() { result = any(TypeModel tm).getASink(package, type) }
417+
418+
API::Node getNodeForType(string package_, string type_) {
419+
package = package_ and type = type_ and result = this.getANode()
420+
}
421+
}
422+
423+
/**
424+
* Gets an API node identified by the given `(package,type)` pair.
425+
*/
426+
pragma[nomagic]
427+
private API::Node getNodeFromType(string package, string type) {
428+
exists(string package2, string type2, AccessPath path2 |
429+
typeModel(package, type, package2, type2, path2) and
430+
result = getNodeFromPath(package2, type2, path2)
431+
)
432+
or
433+
result = any(TypeModelUseEntry e).getNodeForType(package, type)
434+
or
435+
result = any(TypeModelDefEntry e).getNodeForType(package, type)
436+
or
437+
result = Specific::getExtraNodeFromType(package, type)
438+
}
439+
371440
/**
372441
* Gets the API node identified by the first `n` tokens of `path` in the given `(package, type, path)` tuple.
373442
*/
@@ -376,12 +445,8 @@ private API::Node getNodeFromPath(string package, string type, AccessPath path,
376445
isRelevantFullPath(package, type, path) and
377446
(
378447
n = 0 and
379-
exists(string package2, string type2, AccessPath path2 |
380-
typeModel(package, type, package2, type2, path2) and
381-
result = getNodeFromPath(package2, type2, path2, path2.getNumToken())
382-
)
448+
result = getNodeFromType(package, type)
383449
or
384-
// Language-specific cases, such as handling of global variables
385450
result = Specific::getExtraNodeFromPath(package, type, path, n)
386451
)
387452
or
@@ -581,12 +646,7 @@ module ModelOutput {
581646
* Holds if `node` is seen as an instance of `(package,type)` due to a type definition
582647
* contributed by a CSV model.
583648
*/
584-
API::Node getATypeNode(string package, string type) {
585-
exists(string package2, string type2, AccessPath path |
586-
typeModel(package, type, package2, type2, path) and
587-
result = getNodeFromPath(package2, type2, path)
588-
)
589-
}
649+
API::Node getATypeNode(string package, string type) { result = getNodeFromType(package, type) }
590650

591651
/**
592652
* Gets an error message relating to an invalid CSV row in a model.

javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
*/
2121

2222
private import javascript as JS
23-
private import JS::DataFlow as DataFlow
2423
private import ApiGraphModels
2524

2625
class Unit = JS::Unit;
@@ -29,6 +28,7 @@ class Unit = JS::Unit;
2928
module API = JS::API;
3029

3130
import semmle.javascript.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
31+
import JS::DataFlow as DataFlow
3232
private import AccessPathSyntax
3333

3434
/**
@@ -77,10 +77,6 @@ private API::Node getGlobalNode(string globalName) {
7777
/** Gets a JavaScript-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
7878
bindingset[package, type, path]
7979
API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int n) {
80-
type = "" and
81-
n = 0 and
82-
result = API::moduleImport(package)
83-
or
8480
// Global variable accesses is via the 'global' package
8581
exists(AccessPathToken token |
8682
package = getAPackageAlias("global") and
@@ -90,10 +86,15 @@ API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int
9086
result = getGlobalNode(token.getAnArgument()) and
9187
n = 1
9288
)
89+
}
90+
91+
/** Gets a JavaScript-specific interpretation of the `(package, type)` tuple. */
92+
API::Node getExtraNodeFromType(string package, string type) {
93+
type = "" and
94+
result = API::moduleImport(package)
9395
or
9496
// Access instance of a type based on type annotations
95-
n = 0 and
96-
result = API::Node::ofType(getAPackageAlias(package), type)
97+
result = API::Internal::getANodeOfTypeRaw(getAPackageAlias(package), type)
9798
}
9899

99100
/**
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
module.exports = CustomEntryPoint.foo; /* use=getASuccessor(Label::entryPoint("CustomEntryPoint")) */
1+
module.exports = CustomEntryPoint.foo; /* use=entryPoint("CustomEntryPoint") */
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
| body-parser | | index.ts:4:20:4:41 | require ... arser") |
2+
| express | | index.ts:3:17:3:34 | require("express") |
3+
| mongodb | | index.ts:1:8:1:19 | * as mongodb |
14
| mongodb | Collection | index.ts:14:3:14:17 | getCollection() |
5+
| mongoose | | index.ts:17:8:17:20 | * as mongoose |
26
| mongoose | Model | index.ts:22:3:22:20 | getMongooseModel() |
37
| mongoose | Query | index.ts:23:3:23:20 | getMongooseQuery() |
8+
| puppeteer | | index.ts:26:8:26:21 | * as puppeteer |
49
| puppeteer | Browser | index.ts:30:22:30:33 | this.browser |

javascript/ql/test/library-tests/TypeScript/HasUnderlyingType/HasUnderlyingType.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
underlyingTypeNode
2+
| foo | | file://:0:0:0:0 | use moduleImport("foo").getMember("exports") |
3+
| foo | | foo.ts:1:8:1:10 | use moduleImport("foo").getMember("exports").getMember("default") |
24
| foo | Bar | foo.ts:3:1:5:1 | use moduleImport("foo").getMember("exports").getMember("Bar").getInstance() |
35
| foo | Bar | foo.ts:3:12:3:12 | use moduleImport("foo").getMember("exports").getMember("Bar").getInstance() |
46
#select

javascript/ql/test/library-tests/frameworks/Redux/test.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ getAffectedStateAccessPath
125125
getADispatchFunctionNode
126126
| react-redux.jsx:65:20:65:32 | use moduleImport("react-redux").getMember("exports").getMember("useDispatch").getReturn() |
127127
getADispatchedValueNode
128-
| react-redux.jsx:27:12:30:5 | def getASuccessor(Label::entryPoint("react-redux-connect")).getParameter(1).getMember("manualAction").getReturn() |
128+
| react-redux.jsx:27:12:30:5 | def entryPoint("react-redux-connect").getParameter(1).getMember("manualAction").getReturn() |
129129
| react-redux.jsx:69:18:69:39 | def moduleImport("react-redux").getMember("exports").getMember("useDispatch").getReturn().getParameter(0) |
130130
| react-redux.jsx:70:18:70:38 | def moduleImport("react-redux").getMember("exports").getMember("useDispatch").getReturn().getParameter(0) |
131131
getAnUntypedActionInReducer

python/ql/lib/semmle/python/ApiGraphs.qll

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,30 @@ module API {
453453
int getNumArgument() { result = count(this.getArg(_)) }
454454
}
455455

456+
/**
457+
* An API entry point.
458+
*
459+
* By default, API graph nodes are only created for nodes that come from an external
460+
* library or escape into an external library. The points where values are cross the boundary
461+
* between codebases are called "entry points".
462+
*
463+
* Anything imported from an external package is considered to be an entry point, but
464+
* additional entry points may be added by extending this class.
465+
*/
466+
abstract class EntryPoint extends string {
467+
bindingset[this]
468+
EntryPoint() { any() }
469+
470+
/** Gets a data-flow node corresponding to a use-node for this entry point. */
471+
DataFlow::LocalSourceNode getASource() { none() }
472+
473+
/** Gets a data-flow node corresponding to a def-node for this entry point. */
474+
DataFlow::Node getASink() { none() }
475+
476+
/** Gets an API-node for this entry point. */
477+
API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) }
478+
}
479+
456480
/**
457481
* Provides the actual implementation of API graphs, cached for performance.
458482
*
@@ -652,6 +676,12 @@ module API {
652676
|
653677
lbl = Label::memberFromRef(aw)
654678
)
679+
or
680+
exists(EntryPoint entry |
681+
base = root() and
682+
lbl = Label::entryPoint(entry) and
683+
rhs = entry.getASink()
684+
)
655685
}
656686

657687
/**
@@ -735,6 +765,12 @@ module API {
735765
ImportStar::namePossiblyDefinedInImportStar(ref.asCfgNode(), name, s)
736766
))
737767
)
768+
or
769+
exists(EntryPoint entry |
770+
base = root() and
771+
lbl = Label::entryPoint(entry) and
772+
ref = entry.getASource()
773+
)
738774
}
739775

740776
/**
@@ -909,7 +945,8 @@ module API {
909945
MkLabelSelfParameter() or
910946
MkLabelReturn() or
911947
MkLabelSubclass() or
912-
MkLabelAwait()
948+
MkLabelAwait() or
949+
MkLabelEntryPoint(EntryPoint ep)
913950

914951
/** A label for a module. */
915952
class LabelModule extends ApiLabel, MkLabelModule {
@@ -983,6 +1020,15 @@ module API {
9831020
class LabelAwait extends ApiLabel, MkLabelAwait {
9841021
override string toString() { result = "getAwaited()" }
9851022
}
1023+
1024+
/** A label for entry points. */
1025+
class LabelEntryPoint extends ApiLabel, MkLabelEntryPoint {
1026+
private EntryPoint entry;
1027+
1028+
LabelEntryPoint() { this = MkLabelEntryPoint(entry) }
1029+
1030+
override string toString() { result = "entryPoint(\"" + entry + "\")" }
1031+
}
9861032
}
9871033

9881034
/** Gets the edge label for the module `m`. */
@@ -1019,5 +1065,8 @@ module API {
10191065

10201066
/** Gets the `await` edge label. */
10211067
LabelAwait await() { any() }
1068+
1069+
/** Gets the label going from the root node to the nodes associated with the given entry point. */
1070+
LabelEntryPoint entryPoint(EntryPoint ep) { result = MkLabelEntryPoint(ep) }
10221071
}
10231072
}

0 commit comments

Comments
 (0)