Skip to content

Commit 5e712b3

Browse files
authored
Merge pull request #784 from asger-semmle/dedup-promiseTaintStep
Approved by esben-semmle
2 parents 6af8948 + a8d750f commit 5e712b3

File tree

4 files changed

+31
-86
lines changed

4 files changed

+31
-86
lines changed
Lines changed: 1 addition & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,9 @@
11
/**
2-
* Provides classes for working with promises.
2+
* Provides classes for modelling promise libraries.
33
*/
44

55
import javascript
66

7-
/**
8-
* A promise object created by the standard ECMAScript 2015 `Promise` constructor.
9-
*/
10-
private class ES2015PromiseDefinition extends PromiseDefinition, DataFlow::NewNode {
11-
ES2015PromiseDefinition() { this = DataFlow::globalVarRef("Promise").getAnInstantiation() }
12-
13-
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
14-
}
15-
16-
/**
17-
* A data flow edge from a promise reaction to the corresponding handler.
18-
*/
19-
private class PromiseFlowStep extends DataFlow::AdditionalFlowStep {
20-
PromiseDefinition p;
21-
22-
PromiseFlowStep() { this = p }
23-
24-
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
25-
pred = p.getResolveParameter().getACall().getArgument(0) and
26-
succ = p.getAResolveHandler().getParameter(0)
27-
or
28-
pred = p.getRejectParameter().getACall().getArgument(0) and
29-
succ = p.getARejectHandler().getParameter(0)
30-
}
31-
}
32-
33-
/**
34-
* Holds if taint propagates from `pred` to `succ` through promises.
35-
*/
36-
private predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
37-
// from `x` to `new Promise((res, rej) => res(x))`
38-
pred = succ.(PromiseDefinition).getResolveParameter().getACall().getArgument(0)
39-
or
40-
// from `x` to `Promise.resolve(x)`
41-
pred = succ.(ResolvedPromiseDefinition).getValue()
42-
or
43-
exists(DataFlow::MethodCallNode thn, DataFlow::FunctionNode cb |
44-
thn.getMethodName() = "then" and cb = thn.getCallback(0)
45-
|
46-
// from `p` to `x` in `p.then(x => ...)`
47-
pred = thn.getReceiver() and
48-
succ = cb.getParameter(0)
49-
or
50-
// from `v` to `p.then(x => return v)`
51-
pred = cb.getFunction().getAReturnedExpr().flow() and
52-
succ = thn
53-
)
54-
}
55-
56-
/**
57-
* An additional taint step that involves promises.
58-
*/
59-
private class PromiseTaintStep extends TaintTracking::AdditionalTaintStep {
60-
DataFlow::Node source;
61-
62-
PromiseTaintStep() { promiseTaintStep(source, this) }
63-
64-
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
65-
pred = source and succ = this
66-
}
67-
}
68-
697
/**
708
* Provides classes for working with the `bluebird` library (http://bluebirdjs.com).
719
*/
@@ -107,24 +45,3 @@ module Q {
10745
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
10846
}
10947
}
110-
111-
/**
112-
* A promise that is resolved with the given value.
113-
*/
114-
abstract class ResolvedPromiseDefinition extends DataFlow::CallNode {
115-
/**
116-
* Gets the value this promise is resolved with.
117-
*/
118-
abstract DataFlow::Node getValue();
119-
}
120-
121-
/**
122-
* A resolved promise created by the standard ECMAScript 2015 `Promise.resolve` function.
123-
*/
124-
class ResolvedES2015PromiseDefinition extends ResolvedPromiseDefinition {
125-
ResolvedES2015PromiseDefinition() {
126-
this = DataFlow::globalVarRef("Promise").getAMemberCall("resolve")
127-
}
128-
129-
override DataFlow::Node getValue() { result = getArgument(0) }
130-
}

javascript/ql/src/semmle/javascript/StandardLibrary.qll

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,27 @@ private class ES2015PromiseDefinition extends PromiseDefinition, DataFlow::NewNo
139139
override DataFlow::FunctionNode getExecutor() { result = getCallback(0) }
140140
}
141141

142+
/**
143+
* A promise that is resolved with the given value.
144+
*/
145+
abstract class ResolvedPromiseDefinition extends DataFlow::CallNode {
146+
/**
147+
* Gets the value this promise is resolved with.
148+
*/
149+
abstract DataFlow::Node getValue();
150+
}
151+
152+
/**
153+
* A resolved promise created by the standard ECMAScript 2015 `Promise.resolve` function.
154+
*/
155+
class ResolvedES2015PromiseDefinition extends ResolvedPromiseDefinition {
156+
ResolvedES2015PromiseDefinition() {
157+
this = DataFlow::globalVarRef("Promise").getAMemberCall("resolve")
158+
}
159+
160+
override DataFlow::Node getValue() { result = getArgument(0) }
161+
}
162+
142163
/**
143164
* A data flow edge from a promise reaction to the corresponding handler.
144165
*/
@@ -164,8 +185,7 @@ private predicate promiseTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
164185
pred = succ.(PromiseDefinition).getResolveParameter().getACall().getArgument(0)
165186
or
166187
// from `x` to `Promise.resolve(x)`
167-
succ = DataFlow::globalVarRef("Promise").getAMemberCall("resolve") and
168-
pred = succ.(DataFlow::CallNode).getArgument(0)
188+
pred = succ.(ResolvedPromiseDefinition).getValue()
169189
or
170190
exists(DataFlow::MethodCallNode thn, DataFlow::FunctionNode cb |
171191
thn.getMethodName() = "then" and cb = thn.getCallback(0)

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:30:14:30:20 | x.value |
1111
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:41:10:41:18 | id(taint) |
1212
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:51:14:51:14 | x |
13+
| promise.js:4:24:4:31 | source() | promise.js:4:8:4:32 | Promise ... urce()) |
14+
| promise.js:5:25:5:32 | source() | promise.js:5:8:5:33 | bluebir ... urce()) |
1315
| thisAssignments.js:4:17:4:24 | source() | thisAssignments.js:5:10:5:18 | obj.field |
1416
| thisAssignments.js:7:19:7:26 | source() | thisAssignments.js:8:10:8:20 | this.field2 |
1517
| tst.js:2:13:2:20 | source() | tst.js:4:10:4:10 | x |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
let bluebird = require('bluebird');
2+
3+
function test() {
4+
sink(Promise.resolve(source())); // NOT OK
5+
sink(bluebird.resolve(source())); // NOT OK
6+
}

0 commit comments

Comments
 (0)