Merge pull request #10276 from asgerf/mad-typedef-entry-points

Add TypeModel hook for adding MaD type-defs from CodeQL
This commit is contained in:
Asger F
2022-09-07 14:14:48 +02:00
committed by GitHub
21 changed files with 345 additions and 67 deletions

View File

@@ -1495,7 +1495,7 @@ module API {
/** Gets the EntryPoint associated with this label. */
API::EntryPoint getEntryPoint() { result = e }
override string toString() { result = "getASuccessor(Label::entryPoint(\"" + e + "\"))" }
override string toString() { result = "entryPoint(\"" + e + "\")" }
}
/** A label that gets a promised value. */

View File

@@ -10,6 +10,11 @@ private import javascript
private import semmle.javascript.dataflow.TypeTracking
private import semmle.javascript.internal.CachedStages
/**
* An alias for `SourceNode`.
*/
class LocalSourceNode = SourceNode;
/**
* A source node for local data flow, that is, a node from which local data flow is tracked.
*

View File

@@ -72,6 +72,8 @@ private class Unit = Specific::Unit;
private module API = Specific::API;
private module DataFlow = Specific::DataFlow;
private import Specific::AccessPathSyntax
/** Module containing hooks for providing input data to be interpreted as a model. */
@@ -156,6 +158,22 @@ module ModelInput {
abstract predicate row(string row);
}
/**
* A unit class for adding additional type model rows from CodeQL models.
*/
class TypeModel extends Unit {
/**
* Gets a data-flow node that is a source of the type `package;type`.
*/
DataFlow::Node getASource(string package, string type) { none() }
/**
* Gets a data flow node that is a sink of the type `package;type`,
* usually because it is an argument passed to a parameter of that type.
*/
DataFlow::Node getASink(string package, string type) { none() }
}
/**
* A unit class for adding additional type variable model rows.
*/
@@ -368,6 +386,57 @@ private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, A
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
}
private class TypeModelUseEntry extends API::EntryPoint {
private string package;
private string type;
TypeModelUseEntry() {
exists(any(TypeModel tm).getASource(package, type)) and
this = "TypeModelUseEntry;" + package + ";" + type
}
override DataFlow::LocalSourceNode getASource() {
result = any(TypeModel tm).getASource(package, type)
}
API::Node getNodeForType(string package_, string type_) {
package = package_ and type = type_ and result = this.getANode()
}
}
private class TypeModelDefEntry extends API::EntryPoint {
private string package;
private string type;
TypeModelDefEntry() {
exists(any(TypeModel tm).getASink(package, type)) and
this = "TypeModelDefEntry;" + package + ";" + type
}
override DataFlow::Node getASink() { result = any(TypeModel tm).getASink(package, type) }
API::Node getNodeForType(string package_, string type_) {
package = package_ and type = type_ and result = this.getANode()
}
}
/**
* Gets an API node identified by the given `(package,type)` pair.
*/
pragma[nomagic]
private API::Node getNodeFromType(string package, string type) {
exists(string package2, string type2, AccessPath path2 |
typeModel(package, type, package2, type2, path2) and
result = getNodeFromPath(package2, type2, path2)
)
or
result = any(TypeModelUseEntry e).getNodeForType(package, type)
or
result = any(TypeModelDefEntry e).getNodeForType(package, type)
or
result = Specific::getExtraNodeFromType(package, type)
}
/**
* Gets the API node identified by the first `n` tokens of `path` in the given `(package, type, path)` tuple.
*/
@@ -376,12 +445,8 @@ private API::Node getNodeFromPath(string package, string type, AccessPath path,
isRelevantFullPath(package, type, path) and
(
n = 0 and
exists(string package2, string type2, AccessPath path2 |
typeModel(package, type, package2, type2, path2) and
result = getNodeFromPath(package2, type2, path2, path2.getNumToken())
)
result = getNodeFromType(package, type)
or
// Language-specific cases, such as handling of global variables
result = Specific::getExtraNodeFromPath(package, type, path, n)
)
or
@@ -581,12 +646,7 @@ module ModelOutput {
* Holds if `node` is seen as an instance of `(package,type)` due to a type definition
* contributed by a CSV model.
*/
API::Node getATypeNode(string package, string type) {
exists(string package2, string type2, AccessPath path |
typeModel(package, type, package2, type2, path) and
result = getNodeFromPath(package2, type2, path)
)
}
API::Node getATypeNode(string package, string type) { result = getNodeFromType(package, type) }
/**
* Gets an error message relating to an invalid CSV row in a model.

View File

@@ -20,7 +20,6 @@
*/
private import javascript as JS
private import JS::DataFlow as DataFlow
private import ApiGraphModels
class Unit = JS::Unit;
@@ -29,6 +28,7 @@ class Unit = JS::Unit;
module API = JS::API;
import semmle.javascript.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
import JS::DataFlow as DataFlow
private import AccessPathSyntax
/**
@@ -77,10 +77,6 @@ private API::Node getGlobalNode(string globalName) {
/** Gets a JavaScript-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
bindingset[package, type, path]
API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int n) {
type = "" and
n = 0 and
result = API::moduleImport(package)
or
// Global variable accesses is via the 'global' package
exists(AccessPathToken token |
package = getAPackageAlias("global") and
@@ -90,10 +86,15 @@ API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int
result = getGlobalNode(token.getAnArgument()) and
n = 1
)
}
/** Gets a JavaScript-specific interpretation of the `(package, type)` tuple. */
API::Node getExtraNodeFromType(string package, string type) {
type = "" and
result = API::moduleImport(package)
or
// Access instance of a type based on type annotations
n = 0 and
result = API::Node::ofType(getAPackageAlias(package), type)
result = API::Internal::getANodeOfTypeRaw(getAPackageAlias(package), type)
}
/**

View File

@@ -1 +1 @@
module.exports = CustomEntryPoint.foo; /* use=getASuccessor(Label::entryPoint("CustomEntryPoint")) */
module.exports = CustomEntryPoint.foo; /* use=entryPoint("CustomEntryPoint") */

View File

@@ -1,4 +1,9 @@
| body-parser | | index.ts:4:20:4:41 | require ... arser") |
| express | | index.ts:3:17:3:34 | require("express") |
| mongodb | | index.ts:1:8:1:19 | * as mongodb |
| mongodb | Collection | index.ts:14:3:14:17 | getCollection() |
| mongoose | | index.ts:17:8:17:20 | * as mongoose |
| mongoose | Model | index.ts:22:3:22:20 | getMongooseModel() |
| mongoose | Query | index.ts:23:3:23:20 | getMongooseQuery() |
| puppeteer | | index.ts:26:8:26:21 | * as puppeteer |
| puppeteer | Browser | index.ts:30:22:30:33 | this.browser |

View File

@@ -1,4 +1,6 @@
underlyingTypeNode
| foo | | file://:0:0:0:0 | use moduleImport("foo").getMember("exports") |
| foo | | foo.ts:1:8:1:10 | use moduleImport("foo").getMember("exports").getMember("default") |
| foo | Bar | foo.ts:3:1:5:1 | use moduleImport("foo").getMember("exports").getMember("Bar").getInstance() |
| foo | Bar | foo.ts:3:12:3:12 | use moduleImport("foo").getMember("exports").getMember("Bar").getInstance() |
#select

View File

@@ -125,7 +125,7 @@ getAffectedStateAccessPath
getADispatchFunctionNode
| react-redux.jsx:65:20:65:32 | use moduleImport("react-redux").getMember("exports").getMember("useDispatch").getReturn() |
getADispatchedValueNode
| react-redux.jsx:27:12:30:5 | def getASuccessor(Label::entryPoint("react-redux-connect")).getParameter(1).getMember("manualAction").getReturn() |
| react-redux.jsx:27:12:30:5 | def entryPoint("react-redux-connect").getParameter(1).getMember("manualAction").getReturn() |
| react-redux.jsx:69:18:69:39 | def moduleImport("react-redux").getMember("exports").getMember("useDispatch").getReturn().getParameter(0) |
| react-redux.jsx:70:18:70:38 | def moduleImport("react-redux").getMember("exports").getMember("useDispatch").getReturn().getParameter(0) |
getAnUntypedActionInReducer