mirror of
https://github.com/github/codeql.git
synced 2026-04-26 09:15:12 +02:00
JavaScript: Teach API graphs to handle promisify.
Following a suggestion by Asger, we track use nodes through calls to `promisify`. When we see a call to a promisified function, we introduce a new synthetic API-graph node representing the callback argument synthesised by the promisification, and track the result of the call to an `await` (or other promise resolution), which is then considered to be a use of the first parameter of the synthetic callback (the zeroth parameter being an error code, which we do not model yet).
This commit is contained in:
@@ -206,7 +206,8 @@ module API {
|
||||
this = Impl::MkClassInstance(result) or
|
||||
this = Impl::MkUse(result) or
|
||||
this = Impl::MkDef(result) or
|
||||
this = Impl::MkAsyncFuncResult(result)
|
||||
this = Impl::MkAsyncFuncResult(result) or
|
||||
this = Impl::MkSyntheticCallbackArg(_, result)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,12 +390,16 @@ module API {
|
||||
MkCanonicalNameUse(CanonicalName n) {
|
||||
not n.isRoot() and
|
||||
isUsed(n)
|
||||
} or
|
||||
MkSyntheticCallbackArg(DataFlow::Node src, DataFlow::InvokeNode nd) {
|
||||
trackUseNode(src, true).flowsTo(nd.getCalleeNode())
|
||||
}
|
||||
|
||||
class TDef = MkModuleDef or TNonModuleDef;
|
||||
|
||||
class TNonModuleDef =
|
||||
MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkCanonicalNameDef;
|
||||
MkModuleExport or MkClassInstance or MkAsyncFuncResult or MkDef or MkCanonicalNameDef or
|
||||
MkSyntheticCallbackArg;
|
||||
|
||||
class TUse = MkModuleUse or MkModuleImport or MkUse or MkCanonicalNameUse;
|
||||
|
||||
@@ -523,8 +528,8 @@ module API {
|
||||
* The receiver is considered to be argument -1.
|
||||
*/
|
||||
private predicate argumentPassing(TApiNode base, int i, DataFlow::Node arg) {
|
||||
exists(DataFlow::SourceNode use, DataFlow::SourceNode pred |
|
||||
use(base, use) and pred = trackUseNode(use)
|
||||
exists(DataFlow::Node use, DataFlow::SourceNode pred |
|
||||
use(base, use) and pred = trackUseNode(use, _)
|
||||
|
|
||||
arg = pred.getAnInvocation().getArgument(i)
|
||||
or
|
||||
@@ -609,6 +614,12 @@ module API {
|
||||
lbl = Label::instance() and
|
||||
ref = getANodeWithType(tn)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::InvokeNode call |
|
||||
base = MkSyntheticCallbackArg(_, call) and
|
||||
lbl = Label::parameter(1) and
|
||||
ref = awaited(call)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -672,21 +683,34 @@ module API {
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd, DataFlow::TypeTracker t) {
|
||||
private DataFlow::SourceNode trackUseNode(
|
||||
DataFlow::SourceNode nd, boolean promisified, DataFlow::TypeTracker t
|
||||
) {
|
||||
t.start() and
|
||||
use(_, nd) and
|
||||
result = nd
|
||||
result = nd and
|
||||
promisified = false
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = trackUseNode(nd, t2).track(t2, t))
|
||||
exists(DataFlow::CallNode promisify |
|
||||
promisify = API::moduleImport(["util", "bluebird"]).getMember("promisify").getACall()
|
||||
|
|
||||
trackUseNode(nd, false, t.continue()).flowsTo(promisify.getArgument(0)) and
|
||||
promisified = true and
|
||||
result = promisify
|
||||
)
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = trackUseNode(nd, promisified, t2).track(t2, t))
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd, boolean promisified) {
|
||||
result = trackUseNode(nd, promisified, DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node that is inter-procedurally reachable from `nd`, which is a use of some node.
|
||||
*/
|
||||
cached
|
||||
DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd) {
|
||||
result = trackUseNode(nd, DataFlow::TypeTracker::end())
|
||||
}
|
||||
DataFlow::SourceNode trackUseNode(DataFlow::SourceNode nd) { result = trackUseNode(nd, false) }
|
||||
|
||||
private DataFlow::SourceNode trackDefNode(DataFlow::Node nd, DataFlow::TypeBackTracker t) {
|
||||
t.start() and
|
||||
@@ -714,6 +738,17 @@ module API {
|
||||
result = trackDefNode(nd, DataFlow::TypeBackTracker::end())
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode awaited(DataFlow::InvokeNode call, DataFlow::TypeTracker t) {
|
||||
t.startInPromise() and
|
||||
exists(MkSyntheticCallbackArg(_, call))
|
||||
or
|
||||
exists(DataFlow::TypeTracker t2 | result = awaited(call, t2).track(t2, t))
|
||||
}
|
||||
|
||||
private DataFlow::Node awaited(DataFlow::InvokeNode call) {
|
||||
result = awaited(call, DataFlow::TypeTracker::end())
|
||||
}
|
||||
|
||||
private DataFlow::SourceNode getANodeWithType(TypeName tn) {
|
||||
exists(string moduleName, string typeName |
|
||||
tn.hasQualifiedName(moduleName, typeName) and
|
||||
@@ -774,6 +809,12 @@ module API {
|
||||
lbl = Label::return() and
|
||||
succ = MkAsyncFuncResult(f)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::SourceNode src, DataFlow::InvokeNode call |
|
||||
use(pred, src) and
|
||||
lbl = Label::parameter(call.getNumArgument()) and
|
||||
succ = MkSyntheticCallbackArg(src, call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user