Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0cf2eae

Browse files
author
Esben Sparre Andreasen
committed
JS: introduce CapturedSource
1 parent bfbf686 commit 0cf2eae

12 files changed

Lines changed: 312 additions & 0 deletions
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Provides classes for the nodes that the dataflow library can reason about soundly.
3+
*/
4+
5+
import javascript
6+
7+
/**
8+
* Holds if the dataflow library can not track flow through `escape` due to `cause`.
9+
*/
10+
private predicate isEscape(DataFlow::Node escape, string cause) {
11+
escape = any(DataFlow::InvokeNode invk).getAnArgument() and cause = "argument"
12+
or
13+
escape = any(DataFlow::FunctionNode fun).getAReturn() and cause = "return"
14+
or
15+
escape = any(ThrowStmt t).getExpr().flow() and cause = "throw"
16+
or
17+
escape = any(DataFlow::GlobalVariable v).getAnAssignedExpr().flow() and cause = "global"
18+
or
19+
escape = any(DataFlow::PropWrite write).getRhs() and cause = "heap"
20+
or
21+
escape = any(ExportDeclaration e).getSourceNode(_) and cause = "export"
22+
or
23+
any(WithStmt with).mayAffect(escape.asExpr()) and cause = "heap"
24+
}
25+
26+
private DataFlow::Node getAnEscape() {
27+
isEscape(result, _)
28+
}
29+
30+
/**
31+
* Holds if `n` can flow to a `this`-variable.
32+
*/
33+
private predicate exposedAsReceiver(DataFlow::SourceNode n) {
34+
// pragmatic limitation: guarantee for object literals only
35+
not n instanceof DataFlow::ObjectLiteralNode
36+
or
37+
exists(AbstractValue v | n.getAPropertyWrite().getRhs().analyze().getALocalValue() = v |
38+
v.isIndefinite(_) or
39+
exists(ThisExpr dis | dis.getBinder() = v.(AbstractCallable).getFunction())
40+
)
41+
or
42+
n.flowsToExpr(any(FunctionBindExpr bind).getObject())
43+
or
44+
// technically, the builtin prototypes could have a `this`-using function through which this node escapes, but we ignore that here
45+
// (we also ignore `o['__' + 'proto__"] = ...`)
46+
exists(n.getAPropertyWrite("__proto__"))
47+
or
48+
// could check the assigned value of all affected variables, but it is unlikely to matter in practice
49+
exists(WithStmt with | n.flowsToExpr(with.getExpr()))
50+
}
51+
52+
/**
53+
* A source for which the flow is entirely captured by the dataflow library.
54+
* All uses of the node is represented by `this.flowsTo(_)` and friends.
55+
*/
56+
class CapturedSource extends DataFlow::SourceNode {
57+
CapturedSource() {
58+
// pragmatic limitation: object literals only
59+
this instanceof DataFlow::ObjectLiteralNode and
60+
not flowsTo(getAnEscape()) and
61+
not exposedAsReceiver(this)
62+
}
63+
64+
predicate hasOwnProperty(string name) {
65+
// the property is defined in the initializer,
66+
any(DataFlow::PropWrite write).writes(this, name, _) and
67+
// and it is never deleted
68+
not exists(DeleteExpr del, DataFlow::PropRef ref |
69+
del.getOperand().flow() = ref and
70+
flowsTo(ref.getBase()) and
71+
(ref.getPropertyName() = name or not exists(ref.getPropertyName()))
72+
)
73+
}
74+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} |
2+
| method-calls.js:11:23:11:24 | {} |
3+
| method-calls.js:16:11:16:12 | {} |
4+
| method-calls.js:23:11:23:17 | {m: f1} |
5+
| method-calls.js:27:11:27:17 | {m: f2} |
6+
| method-calls.js:32:12:32:18 | {m: f3} |
7+
| method-calls.js:36:15:36:21 | {m: f2} |
8+
| method-calls.js:42:16:42:28 | {m: () => 42} |
9+
| method-calls.js:46:17:46:29 | {m: () => 42} |
10+
| method-calls.js:50:16:50:28 | {m: () => 42} |
11+
| method-calls.js:53:16:53:28 | {m: () => 42} |
12+
| tst.js:3:18:3:19 | {} |
13+
| tst.js:4:18:4:36 | { f: function(){} } |
14+
| tst.js:5:18:5:34 | { [unknown]: 42 } |
15+
| tst.js:38:18:38:26 | { p: 42 } |
16+
| tst.js:42:18:42:33 | { p: 42, q: 42 } |
17+
| tst.js:52:23:52:24 | {} |
18+
| tst.js:56:20:56:35 | { p: 42, p: 42 } |
19+
| tst.js:59:20:59:28 | { p: 42 } |
20+
| tst.js:63:20:63:28 | { p: 42 } |
21+
| tst.js:68:19:68:27 | { p: 42 } |
22+
| tst.js:73:18:73:20 | { } |
23+
| tst.js:76:18:76:26 | { p: 42 } |
24+
| tst.js:77:18:77:28 | { p: true } |
25+
| tst.js:82:20:82:21 | {} |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
import semmle.javascript.dataflow.CapturedNodes
3+
4+
select any(CapturedSource n)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} | m1 |
2+
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} | m2 |
3+
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} | m3 |
4+
| method-calls.js:2:11:7:2 | {\\n\\t\\tm1: ... }; }\\n\\t} | m4 |
5+
| method-calls.js:23:11:23:17 | {m: f1} | m |
6+
| method-calls.js:27:11:27:17 | {m: f2} | m |
7+
| method-calls.js:32:12:32:18 | {m: f3} | m |
8+
| method-calls.js:36:15:36:21 | {m: f2} | m |
9+
| method-calls.js:42:16:42:28 | {m: () => 42} | m |
10+
| method-calls.js:46:17:46:29 | {m: () => 42} | m |
11+
| method-calls.js:50:16:50:28 | {m: () => 42} | m |
12+
| method-calls.js:53:16:53:28 | {m: () => 42} | m |
13+
| tst.js:4:18:4:36 | { f: function(){} } | f |
14+
| tst.js:38:18:38:26 | { p: 42 } | p |
15+
| tst.js:42:18:42:33 | { p: 42, q: 42 } | p |
16+
| tst.js:42:18:42:33 | { p: 42, q: 42 } | q |
17+
| tst.js:56:20:56:35 | { p: 42, p: 42 } | p |
18+
| tst.js:59:20:59:28 | { p: 42 } | p |
19+
| tst.js:63:20:63:28 | { p: 42 } | p |
20+
| tst.js:68:19:68:27 | { p: 42 } | p |
21+
| tst.js:76:18:76:26 | { p: 42 } | p |
22+
| tst.js:77:18:77:28 | { p: true } | p |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import javascript
2+
import semmle.javascript.dataflow.CapturedNodes
3+
4+
from CapturedSource src, string name
5+
where src.hasOwnProperty(name)
6+
select src, name
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
| method-calls.js:8:2:8:8 | o1.m1() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
2+
| method-calls.js:9:2:9:8 | o1.m2() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
3+
| method-calls.js:12:2:12:7 | o.m3() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
4+
| method-calls.js:19:2:19:7 | o2.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
5+
| method-calls.js:23:11:23:21 | {m: f1}.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
6+
| method-calls.js:28:11:28:16 | o2.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
7+
| method-calls.js:32:11:32:23 | ({m: f3}).m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
8+
| method-calls.js:37:11:37:16 | o4.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
9+
| method-calls.js:43:12:43:16 | o.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
10+
| method-calls.js:47:12:47:16 | o.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
11+
| method-calls.js:51:12:51:16 | o.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
12+
| method-calls.js:54:12:54:16 | o.m() | boolean, class, date, function, null, number, object, regular expression,string or undefined |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from DataFlow::MethodCallNode call
4+
select call, call.analyze().ppTypes()
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| method-calls.js:23:11:23:21 | {m: f1}.m() | method-calls.js:24:2:24:3 | v1 | file://:0:0:0:0 | indefinite value (call) |
2+
| method-calls.js:28:11:28:16 | o2.m() | method-calls.js:29:2:29:3 | v2 | file://:0:0:0:0 | indefinite value (call) |
3+
| method-calls.js:32:11:32:23 | ({m: f3}).m() | method-calls.js:33:2:33:3 | v3 | file://:0:0:0:0 | indefinite value (call) |
4+
| method-calls.js:37:11:37:16 | o4.m() | method-calls.js:38:2:38:3 | v4 | file://:0:0:0:0 | indefinite value (call) |
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import javascript
2+
3+
from DataFlow::MethodCallNode call, DataFlow::Node use
4+
where call.flowsTo(use) and use != call and not exists(use.getASuccessor())
5+
select call, use, use.analyze().getAValue()
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
(function() {
2+
var o1 = {
3+
m1: function(){ return {}; },
4+
m2: function(){ return {}; },
5+
m3: function(){ return {}; },
6+
m4: function(){ return {}; }
7+
};
8+
o1.m1();
9+
o1.m2();
10+
unknown(o1.m2);
11+
var o = unknown? o1: {};
12+
o.m3(); // NOT supported
13+
var m4 = o.m4;
14+
m4();
15+
16+
var o2 = {};
17+
o2.m = function() { return {}; };
18+
o2[unknown] = function() { return true; }; // could be __proto__
19+
o2.m();
20+
});
21+
(function(){
22+
function f1(){return {};}
23+
var v1 = {m: f1}.m();
24+
v1 === true;
25+
26+
function f2(){return {};}
27+
var o2 = {m: f2};
28+
var v2 = o2.m();
29+
v2 === true;
30+
31+
function f3(){return {};}
32+
var v3 = ({m: f3}).m();
33+
v3 === true;
34+
35+
function f4(){return {};}
36+
var { o4 } = {m: f2};
37+
var v4 = o4.m();
38+
v4 === true;
39+
});
40+
41+
(function(){
42+
(function(o = {m: () => 42}){
43+
var v1 = o.m();
44+
})(unknown);
45+
46+
function f(o = {m: () => 42}){
47+
var v2 = o.m();
48+
};
49+
f(unknown);
50+
(function(o = {m: () => 42}){
51+
var v3 = o.m();
52+
})({m: unknown});
53+
(function(o = {m: () => 42}){
54+
var v4 = o.m();
55+
})({m: () => true});
56+
57+
});

0 commit comments

Comments
 (0)