Skip to content

Commit 2adaf0f

Browse files
authored
Merge pull request #17261 from asgerf/jss/dynamic-import-step
JS: Port step for dynamic imports
2 parents 4b8ae2a + 47c519f commit 2adaf0f

File tree

7 files changed

+68
-1
lines changed

7 files changed

+68
-1
lines changed

javascript/ql/lib/semmle/javascript/Promises.qll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,6 @@ private module DynamicImportSteps {
705705
*/
706706
class DynamicImportStep extends LegacyPreCallGraphStep {
707707
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
708-
// TODO: this step needs to be ported to dataflow2
709708
exists(DynamicImportExpr imprt |
710709
pred = imprt.getImportedModule().getAnExportedValue("default") and
711710
succ = imprt.flow() and

javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ private import Maps
99
private import Promises
1010
private import Sets
1111
private import Strings
12+
private import DynamicImportStep
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Contains flow steps to model flow from a module into a dynamic `import()` expression.
3+
*/
4+
5+
private import javascript
6+
private import semmle.javascript.dataflow.internal.DataFlowNode
7+
private import semmle.javascript.dataflow.internal.AdditionalFlowInternal
8+
private import semmle.javascript.dataflow.internal.DataFlowPrivate
9+
10+
/**
11+
* Flow steps for dynamic import expressions.
12+
*
13+
* The default export of the imported module must be boxed in a promise, so we pass
14+
* it through a synthetic node.
15+
*/
16+
class DynamicImportStep extends AdditionalFlowInternal {
17+
override predicate needsSynthesizedNode(AstNode node, string tag, DataFlowCallable container) {
18+
node instanceof DynamicImportExpr and
19+
tag = "imported-value" and
20+
container.asSourceCallable() = node.getContainer()
21+
}
22+
23+
override predicate jumpStep(DataFlow::Node pred, DataFlow::Node succ) {
24+
exists(DynamicImportExpr expr |
25+
pred = expr.getImportedModule().getAnExportedValue("default") and
26+
succ = getSynthesizedNode(expr, "imported-value")
27+
)
28+
}
29+
30+
override predicate storeStep(
31+
DataFlow::Node pred, DataFlow::ContentSet contents, DataFlow::Node succ
32+
) {
33+
exists(DynamicImportExpr expr |
34+
pred = getSynthesizedNode(expr, "imported-value") and
35+
contents = DataFlow::ContentSet::promiseValue() and
36+
succ = TValueNode(expr)
37+
)
38+
}
39+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ legacyDataFlowDifference
1414
| constructor-calls.js:4:18:4:25 | source() | constructor-calls.js:44:8:44:19 | f_safe.taint | only flow with NEW data flow library |
1515
| constructor-calls.js:20:15:20:22 | source() | constructor-calls.js:39:8:39:14 | e.param | only flow with NEW data flow library |
1616
| exceptions.js:53:14:53:21 | source() | exceptions.js:54:10:54:10 | e | only flow with NEW data flow library |
17+
| export-taint.js:3:22:3:29 | source() | import-taint.js:7:10:7:25 | mod.object.taint | only flow with OLD data flow library |
18+
| export-taint.js:3:22:3:29 | source() | import-taint.js:14:14:14:29 | mod.object.taint | only flow with OLD data flow library |
1719
| getters-and-setters.js:53:21:53:28 | source() | getters-and-setters.js:53:10:53:30 | getX(ne ... rce())) | only flow with NEW data flow library |
1820
| nested-props.js:14:15:14:22 | source() | nested-props.js:15:10:15:16 | obj.x.y | only flow with NEW data flow library |
1921
| nested-props.js:27:18:27:25 | source() | nested-props.js:28:10:28:14 | obj.x | only flow with NEW data flow library |
@@ -165,6 +167,8 @@ flow
165167
| exceptions.js:144:9:144:16 | source() | exceptions.js:132:8:132:27 | returnThrownSource() |
166168
| exceptions.js:150:13:150:20 | source() | exceptions.js:153:10:153:10 | e |
167169
| exceptions.js:158:13:158:20 | source() | exceptions.js:161:10:161:10 | e |
170+
| export-taint.js:2:12:2:19 | source() | import-taint.js:6:10:6:18 | mod.taint |
171+
| export-taint.js:2:12:2:19 | source() | import-taint.js:13:14:13:22 | mod.taint |
168172
| factory-function.js:21:13:21:20 | source() | factory-function.js:7:10:7:12 | obj |
169173
| factory-function.js:22:13:22:20 | source() | factory-function.js:7:10:7:12 | obj |
170174
| factory-function.js:26:7:26:14 | source() | factory-function.js:16:14:16:16 | obj |

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ legacyDataFlowDifference
1515
| constructor-calls.js:4:18:4:25 | source() | constructor-calls.js:44:8:44:19 | f_safe.taint | only flow with NEW data flow library |
1616
| constructor-calls.js:20:15:20:22 | source() | constructor-calls.js:39:8:39:14 | e.param | only flow with NEW data flow library |
1717
| exceptions.js:53:14:53:21 | source() | exceptions.js:54:10:54:10 | e | only flow with NEW data flow library |
18+
| export-taint.js:3:22:3:29 | source() | import-taint.js:7:10:7:25 | mod.object.taint | only flow with OLD data flow library |
19+
| export-taint.js:3:22:3:29 | source() | import-taint.js:14:14:14:29 | mod.object.taint | only flow with OLD data flow library |
1820
| getters-and-setters.js:53:21:53:28 | source() | getters-and-setters.js:53:10:53:30 | getX(ne ... rce())) | only flow with NEW data flow library |
1921
| nested-props.js:14:15:14:22 | source() | nested-props.js:15:10:15:16 | obj.x.y | only flow with NEW data flow library |
2022
| nested-props.js:27:18:27:25 | source() | nested-props.js:28:10:28:14 | obj.x | only flow with NEW data flow library |
@@ -115,6 +117,8 @@ flow
115117
| exceptions.js:144:9:144:16 | source() | exceptions.js:132:8:132:27 | returnThrownSource() |
116118
| exceptions.js:150:13:150:20 | source() | exceptions.js:153:10:153:10 | e |
117119
| exceptions.js:158:13:158:20 | source() | exceptions.js:161:10:161:10 | e |
120+
| export-taint.js:2:12:2:19 | source() | import-taint.js:6:10:6:18 | mod.taint |
121+
| export-taint.js:2:12:2:19 | source() | import-taint.js:13:14:13:22 | mod.taint |
118122
| factory-function.js:21:13:21:20 | source() | factory-function.js:7:10:7:12 | obj |
119123
| factory-function.js:22:13:22:20 | source() | factory-function.js:7:10:7:12 | obj |
120124
| factory-function.js:26:7:26:14 | source() | factory-function.js:16:14:16:16 | obj |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default {
2+
taint: source(),
3+
object: { taint: source() }
4+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import 'dummy';
2+
3+
async function test1() {
4+
let mod = await import("./export-taint");
5+
sink(mod); // OK
6+
sink(mod.taint); // NOT OK
7+
sink(mod.object.taint); // NOT OK [INCONSISTENCY] - blocked by access path limit
8+
}
9+
10+
function test2() {
11+
import("./export-taint").then(mod => {
12+
sink(mod); // OK
13+
sink(mod.taint); // NOT OK
14+
sink(mod.object.taint); // NOT OK [INCONSISTENCY] - blocked by access path limit
15+
});
16+
}

0 commit comments

Comments
 (0)