Skip to content

Commit f3b14e1

Browse files
authored
Merge pull request github#12841 from asgerf/rb/api-graph-class-nodes
Ruby: add API node representing a module/class object
2 parents 239a763 + e180b7e commit f3b14e1

File tree

3 files changed

+53
-14
lines changed

3 files changed

+53
-14
lines changed

ruby/ql/lib/codeql/ruby/ApiGraphs.qll

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,11 @@ module API {
359359
Location getLocation() {
360360
result = this.getInducingNode().getLocation()
361361
or
362+
exists(DataFlow::ModuleNode mod |
363+
this = Impl::MkModuleObject(mod) and
364+
result = mod.getLocation()
365+
)
366+
or
362367
// For nodes that do not have a meaningful location, `path` is the empty string and all other
363368
// parameters are zero.
364369
not exists(this.getInducingNode()) and
@@ -601,7 +606,9 @@ module API {
601606
/** A use of an API member at the node `nd`. */
602607
MkUse(DataFlow::Node nd) { isUse(nd) } or
603608
/** A value that escapes into an external library at the node `nd` */
604-
MkDef(DataFlow::Node nd) { isDef(nd) }
609+
MkDef(DataFlow::Node nd) { isDef(nd) } or
610+
/** A module object seen as a use node. */
611+
MkModuleObject(DataFlow::ModuleNode mod)
605612

606613
private string resolveTopLevel(ConstantReadAccess read) {
607614
result = read.getModule().getQualifiedName() and
@@ -684,7 +691,14 @@ module API {
684691
* Holds if `ref` is a use of node `nd`.
685692
*/
686693
cached
687-
predicate use(TApiNode nd, DataFlow::Node ref) { nd = MkUse(ref) }
694+
predicate use(TApiNode nd, DataFlow::Node ref) {
695+
nd = MkUse(ref)
696+
or
697+
exists(DataFlow::ModuleNode mod |
698+
nd = MkModuleObject(mod) and
699+
ref = mod.getAnImmediateReference()
700+
)
701+
}
688702

689703
/**
690704
* Holds if `rhs` is a RHS of node `nd`.
@@ -802,6 +816,14 @@ module API {
802816
trackUseNode(use).flowsTo(call.getReceiver())
803817
}
804818

819+
/**
820+
* Holds if `superclass` is the superclass of `mod`.
821+
*/
822+
pragma[nomagic]
823+
private predicate superclassNode(DataFlow::ModuleNode mod, DataFlow::Node superclass) {
824+
superclass.asExpr().getExpr() = mod.getADeclaration().(ClassDeclaration).getSuperclassExpr()
825+
}
826+
805827
/**
806828
* Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`.
807829
*/
@@ -813,38 +835,35 @@ module API {
813835
useRoot(lbl, ref)
814836
or
815837
exists(DataFlow::Node node, DataFlow::Node src |
816-
pred = MkUse(src) and
838+
use(pred, src) and
817839
trackUseNode(src).flowsTo(node) and
818840
useStep(lbl, node, ref)
819841
)
820842
or
821843
exists(DataFlow::Node callback |
822-
pred = MkDef(callback) and
844+
def(pred, callback) and
823845
parameterStep(lbl, trackDefNode(callback), ref)
824846
)
825847
)
826848
or
827849
exists(DataFlow::Node predNode, DataFlow::Node succNode |
828850
def(pred, predNode) and
829-
def(succ, succNode) and
851+
succ = MkDef(succNode) and
830852
defStep(lbl, trackDefNode(predNode), succNode)
831853
)
832854
or
833-
// `pred` is a use of class A
834-
// `succ` is a use of class B
835-
// there exists a class declaration B < A
836-
exists(ClassDeclaration c, DataFlow::Node a, DataFlow::Node b |
837-
use(pred, a) and
838-
use(succ, b) and
839-
b.asExpr().getExpr().(ConstantReadAccess).getAQualifiedName() = c.getAQualifiedName() and
840-
pragma[only_bind_into](c).getSuperclassExpr() = a.asExpr().getExpr() and
855+
exists(DataFlow::Node predNode, DataFlow::Node superclassNode, DataFlow::ModuleNode mod |
856+
use(pred, predNode) and
857+
trackUseNode(predNode).flowsTo(superclassNode) and
858+
superclassNode(mod, superclassNode) and
859+
succ = MkModuleObject(mod) and
841860
lbl = Label::subclass()
842861
)
843862
or
844863
exists(DataFlow::CallNode call |
845864
// from receiver to method call node
846865
exists(DataFlow::Node receiver |
847-
pred = MkUse(receiver) and
866+
use(pred, receiver) and
848867
useNodeReachesReceiver(receiver, call) and
849868
lbl = Label::method(call.getMethodName()) and
850869
succ = MkMethodAccessNode(call)

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,9 @@ class ModuleNode instanceof Module {
890890
/** Gets a constant or `self` variable that refers to this module. */
891891
LocalSourceNode getAnImmediateReference() {
892892
result.asExpr().getExpr() = super.getAnImmediateReference()
893+
or
894+
// Include 'self' parameters; these are not expressions and so not found by the case above
895+
result = this.getAnOwnModuleSelf()
893896
}
894897

895898
/**

ruby/ql/test/library-tests/dataflow/helpers/dataflow.expected

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,28 @@ getModuleLevelSelf
127127
getAnImmediateReference
128128
| file://:0:0:0:0 | Array | tst.rb:59:15:59:21 | Array |
129129
| file://:0:0:0:0 | Hash | tst.rb:60:14:60:45 | Hash |
130+
| tst.rb:1:1:6:3 | C1 | tst.rb:1:1:6:3 | self (C1) |
130131
| tst.rb:1:1:6:3 | C1 | tst.rb:8:12:8:13 | C1 |
132+
| tst.rb:8:1:11:3 | C2 | tst.rb:8:1:11:3 | self (C2) |
131133
| tst.rb:8:1:11:3 | C2 | tst.rb:27:12:27:13 | C2 |
134+
| tst.rb:13:1:18:3 | Mixin | tst.rb:13:1:18:3 | self (Mixin) |
135+
| tst.rb:13:1:18:3 | Mixin | tst.rb:16:5:17:7 | self in m1s |
132136
| tst.rb:13:1:18:3 | Mixin | tst.rb:28:13:28:17 | Mixin |
137+
| tst.rb:20:1:25:3 | Mixin2 | tst.rb:20:1:25:3 | self (Mixin2) |
138+
| tst.rb:20:1:25:3 | Mixin2 | tst.rb:23:5:24:7 | self in m2s |
133139
| tst.rb:20:1:25:3 | Mixin2 | tst.rb:29:13:29:18 | Mixin2 |
140+
| tst.rb:27:1:35:3 | C3 | tst.rb:27:1:35:3 | self (C3) |
141+
| tst.rb:27:1:35:3 | C3 | tst.rb:32:9:33:11 | self in c3_self1 |
142+
| tst.rb:27:1:35:3 | C3 | tst.rb:37:1:38:3 | self in c3_self2 |
134143
| tst.rb:27:1:35:3 | C3 | tst.rb:37:5:37:6 | C3 |
144+
| tst.rb:40:1:47:3 | N1 | tst.rb:40:1:47:3 | self (N1) |
145+
| tst.rb:41:5:42:7 | N1::XY1 | tst.rb:41:5:42:7 | self (XY1) |
146+
| tst.rb:43:5:46:7 | N1::N2 | tst.rb:43:5:46:7 | self (N2) |
147+
| tst.rb:44:9:45:11 | N1::N2::XY2 | tst.rb:44:9:45:11 | self (XY2) |
148+
| tst.rb:49:1:51:3 | N2 | tst.rb:49:1:51:3 | self (N2) |
149+
| tst.rb:49:1:51:3 | N2 | tst.rb:52:1:55:3 | self (N2) |
150+
| tst.rb:53:5:54:7 | N2::XY3 | tst.rb:53:5:54:7 | self (XY3) |
151+
| tst.rb:57:1:62:3 | Nodes | tst.rb:57:1:62:3 | self (Nodes) |
135152
getOwnInstanceMethod
136153
| tst.rb:1:1:6:3 | C1 | c1 | tst.rb:2:5:5:7 | c1 |
137154
| tst.rb:8:1:11:3 | C2 | c2 | tst.rb:9:5:10:7 | c2 |

0 commit comments

Comments
 (0)