Skip to content

Commit a908b21

Browse files
committed
more backtracking of def nodes, and lots of tests
1 parent 038b032 commit a908b21

File tree

3 files changed

+50
-15
lines changed

3 files changed

+50
-15
lines changed

python/ql/lib/semmle/python/ApiGraphs.qll

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,14 @@ module API {
462462
lbl = Label::memberFromRef(pw) and
463463
rhs = pw.getValue()
464464
)
465+
or
466+
// TODO: I had expected `DataFlow::AttrWrite` to contain the attribute writes from a dict, that's how JS works.
467+
exists(Dict dict, KeyValuePair item |
468+
dict = pred.asExpr() and
469+
dict.getItem(_) = item and
470+
lbl = Label::member(item.getKey().(StrConst).getS()) and
471+
rhs.asExpr() = item.getValue()
472+
)
465473
// or
466474
// special case: from `require('m')` to an export of `prop` in `m`
467475
// TODO: Figure out if this is needed.
@@ -528,7 +536,7 @@ module API {
528536
|
529537
// Referring to an attribute on a node that is a use of `base`:
530538
lbl = Label::memberFromRef(ref) and
531-
ref = pred.getAnAttributeReference()
539+
ref = pred.getAnAttributeReference() // TODO: Change to read.
532540
or
533541
// Calling a node that is a use of `base`
534542
lbl = Label::return() and
@@ -548,20 +556,12 @@ module API {
548556
)
549557
)
550558
or
551-
exists(DataFlow::Node def, CallableExpr fn |
559+
exists(DataFlow::Node def, CallableExpr fn, int i |
552560
rhs(base, def) and fn = trackDefNode(def).asExpr()
553561
|
554-
exists(int i |
555-
lbl = Label::parameter(i) and
556-
ref.asExpr() = fn.getInnerScope().getArg(i)
557-
)
558-
/*
559-
* or // TODO: Figure out self. (and arg = -2, that might be a thing in python)
560-
* lbl = Label::receiver() and
561-
* ref = fn.getReceiver()
562-
*/
563-
564-
)
562+
lbl = Label::parameter(i) and
563+
ref.asExpr() = fn.getInnerScope().getArg(i)
564+
)
565565
or
566566
/*
567567
* or // TODO: Figure out classes.
@@ -771,7 +771,8 @@ module API {
771771
ImportStar::namePossiblyDefinedInImportStar(_, member, _) or
772772
Impl::prefix_member(_, member, _) or
773773
exists(any(Module mod).getSubModule(member)) or
774-
exports(_, member, _)
774+
exports(_, member, _) or
775+
member = any(Dict d).getAnItem().(KeyValuePair).getKey().(StrConst).getS()
775776
} or
776777
MkLabelUnknownMember() or
777778
MkLabelParameter(int i) {

python/ql/test/library-tests/ApiGraphs/deftest1.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,32 @@ def callback(x): #$ use=moduleImport("mypkg").getMember("foo").getMember("bar").
44
x.baz() #$ use=moduleImport("mypkg").getMember("foo").getMember("bar").getParameter(0).getParameter(0).getMember("baz").getReturn()
55

66
foo.bar(callback) #$ def=moduleImport("mypkg").getMember("foo").getMember("bar").getParameter(0) use=moduleImport("mypkg").getMember("foo").getMember("bar").getReturn()
7+
8+
def callback2(x): #$ use=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("c").getParameter(0)
9+
x.baz2() #$ use=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("c").getParameter(0).getMember("baz2").getReturn()
10+
11+
mydict = {
12+
"c": callback2, #$ def=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("c")
13+
"other": "whatever" #$ def=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("other")
14+
}
15+
16+
foo.baz(mydict) #$ def=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0) use=moduleImport("mypkg").getMember("foo").getMember("baz").getReturn()
17+
18+
def callback3(x): #$ use=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("third").getParameter(0)
19+
x.baz3() #$ use=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("third").getParameter(0).getMember("baz3").getReturn()
20+
21+
mydict.third = callback3 #$ def=moduleImport("mypkg").getMember("foo").getMember("baz").getParameter(0).getMember("third")
22+
23+
foo.blab(mydict) #$ def=moduleImport("mypkg").getMember("foo").getMember("blab").getParameter(0) use=moduleImport("mypkg").getMember("foo").getMember("blab").getReturn()
24+
25+
def callback4(x): #$ use=moduleImport("mypkg").getMember("foo").getMember("quack").getParameter(0).getParameter(0)
26+
x.baz4() #$ use=moduleImport("mypkg").getMember("foo").getMember("quack").getParameter(0).getParameter(0).getMember("baz4").getReturn()
27+
28+
otherDict = {
29+
# TODO: Backtracking through a property set using a dict doesn't work, but I can backtrack through explicit property writes, e.g. the `otherDict.fourth` below.
30+
# TODO: There is a related TODO in ApiGraphs.qll
31+
"blab": "whatever"
32+
}
33+
otherDict.fourth = callback4
34+
35+
foo.quack(otherDict.fourth) #$ def=moduleImport("mypkg").getMember("foo").getMember("quack").getParameter(0) use=moduleImport("mypkg").getMember("foo").getMember("quack").getReturn()
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
value = 3 #$ def=moduleImport("mypkg").getMember("foo").getMember("value")
1+
value = 3 #$ def=moduleImport("mypkg").getMember("foo").getMember("value")
2+
3+
class MyClass: #$ def=moduleImport("mypkg").getMember("foo").getMember("MyClass")
4+
def myFunc(self, x): #$ def=moduleImport("mypkg").getMember("foo").getMember("MyClass").getMember("myFunc") use=moduleImport("mypkg").getMember("foo").getMember("MyClass").getMember("myFunc").getParameter(1)
5+
self.selfThing() #$ use=moduleImport("mypkg").getMember("foo").getMember("MyClass").getMember("myFunc").getParameter(0).getMember("selfThing").getReturn()
6+
x.xThing() #$ use=moduleImport("mypkg").getMember("foo").getMember("MyClass").getMember("myFunc").getParameter(1).getMember("xThing").getReturn()

0 commit comments

Comments
 (0)