Skip to content

Commit fec2837

Browse files
committed
JS: Ensure accessors do not appear to be calls
1 parent ddb682b commit fec2837

File tree

3 files changed

+71
-23
lines changed

3 files changed

+71
-23
lines changed

javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,13 @@ module DataFlow {
589589
* Gets the node where the property write happens in the control flow graph.
590590
*/
591591
abstract ControlFlowNode getWriteNode();
592+
593+
/**
594+
* If this installs an accessor on an object, as opposed to a regular property,
595+
* gets the body of the accessor. `isSetter` is true if installing a setter, and
596+
* false is installing a getter.
597+
*/
598+
DataFlow::FunctionNode getInstalledAccessor(boolean isSetter) { none() }
592599
}
593600

594601
/**
@@ -628,6 +635,17 @@ module DataFlow {
628635

629636
override Node getRhs() { result = valueNode(prop.(ValueProperty).getInit()) }
630637

638+
override DataFlow::FunctionNode getInstalledAccessor(boolean isSetter) {
639+
(
640+
prop instanceof PropertySetter and
641+
isSetter = true
642+
or
643+
prop instanceof PropertyGetter and
644+
isSetter = false
645+
) and
646+
result = valueNode(prop.getInit())
647+
}
648+
631649
override ControlFlowNode getWriteNode() { result = prop }
632650
}
633651

@@ -688,6 +706,17 @@ module DataFlow {
688706
result = valueNode(prop.getInit())
689707
}
690708

709+
override DataFlow::FunctionNode getInstalledAccessor(boolean isSetter) {
710+
(
711+
prop instanceof SetterMethodDefinition and
712+
isSetter = true
713+
or
714+
prop instanceof GetterMethodDefinition and
715+
isSetter = false
716+
) and
717+
result = valueNode(prop.getInit())
718+
}
719+
691720
override ControlFlowNode getWriteNode() { result = prop }
692721
}
693722

@@ -711,6 +740,17 @@ module DataFlow {
711740
result = valueNode(prop.getInit())
712741
}
713742

743+
override DataFlow::FunctionNode getInstalledAccessor(boolean isSetter) {
744+
(
745+
prop instanceof SetterMethodDefinition and
746+
isSetter = true
747+
or
748+
prop instanceof GetterMethodDefinition and
749+
isSetter = false
750+
) and
751+
result = valueNode(prop.getInit())
752+
}
753+
714754
override ControlFlowNode getWriteNode() { result = prop }
715755
}
716756

javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -189,31 +189,43 @@ module CallGraph {
189189
)
190190
}
191191

192+
/**
193+
* Holds if `ref` installs an accessor on an object. Such property writes should not
194+
* be considered calls to an accessor.
195+
*/
196+
pragma[nomagic]
197+
private predicate isAccessorInstallation(DataFlow::PropWrite write) {
198+
exists(write.getInstalledAccessor(_))
199+
}
200+
192201
/**
193202
* Gets a getter or setter invoked as a result of the given property access.
194203
*/
195204
cached
196205
DataFlow::FunctionNode getAnAccessorCallee(DataFlow::PropRef ref) {
197-
exists(DataFlow::ClassNode cls, string name |
198-
ref = cls.getAnInstanceMemberAccess(name) and
199-
result = cls.getInstanceMember(name, DataFlow::MemberKind::getter())
200-
or
201-
ref = getAnInstanceMemberAssignment(cls, name) and
202-
result = cls.getInstanceMember(name, DataFlow::MemberKind::setter())
203-
or
204-
ref = cls.getAClassReference().getAPropertyRead(name) and
205-
result = cls.getStaticMember(name, DataFlow::MemberKind::getter())
206-
or
207-
ref = cls.getAClassReference().getAPropertyWrite(name) and
208-
result = cls.getStaticMember(name, DataFlow::MemberKind::setter())
209-
)
210-
or
211-
exists(DataFlow::ObjectLiteralNode object, string name |
212-
ref = getAnAllocationSiteRef(object).getAPropertyRead(name) and
213-
result = object.getPropertyGetter(name)
206+
not isAccessorInstallation(ref) and
207+
(
208+
exists(DataFlow::ClassNode cls, string name |
209+
ref = cls.getAnInstanceMemberAccess(name) and
210+
result = cls.getInstanceMember(name, DataFlow::MemberKind::getter())
211+
or
212+
ref = getAnInstanceMemberAssignment(cls, name) and
213+
result = cls.getInstanceMember(name, DataFlow::MemberKind::setter())
214+
or
215+
ref = cls.getAClassReference().getAPropertyRead(name) and
216+
result = cls.getStaticMember(name, DataFlow::MemberKind::getter())
217+
or
218+
ref = cls.getAClassReference().getAPropertyWrite(name) and
219+
result = cls.getStaticMember(name, DataFlow::MemberKind::setter())
220+
)
214221
or
215-
ref = getAnAllocationSiteRef(object).getAPropertyWrite(name) and
216-
result = object.getPropertySetter(name)
222+
exists(DataFlow::ObjectLiteralNode object, string name |
223+
ref = getAnAllocationSiteRef(object).getAPropertyRead(name) and
224+
result = object.getPropertyGetter(name)
225+
or
226+
ref = getAnAllocationSiteRef(object).getAPropertyWrite(name) and
227+
result = object.getPropertySetter(name)
228+
)
217229
)
218230
}
219231

javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/Test.expected

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,8 @@ missingCallee
44
| constructor-field.ts:71:1:71:11 | bf3.build() | constructor-field.ts:13:3:13:12 | build() {} | -1 | calls |
55
badAnnotation
66
accessorCall
7-
| accessors.js:5:3:5:12 | get f() {} | accessors.js:8:8:8:13 | (x) {} |
8-
| accessors.js:8:3:8:13 | set f(x) {} | accessors.js:8:8:8:13 | (x) {} |
97
| accessors.js:12:1:12:5 | obj.f | accessors.js:5:8:5:12 | () {} |
108
| accessors.js:15:1:15:5 | obj.f | accessors.js:8:8:8:13 | (x) {} |
11-
| accessors.js:19:3:19:19 | static get f() {} | accessors.js:22:15:22:20 | (x) {} |
12-
| accessors.js:22:3:22:20 | static set f(x) {} | accessors.js:22:15:22:20 | (x) {} |
139
| accessors.js:26:1:26:3 | C.f | accessors.js:19:15:19:19 | () {} |
1410
| accessors.js:29:1:29:3 | C.f | accessors.js:22:15:22:20 | (x) {} |
1511
| accessors.js:41:1:41:9 | new D().f | accessors.js:34:8:34:12 | () {} |

0 commit comments

Comments
 (0)