Skip to content

Commit 00ef80d

Browse files
author
Esben Sparre Andreasen
authored
Merge pull request #741 from asger-semmle/this-access-path
JS: support 'this' as the root of an access path
2 parents 50ce961 + 107ec3b commit 00ef80d

File tree

4 files changed

+68
-3
lines changed

4 files changed

+68
-3
lines changed

javascript/ql/src/semmle/javascript/dataflow/internal/AccessPaths.qll

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ private PropertyName getPropertyName(PropAccess pacc) {
4444
* where each property name is either constant or itself an SSA variable.
4545
*/
4646
private newtype TAccessPath =
47-
MkRoot(SsaVariable var) or
47+
MkSsaRoot(SsaVariable var) or
48+
MkThisRoot(Function function) { function.getThisBinder() = function } or
4849
MkAccessStep(AccessPath base, PropertyName name) {
4950
exists(PropAccess pacc |
5051
pacc.getBase() = base.getAnInstance() and
@@ -62,10 +63,16 @@ class AccessPath extends TAccessPath {
6263
*/
6364
Expr getAnInstanceIn(BasicBlock bb) {
6465
exists(SsaVariable var |
65-
this = MkRoot(var) and
66+
this = MkSsaRoot(var) and
6667
result = var.getAUseIn(bb)
6768
)
6869
or
70+
exists(ThisExpr this_ |
71+
this = MkThisRoot(this_.getBinder()) and
72+
result = this_ and
73+
this_.getBasicBlock() = bb
74+
)
75+
or
6976
exists(PropertyName name |
7077
result = getABaseInstanceIn(bb, name) and
7178
getPropertyName(result) = name
@@ -96,7 +103,9 @@ class AccessPath extends TAccessPath {
96103
* Gets a textual representation of this access path.
97104
*/
98105
string toString() {
99-
exists(SsaVariable var | this = MkRoot(var) | result = var.getSourceVariable().getName())
106+
exists(SsaVariable var | this = MkSsaRoot(var) | result = var.getSourceVariable().getName())
107+
or
108+
this = MkThisRoot(_) and result = "this"
100109
or
101110
exists(AccessPath base, PropertyName name, string rest |
102111
rest = "." + any(string s | name = StaticPropertyName(s))

javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:51:14:51:14 | x |
1313
| promise.js:4:24:4:31 | source() | promise.js:4:8:4:32 | Promise ... urce()) |
1414
| promise.js:5:25:5:32 | source() | promise.js:5:8:5:33 | bluebir ... urce()) |
15+
| sanitizer-guards.js:2:11:2:18 | source() | sanitizer-guards.js:4:8:4:8 | x |
16+
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:15:10:15:15 | this.x |
17+
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:21:14:21:19 | this.x |
18+
| sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:26:9:26:14 | this.x |
1519
| thisAssignments.js:4:17:4:24 | source() | thisAssignments.js:5:10:5:18 | obj.field |
1620
| thisAssignments.js:7:19:7:26 | source() | thisAssignments.js:8:10:8:20 | this.field2 |
1721
| tst.js:2:13:2:20 | source() | tst.js:4:10:4:10 | x |

javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.ql

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@ class BasicConfig extends TaintTracking::Configuration {
88
override predicate isSource(DataFlow::Node node) { node = getACall("source") }
99

1010
override predicate isSink(DataFlow::Node node) { node = getACall("sink").getAnArgument() }
11+
12+
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode node) {
13+
node instanceof BasicSanitizerGuard
14+
}
15+
}
16+
17+
class BasicSanitizerGuard extends TaintTracking::SanitizerGuardNode, DataFlow::CallNode {
18+
BasicSanitizerGuard() { this = getACall("isSafe") }
19+
20+
override predicate sanitizes(boolean outcome, Expr e) {
21+
outcome = true and e = getArgument(0).asExpr()
22+
}
1123
}
1224

1325
from BasicConfig cfg, DataFlow::Node src, DataFlow::Node sink
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
function test() {
2+
let x = source();
3+
4+
sink(x); // NOT OK
5+
6+
if (isSafe(x)) {
7+
sink(x); // OK
8+
}
9+
}
10+
11+
class C {
12+
method() {
13+
this.x = source();
14+
15+
sink(this.x); // NOT OK
16+
17+
if (isSafe(this.x)) {
18+
sink(this.x); // OK
19+
20+
addEventListener('hey', () => {
21+
sink(this.x); // OK - but still flagged
22+
});
23+
}
24+
25+
addEventListener('hey', () => {
26+
sink(this.x); // NOT OK
27+
});
28+
29+
let self = this;
30+
if (isSafe(self.x)) {
31+
sink(self.x); // OK
32+
}
33+
34+
addEventListener('hey', function() {
35+
if (isSafe(self.x)) {
36+
sink(self.x); // OK
37+
}
38+
});
39+
}
40+
}

0 commit comments

Comments
 (0)