From b6648def1975ab57ef51447a2ab769a6ad806ab1 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 26 Feb 2019 12:18:55 +0000 Subject: [PATCH 1/2] JS: Add ClassNode.getAReceiverNode --- javascript/ql/src/semmle/javascript/dataflow/Nodes.qll | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll index 64cbe8bc5576..0d841e3a9650 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/Nodes.qll @@ -650,6 +650,15 @@ class ClassNode extends DataFlow::SourceNode { .(AbstractCallable) .getFunction() } + + /** + * Gets the receiver of an instance member or constructor of this class. + */ + DataFlow::SourceNode getAReceiverNode() { + result = getConstructor().getReceiver() + or + result = getAnInstanceMember().getReceiver() + } } module ClassNode { From 9497199cbdeed990c74f9be8740e430f976a2990 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 26 Feb 2019 12:24:05 +0000 Subject: [PATCH 2/2] JS: add localFieldStep --- .../semmle/javascript/dataflow/DataFlow.qll | 10 ++++++++++ .../library-tests/ClassNode/FieldStep.expected | 3 +++ .../test/library-tests/ClassNode/FieldStep.ql | 5 +++++ .../ClassNode/InstanceMember.expected | 2 ++ .../ClassNode/InstanceMethod.expected | 1 + .../ClassNode/getAReceiverNode.expected | 18 ++++++++++++++++++ .../ClassNode/getAReceiverNode.ql | 4 ++++ .../ql/test/library-tests/ClassNode/tst2.js | 14 ++++++++++++++ 8 files changed, 57 insertions(+) create mode 100644 javascript/ql/test/library-tests/ClassNode/FieldStep.expected create mode 100644 javascript/ql/test/library-tests/ClassNode/FieldStep.ql create mode 100644 javascript/ql/test/library-tests/ClassNode/getAReceiverNode.expected create mode 100644 javascript/ql/test/library-tests/ClassNode/getAReceiverNode.ql create mode 100644 javascript/ql/test/library-tests/ClassNode/tst2.js diff --git a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll index 87feadd43610..b58e85e838af 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll @@ -1078,6 +1078,16 @@ module DataFlow { ) } + /** + * Holds if there is a step from `pred` to `succ` through a field accessed through `this` in a class. + */ + predicate localFieldStep(DataFlow::Node pred, DataFlow::Node succ) { + exists (ClassNode cls, string prop | + pred = cls.getAReceiverNode().getAPropertyWrite(prop).getRhs() and + succ = cls.getAReceiverNode().getAPropertyRead(prop) + ) + } + /** * Gets the data flow node representing the source of definition `def`, taking * flow through IIFE calls into account. diff --git a/javascript/ql/test/library-tests/ClassNode/FieldStep.expected b/javascript/ql/test/library-tests/ClassNode/FieldStep.expected new file mode 100644 index 000000000000..23feb07fbfaf --- /dev/null +++ b/javascript/ql/test/library-tests/ClassNode/FieldStep.expected @@ -0,0 +1,3 @@ +| tst2.js:3:14:3:14 | x | tst2.js:7:5:7:10 | this.x | +| tst2.js:3:14:3:14 | x | tst2.js:8:25:8:30 | this.x | +| tst2.js:3:14:3:14 | x | tst2.js:12:12:12:17 | this.x | diff --git a/javascript/ql/test/library-tests/ClassNode/FieldStep.ql b/javascript/ql/test/library-tests/ClassNode/FieldStep.ql new file mode 100644 index 000000000000..75dbd42e4f80 --- /dev/null +++ b/javascript/ql/test/library-tests/ClassNode/FieldStep.ql @@ -0,0 +1,5 @@ +import javascript + +from DataFlow::Node pred, DataFlow::Node succ +where DataFlow::localFieldStep(pred, succ) +select pred, succ diff --git a/javascript/ql/test/library-tests/ClassNode/InstanceMember.expected b/javascript/ql/test/library-tests/ClassNode/InstanceMember.expected index d9d9bfdda342..1566c4ff9c20 100644 --- a/javascript/ql/test/library-tests/ClassNode/InstanceMember.expected +++ b/javascript/ql/test/library-tests/ClassNode/InstanceMember.expected @@ -1,4 +1,6 @@ | namespace.js:5:32:5:44 | function() {} | Baz.method | method | +| tst2.js:6:9:9:3 | () {\\n ... .x;\\n } | C.method | method | +| tst2.js:11:13:13:3 | () {\\n ... .x;\\n } | C.getter | getter | | tst.js:4:17:4:21 | () {} | A.instanceMethod | method | | tst.js:7:6:7:10 | () {} | A.bar | method | | tst.js:9:10:9:14 | () {} | A.baz | getter | diff --git a/javascript/ql/test/library-tests/ClassNode/InstanceMethod.expected b/javascript/ql/test/library-tests/ClassNode/InstanceMethod.expected index ac97e60da684..d0406361e6bb 100644 --- a/javascript/ql/test/library-tests/ClassNode/InstanceMethod.expected +++ b/javascript/ql/test/library-tests/ClassNode/InstanceMethod.expected @@ -1,4 +1,5 @@ | namespace.js:5:32:5:44 | function() {} | Baz.method | +| tst2.js:6:9:9:3 | () {\\n ... .x;\\n } | C.method | | tst.js:4:17:4:21 | () {} | A.instanceMethod | | tst.js:7:6:7:10 | () {} | A.bar | | tst.js:17:19:17:31 | function() {} | B.foo | diff --git a/javascript/ql/test/library-tests/ClassNode/getAReceiverNode.expected b/javascript/ql/test/library-tests/ClassNode/getAReceiverNode.expected new file mode 100644 index 000000000000..c17b1db03a4d --- /dev/null +++ b/javascript/ql/test/library-tests/ClassNode/getAReceiverNode.expected @@ -0,0 +1,18 @@ +| namespace.js:3:15:3:31 | function Baz() {} | namespace.js:3:15:3:14 | this | +| namespace.js:3:15:3:31 | function Baz() {} | namespace.js:5:32:5:31 | this | +| tst2.js:1:1:14:1 | class C ... ;\\n }\\n} | tst2.js:2:14:2:13 | this | +| tst2.js:1:1:14:1 | class C ... ;\\n }\\n} | tst2.js:6:9:6:8 | this | +| tst2.js:1:1:14:1 | class C ... ;\\n }\\n} | tst2.js:11:13:11:12 | this | +| tst.js:3:1:10:1 | class A ... () {}\\n} | tst.js:3:9:3:8 | this | +| tst.js:3:1:10:1 | class A ... () {}\\n} | tst.js:4:17:4:16 | this | +| tst.js:3:1:10:1 | class A ... () {}\\n} | tst.js:7:6:7:5 | this | +| tst.js:3:1:10:1 | class A ... () {}\\n} | tst.js:9:10:9:9 | this | +| tst.js:13:1:13:21 | class A ... ds A {} | tst.js:13:20:13:19 | this | +| tst.js:15:1:15:15 | function B() {} | tst.js:15:1:15:0 | this | +| tst.js:15:1:15:15 | function B() {} | tst.js:17:19:17:18 | this | +| tst.js:19:1:19:15 | function C() {} | tst.js:19:1:19:0 | this | +| tst.js:19:1:19:15 | function C() {} | tst.js:21:19:21:18 | this | +| tst.js:23:1:23:15 | function D() {} | tst.js:23:1:23:0 | this | +| tst.js:23:1:23:15 | function D() {} | tst.js:25:13:25:12 | this | +| tst.js:23:1:23:15 | function D() {} | tst.js:26:13:26:12 | this | +| tst.js:23:1:23:15 | function D() {} | tst.js:27:4:27:3 | this | diff --git a/javascript/ql/test/library-tests/ClassNode/getAReceiverNode.ql b/javascript/ql/test/library-tests/ClassNode/getAReceiverNode.ql new file mode 100644 index 000000000000..97428ff54c74 --- /dev/null +++ b/javascript/ql/test/library-tests/ClassNode/getAReceiverNode.ql @@ -0,0 +1,4 @@ +import javascript + +from DataFlow::ClassNode cls +select cls, cls.getAReceiverNode() diff --git a/javascript/ql/test/library-tests/ClassNode/tst2.js b/javascript/ql/test/library-tests/ClassNode/tst2.js new file mode 100644 index 000000000000..88746d8fcd40 --- /dev/null +++ b/javascript/ql/test/library-tests/ClassNode/tst2.js @@ -0,0 +1,14 @@ +class C { + constructor(x) { + this.x = x; + } + + method() { + this.x; + let closure = () => this.x; + } + + get getter() { + return this.x; + } +}