Skip to content

Commit 6e23a9a

Browse files
authored
Merge pull request #275 from github/hvitved/api-graphs-fix
API graphs: Fix bug for resolvable modules
2 parents 701eab7 + 03e91a2 commit 6e23a9a

File tree

2 files changed

+36
-16
lines changed

2 files changed

+36
-16
lines changed

ql/lib/codeql/ruby/ApiGraphs.qll

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,11 @@ module API {
253253
/** A use of an API member at the node `nd`. */
254254
MkUse(DataFlow::Node nd) { use(_, _, nd) }
255255

256+
private string resolveTopLevel(ConstantReadAccess read) {
257+
TResolved(result) = resolveScopeExpr(read) and
258+
not result.matches("%::%")
259+
}
260+
256261
/**
257262
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
258263
* `lbl` in the API graph.
@@ -265,58 +270,63 @@ module API {
265270
lbl = Label::member(read.getName()) and
266271
read = access.getExpr()
267272
|
268-
TResolved(name) = resolveScopeExpr(read) and
269-
not name.matches("%::%")
273+
name = resolveTopLevel(read)
270274
or
271275
name = read.getName() and
272-
not exists(resolveScopeExpr(read)) and
276+
not exists(resolveTopLevel(read)) and
273277
not exists(read.getScopeExpr())
274278
)
275279
or
276-
exists(DataFlow::LocalSourceNode src, DataFlow::LocalSourceNode pred |
280+
exists(ExprCfgNode node |
277281
// First, we find a predecessor of the node `ref` that we want to determine. The predecessor
278282
// is any node that is a type-tracked use of a data flow node (`src`), which is itself a
279283
// reference to the API node `base`. Thus, `pred` and `src` both represent uses of `base`.
280284
//
281285
// Once we have identified the predecessor, we define its relation to the successor `ref` as
282286
// well as the label on the edge from `pred` to `ref`. This label describes the nature of
283287
// the relationship between `pred` and `ref`.
284-
use(base, src) and pred = trackUseNode(src)
288+
useExpr(node, base)
285289
|
286290
// // Referring to an attribute on a node that is a use of `base`:
287291
// pred = `Rails` part of `Rails::Whatever`
288292
// lbl = `Whatever`
289293
// ref = `Rails::Whatever`
290-
exists(ExprNodes::ConstantAccessCfgNode c, DataFlow::Node node, ConstantReadAccess read |
291-
not exists(resolveScopeExpr(read)) and
292-
pred.flowsTo(node) and
293-
node.asExpr() = c.getScopeExpr() and
294+
exists(ExprNodes::ConstantAccessCfgNode c, ConstantReadAccess read |
295+
not exists(resolveTopLevel(read)) and
296+
node = c.getScopeExpr() and
294297
lbl = Label::member(read.getName()) and
295298
ref.asExpr() = c and
296299
read = c.getExpr()
297300
)
298301
or
299302
// Calling a method on a node that is a use of `base`
300-
exists(ExprNodes::MethodCallCfgNode call, DataFlow::Node node, string name |
301-
pred.flowsTo(node) and
302-
node.asExpr() = call.getReceiver() and
303+
exists(ExprNodes::MethodCallCfgNode call, string name |
304+
node = call.getReceiver() and
303305
name = call.getExpr().getMethodName() and
304306
lbl = Label::return(name) and
305307
name != "new" and
306308
ref.asExpr() = call
307309
)
308310
or
309311
// Calling the `new` method on a node that is a use of `base`, which creates a new instance
310-
exists(ExprNodes::MethodCallCfgNode call, DataFlow::Node node |
311-
pred.flowsTo(node) and
312-
node.asExpr() = call.getReceiver() and
312+
exists(ExprNodes::MethodCallCfgNode call |
313+
node = call.getReceiver() and
313314
lbl = Label::instance() and
314315
call.getExpr().getMethodName() = "new" and
315316
ref.asExpr() = call
316317
)
317318
)
318319
}
319320

321+
pragma[nomagic]
322+
private predicate useExpr(ExprCfgNode node, TApiNode base) {
323+
exists(DataFlow::LocalSourceNode src, DataFlow::LocalSourceNode pred |
324+
use(base, src) and
325+
pred = trackUseNode(src) and
326+
pred.flowsTo(any(DataFlow::ExprNode n | n.getExprNode() = node))
327+
)
328+
}
329+
320330
/**
321331
* Holds if `ref` is a use of node `nd`.
322332
*/
@@ -328,7 +338,10 @@ module API {
328338
*
329339
* The flow from `src` to that node may be inter-procedural.
330340
*/
331-
private DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src, TypeTracker t) {
341+
private DataFlow::LocalSourceNode trackUseNode(DataFlow::Node src, TypeTracker t) {
342+
// Declaring `src` to be a `SourceNode` currently causes a redundant check in the
343+
// recursive case, so instead we check it explicitly here.
344+
src instanceof DataFlow::LocalSourceNode and
332345
t.start() and
333346
use(_, src) and
334347
result = src

ql/test/library-tests/dataflow/api-graphs/test1.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,10 @@
2222

2323
FooAlias = Foo #$ use=getMember("Foo")
2424
FooAlias::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
25+
26+
module Outer
27+
module Inner
28+
end
29+
end
30+
31+
Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getReturn("foo")

0 commit comments

Comments
 (0)