Skip to content

Commit 7d55771

Browse files
authored
Merge pull request #8150 from asgerf/js/prep-sharing-api-graph-mad
Approved by erik-krogh
2 parents 62ee8fc + 1be47db commit 7d55771

File tree

3 files changed

+96
-76
lines changed

3 files changed

+96
-76
lines changed

javascript/ql/lib/semmle/javascript/frameworks/data/ModelsAsData.qll

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
*/
2222

2323
private import javascript
24-
private import internal.Shared as Shared
24+
private import internal.ApiGraphModels as Shared
25+
private import internal.ApiGraphModelsSpecific as Specific
2526
import Shared::ModelInput as ModelInput
2627
import Shared::ModelOutput as ModelOutput
2728

@@ -39,7 +40,7 @@ private class RemoteFlowSourceFromCsv extends RemoteFlowSource {
3940
*/
4041
private predicate summaryStepNodes(DataFlow::Node pred, DataFlow::Node succ, string kind) {
4142
exists(API::Node predNode, API::Node succNode |
42-
ModelOutput::summaryStep(predNode, succNode, kind) and
43+
Specific::summaryStep(predNode, succNode, kind) and
4344
pred = predNode.getARhs() and
4445
succ = succNode.getAnImmediateUse()
4546
)

javascript/ql/lib/semmle/javascript/frameworks/data/internal/Shared.qll renamed to javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll

Lines changed: 34 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,13 @@
5656
* the type is related to the `foo` package but is not intended to match a static type.
5757
*/
5858

59-
private import Impl as Impl
60-
import AccessPathSyntax
59+
private import ApiGraphModelsSpecific as Specific
6160

62-
private class Unit = Impl::Unit;
61+
private class Unit = Specific::Unit;
6362

64-
private module API = Impl::API;
63+
private module API = Specific::API;
64+
65+
private import Specific::AccessPathSyntax
6566

6667
/** Module containing hooks for providing input data to be interpreted as a model. */
6768
module ModelInput {
@@ -230,7 +231,7 @@ string getAPackageAlias(string package) {
230231
* Holds if CSV rows involving `package` might be relevant for the analysis of this database.
231232
*/
232233
private predicate isRelevantPackage(string package) {
233-
Impl::isPackageUsed(package)
234+
Specific::isPackageUsed(package)
234235
or
235236
exists(string other |
236237
isRelevantPackage(other) and
@@ -268,28 +269,25 @@ private class AccessPathRange extends AccessPath::Range {
268269
* Gets a successor of `node` in the API graph.
269270
*/
270271
bindingset[token]
271-
private API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
272+
API::Node getSuccessorFromNode(API::Node node, AccessPathToken token) {
272273
// API graphs use the same label for arguments and parameters. An edge originating from a
273274
// use-node represents be an argument, and an edge originating from a def-node represents a parameter.
274275
// We just map both to the same thing.
275276
token.getName() = ["Argument", "Parameter"] and
276277
result = node.getParameter(getAnIntFromStringUnbounded(token.getAnArgument()))
277278
or
278-
token.getName() = "Member" and
279-
result = node.getMember(token.getAnArgument())
280-
or
281279
token.getName() = "ReturnValue" and
282280
result = node.getReturn()
283281
or
284282
// Language-specific tokens
285-
result = Impl::getExtraSuccessorFromNode(node, token)
283+
result = Specific::getExtraSuccessorFromNode(node, token)
286284
}
287285

288286
/**
289287
* Gets an API-graph successor for the given invocation.
290288
*/
291289
bindingset[token]
292-
private API::Node getSuccessorFromInvoke(API::InvokeNode invoke, AccessPathToken token) {
290+
API::Node getSuccessorFromInvoke(Specific::InvokeNode invoke, AccessPathToken token) {
293291
token.getName() = "Argument" and
294292
(
295293
result = invoke.getParameter(getAnIntFromStringUnbounded(token.getAnArgument()))
@@ -303,18 +301,18 @@ private API::Node getSuccessorFromInvoke(API::InvokeNode invoke, AccessPathToken
303301
result = invoke.getReturn()
304302
or
305303
// Language-specific tokens
306-
result = Impl::getExtraSuccessorFromInvoke(invoke, token)
304+
result = Specific::getExtraSuccessorFromInvoke(invoke, token)
307305
}
308306

309307
/**
310308
* Holds if `invoke` invokes a call-site filter given by `token`.
311309
*/
312310
pragma[inline]
313-
private predicate invocationMatchesCallSiteFilter(API::InvokeNode invoke, AccessPathToken token) {
311+
private predicate invocationMatchesCallSiteFilter(Specific::InvokeNode invoke, AccessPathToken token) {
314312
token.getName() = "WithArity" and
315313
invoke.getNumArgument() = getAnIntFromStringUnbounded(token.getAnArgument())
316314
or
317-
Impl::invocationMatchesExtraCallSiteFilter(invoke, token)
315+
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
318316
}
319317

320318
/**
@@ -324,18 +322,14 @@ pragma[nomagic]
324322
API::Node getNodeFromPath(string package, string type, AccessPath path, int n) {
325323
isRelevantFullPath(package, type, path) and
326324
(
327-
type = "" and
328-
n = 0 and
329-
result = API::moduleImport(package)
330-
or
331325
n = 0 and
332326
exists(string package2, string type2, AccessPath path2 |
333327
typeModel(package, type, package2, type2, path2) and
334328
result = getNodeFromPath(package2, type2, path2, path2.getNumToken())
335329
)
336330
or
337331
// Language-specific cases, such as handling of global variables
338-
result = Impl::getExtraNodeFromPath(package, type, path, n)
332+
result = Specific::getExtraNodeFromPath(package, type, path, n)
339333
)
340334
or
341335
result = getSuccessorFromNode(getNodeFromPath(package, type, path, n - 1), path.getToken(n - 1))
@@ -355,62 +349,18 @@ API::Node getNodeFromPath(string package, string type, AccessPath path) {
355349
*
356350
* Unlike `getNodeFromPath`, the `path` may end with one or more call-site filters.
357351
*/
358-
API::InvokeNode getInvocationFromPath(string package, string type, AccessPath path, int n) {
359-
result = getNodeFromPath(package, type, path, n).getAnInvocation()
352+
Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path, int n) {
353+
result = Specific::getAnInvocationOf(getNodeFromPath(package, type, path, n))
360354
or
361355
result = getInvocationFromPath(package, type, path, n - 1) and
362356
invocationMatchesCallSiteFilter(result, path.getToken(n - 1))
363357
}
364358

365359
/** Gets an invocation identified by the given `(package, type, path)` tuple. */
366-
API::InvokeNode getInvocationFromPath(string package, string type, AccessPath path) {
360+
Specific::InvokeNode getInvocationFromPath(string package, string type, AccessPath path) {
367361
result = getInvocationFromPath(package, type, path, path.getNumToken())
368362
}
369363

370-
/**
371-
* Holds if a summary edge with the given `input, output, kind` columns have a `package, type, path` tuple
372-
* that resolves to `baseNode`.
373-
*/
374-
private predicate resolvedSummaryBase(
375-
API::InvokeNode baseNode, AccessPath input, AccessPath output, string kind
376-
) {
377-
exists(string package, string type, AccessPath path |
378-
summaryModel(package, type, path, input, output, kind) and
379-
baseNode = getInvocationFromPath(package, type, path)
380-
)
381-
}
382-
383-
/**
384-
* Holds if `path` is an input or output spec for a summary with the given `base` node.
385-
*/
386-
pragma[nomagic]
387-
private predicate relevantInputOutputPath(API::InvokeNode base, AccessPath path) {
388-
resolvedSummaryBase(base, path, _, _)
389-
or
390-
resolvedSummaryBase(base, _, path, _)
391-
}
392-
393-
/**
394-
* Gets the API node for the first `n` tokens of the given input/output path, evaluated relative to `baseNode`.
395-
*/
396-
private API::Node getNodeFromInputOutputPath(API::InvokeNode baseNode, AccessPath path, int n) {
397-
relevantInputOutputPath(baseNode, path) and
398-
(
399-
n = 1 and
400-
result = getSuccessorFromInvoke(baseNode, path.getToken(0))
401-
or
402-
result =
403-
getSuccessorFromNode(getNodeFromInputOutputPath(baseNode, path, n - 1), path.getToken(n - 1))
404-
)
405-
}
406-
407-
/**
408-
* Gets the API node for the given input/output path, evaluated relative to `baseNode`.
409-
*/
410-
private API::Node getNodeFromInputOutputPath(API::InvokeNode baseNode, AccessPath path) {
411-
result = getNodeFromInputOutputPath(baseNode, path, path.getNumToken())
412-
}
413-
414364
/**
415365
* Convenience-predicate for extracting two capture groups at once.
416366
*/
@@ -519,13 +469,25 @@ module ModelOutput {
519469
}
520470

521471
/**
522-
* Holds if a CSV summary contributed the step `pred -> succ` of the given `kind`.
472+
* Holds if a relevant CSV summary row has the given `kind`, `input` and `output`.
473+
*/
474+
predicate summaryModel(string input, string output, string kind) {
475+
exists(string package |
476+
isRelevantPackage(package) and
477+
summaryModel(package, _, _, input, output, kind)
478+
)
479+
}
480+
481+
/**
482+
* Holds if a summary edge with the given `input, output, kind` columns have a `package, type, path` tuple
483+
* that resolves to `baseNode`.
523484
*/
524-
predicate summaryStep(API::Node pred, API::Node succ, string kind) {
525-
exists(API::InvokeNode base, AccessPath input, AccessPath output |
526-
resolvedSummaryBase(base, input, output, kind) and
527-
pred = getNodeFromInputOutputPath(base, input) and
528-
succ = getNodeFromInputOutputPath(base, output)
485+
predicate resolvedSummaryBase(
486+
Specific::InvokeNode baseNode, AccessPath input, AccessPath output, string kind
487+
) {
488+
exists(string package, string type, AccessPath path |
489+
summaryModel(package, type, path, input, output, kind) and
490+
baseNode = getInvocationFromPath(package, type, path)
529491
)
530492
}
531493

javascript/ql/lib/semmle/javascript/frameworks/data/internal/Impl.qll renamed to javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModelsSpecific.qll

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Contains the language-specific part of the models-as-data implementation found in `Shared.qll`.
2+
* Contains the language-specific part of the models-as-data implementation found in `ApiGraphModels.qll`.
33
*
44
* It must export the following members:
55
* ```codeql
@@ -15,12 +15,15 @@
1515

1616
private import javascript as js
1717
private import js::DataFlow as DataFlow
18-
private import Shared
18+
private import ApiGraphModels
1919

2020
class Unit = js::Unit;
2121

2222
module API = js::API;
2323

24+
import semmle.javascript.frameworks.data.internal.AccessPathSyntax as AccessPathSyntax
25+
private import AccessPathSyntax
26+
2427
/**
2528
* Holds if models describing `package` may be relevant for the analysis of this database.
2629
*/
@@ -69,6 +72,10 @@ private API::Node getGlobalNode(string globalName) {
6972
/** Gets a JavaScript-specific interpretation of the `(package, type, path)` tuple after resolving the first `n` access path tokens. */
7073
bindingset[package, type, path]
7174
API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int n) {
75+
type = "" and
76+
n = 0 and
77+
result = API::moduleImport(package)
78+
or
7279
// Global variable accesses is via the 'global' package
7380
exists(AccessPathToken token |
7481
package = getAPackageAlias("global") and
@@ -89,6 +96,9 @@ API::Node getExtraNodeFromPath(string package, string type, AccessPath path, int
8996
*/
9097
bindingset[token]
9198
API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) {
99+
token.getName() = "Member" and
100+
result = node.getMember(token.getAnArgument())
101+
or
92102
token.getName() = "Instance" and
93103
result = node.getInstance()
94104
or
@@ -132,3 +142,50 @@ predicate invocationMatchesExtraCallSiteFilter(API::InvokeNode invoke, AccessPat
132142
invoke instanceof API::CallNode and
133143
invoke instanceof DataFlow::CallNode // Workaround compiler bug
134144
}
145+
146+
/**
147+
* Holds if `path` is an input or output spec for a summary with the given `base` node.
148+
*/
149+
pragma[nomagic]
150+
private predicate relevantInputOutputPath(API::InvokeNode base, AccessPath path) {
151+
ModelOutput::resolvedSummaryBase(base, path, _, _)
152+
or
153+
ModelOutput::resolvedSummaryBase(base, _, path, _)
154+
}
155+
156+
/**
157+
* Gets the API node for the first `n` tokens of the given input/output path, evaluated relative to `baseNode`.
158+
*/
159+
private API::Node getNodeFromInputOutputPath(API::InvokeNode baseNode, AccessPath path, int n) {
160+
relevantInputOutputPath(baseNode, path) and
161+
(
162+
n = 1 and
163+
result = getSuccessorFromInvoke(baseNode, path.getToken(0))
164+
or
165+
result =
166+
getSuccessorFromNode(getNodeFromInputOutputPath(baseNode, path, n - 1), path.getToken(n - 1))
167+
)
168+
}
169+
170+
/**
171+
* Gets the API node for the given input/output path, evaluated relative to `baseNode`.
172+
*/
173+
private API::Node getNodeFromInputOutputPath(API::InvokeNode baseNode, AccessPath path) {
174+
result = getNodeFromInputOutputPath(baseNode, path, path.getNumToken())
175+
}
176+
177+
/**
178+
* Holds if a CSV summary contributed the step `pred -> succ` of the given `kind`.
179+
*/
180+
predicate summaryStep(API::Node pred, API::Node succ, string kind) {
181+
exists(API::InvokeNode base, AccessPath input, AccessPath output |
182+
ModelOutput::resolvedSummaryBase(base, input, output, kind) and
183+
pred = getNodeFromInputOutputPath(base, input) and
184+
succ = getNodeFromInputOutputPath(base, output)
185+
)
186+
}
187+
188+
class InvokeNode = API::InvokeNode;
189+
190+
/** Gets an `InvokeNode` corresponding to an invocation of `node`. */
191+
InvokeNode getAnInvocationOf(API::Node node) { result = node.getAnInvocation() }

0 commit comments

Comments
 (0)