Skip to content

Commit d316cb5

Browse files
committed
deprecate exports and replace uses with the new getAnExportedValue
1 parent c5b5a4f commit d316cb5

File tree

18 files changed

+157
-109
lines changed

18 files changed

+157
-109
lines changed

javascript/ql/src/NodeJS/MissingExports.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ predicate definedInModule(GlobalVariable v, NodeModule m) {
2020
)
2121
}
2222

23-
from NodeModule m, GlobalVariable f, InvokeExpr invk, ASTNode export, GlobalVarAccess acc
23+
from NodeModule m, GlobalVariable f, InvokeExpr invk, DataFlow::Node export, GlobalVarAccess acc
2424
where
25-
m.exports(f.getName(), export) and
25+
export = m.getAnExportedValue(f.getName()) and
2626
acc = f.getAnAccess() and
2727
invk.getCallee() = acc and
2828
invk.getTopLevel() = m and

javascript/ql/src/semmle/javascript/AMD.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,8 @@ class AmdModule extends Module {
295295
/** Gets the definition of this module. */
296296
AmdModuleDefinition getDefine() { amdModuleTopLevel(result, this) }
297297

298-
override predicate exports(string name, ASTNode export) {
299-
exists(DataFlow::PropWrite pwn | export = pwn.getAstNode() |
298+
override DataFlow::Node getAnExportedValue(string name) {
299+
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
300300
pwn.getBase().analyze().getAValue() = getDefine().getAModuleExportsValue() and
301301
name = pwn.getPropertyName()
302302
)

javascript/ql/src/semmle/javascript/Closure.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,9 @@ module Closure {
165165
result = getScope().getVariable("exports")
166166
}
167167

168-
override predicate exports(string name, ASTNode export) {
168+
override DataFlow::Node getAnExportedValue(string name) {
169169
exists(DataFlow::PropWrite write, Expr base |
170-
write.getAstNode() = export and
170+
result = write.getRhs() and
171171
write.writes(base.flow(), name, _) and
172172
(
173173
base = getExportsVariable().getAReference()

javascript/ql/src/semmle/javascript/ES2015Modules.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ class ES2015Module extends Module {
2727
/** Gets an export declaration in this module. */
2828
ExportDeclaration getAnExport() { result.getTopLevel() = this }
2929

30-
override predicate exports(string name, ASTNode export) {
31-
exists(ExportDeclaration ed | ed = getAnExport() and ed = export | ed.exportsAs(_, name))
30+
override DataFlow::Node getAnExportedValue(string name) {
31+
exists(ExportDeclaration ed | ed = getAnExport() and result = ed.getSourceNode(name))
3232
}
3333

3434
/** Holds if this module exports variable `v` under the name `name`. */
@@ -235,7 +235,7 @@ abstract class ExportDeclaration extends Stmt, @export_declaration {
235235
ES2015Module getEnclosingModule() { this = result.getAnExport() }
236236

237237
/** Holds if this export declaration exports variable `v` under the name `name`. */
238-
abstract predicate exportsAs(LexicalName v, string name);
238+
abstract predicate exportsAs(LexicalName v, string name); // TODO: Can I deprecate this?
239239

240240
/**
241241
* Gets the data flow node corresponding to the value this declaration exports

javascript/ql/src/semmle/javascript/Modules.qll

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ abstract class Module extends TopLevel {
2424
Module getAnImportedModule() { result = getAnImport().getImportedModule() }
2525

2626
/** Gets a symbol exported by this module. */
27-
string getAnExportedSymbol() { exports(result, _) }
27+
string getAnExportedSymbol() { exists(getAnExportedValue(result)) }
2828

2929
/**
30+
* DEPRECATED. Use `getAnExportedValue` instead.
31+
*
3032
* Holds if this module explicitly exports symbol `name` at the
3133
* program element `export`.
3234
*
@@ -36,9 +38,77 @@ abstract class Module extends TopLevel {
3638
* that are explicitly defined on the module object.
3739
*
3840
* Symbols defined in another module that are re-exported by
39-
* this module are not considered either.
41+
* this module are only sometimes considered.
42+
*/
43+
deprecated predicate exports(string name, ASTNode export) {
44+
this instanceof AmdModule and
45+
exists(DataFlow::PropWrite pwn | export = pwn.getAstNode() |
46+
pwn.getBase().analyze().getAValue() = this.(AmdModule).getDefine().getAModuleExportsValue() and
47+
name = pwn.getPropertyName()
48+
)
49+
or
50+
this instanceof Closure::ClosureModule and
51+
exists(DataFlow::PropWrite write, Expr base |
52+
write.getAstNode() = export and
53+
write.writes(base.flow(), name, _) and
54+
(
55+
base = this.(Closure::ClosureModule).getExportsVariable().getAReference()
56+
or
57+
base = this.(Closure::ClosureModule).getExportsVariable().getAnAssignedExpr()
58+
)
59+
)
60+
or
61+
this instanceof NodeModule and
62+
(
63+
// a property write whose base is `exports` or `module.exports`
64+
exists(DataFlow::PropWrite pwn | export = pwn.getAstNode() |
65+
pwn.getBase() = this.(NodeModule).getAModuleExportsNode() and
66+
name = pwn.getPropertyName()
67+
)
68+
or
69+
// a re-export using spread-operator. E.g. `const foo = require("./foo"); module.exports = {bar: bar, ...foo};`
70+
exists(ObjectExpr obj | obj = this.(NodeModule).getAModuleExportsNode().asExpr() |
71+
obj
72+
.getAProperty()
73+
.(SpreadProperty)
74+
.getInit()
75+
.(SpreadElement)
76+
.getOperand()
77+
.flow()
78+
.getALocalSource()
79+
.asExpr()
80+
.(Import)
81+
.getImportedModule()
82+
.exports(name, export)
83+
)
84+
or
85+
// an externs definition (where appropriate)
86+
exists(PropAccess pacc | export = pacc |
87+
pacc.getBase() = this.(NodeModule).getAModuleExportsNode().asExpr() and
88+
name = pacc.getPropertyName() and
89+
isExterns() and
90+
exists(pacc.getDocumentation())
91+
)
92+
)
93+
or
94+
this instanceof ES2015Module and
95+
exists(ExportDeclaration ed | ed = this.(ES2015Module).getAnExport() and ed = export |
96+
ed.exportsAs(_, name)
97+
)
98+
}
99+
100+
/**
101+
* Get a value that is explicitly exported from this module with under `name`.
102+
*
103+
* Note that in some module systems (notably CommonJS and AMD)
104+
* modules are arbitrary objects that export all their
105+
* properties. This predicate only considers properties
106+
* that are explicitly defined on the module object.
107+
*
108+
* Symbols defined in another module that are re-exported by
109+
* this module are only sometimes considered.
40110
*/
41-
abstract predicate exports(string name, ASTNode export);
111+
abstract DataFlow::Node getAnExportedValue(string name);
42112

43113
/**
44114
* Gets the root folder relative to which the given import path (which must

javascript/ql/src/semmle/javascript/NodeJS.qll

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class NodeModule extends Module {
4848
* So if using this predicate - consider expanding the list of relevant expressions.
4949
*/
5050
pragma[noinline]
51-
private DataFlow::Node getAModuleExportsNode() {
51+
DataFlow::Node getAModuleExportsNode() {
5252
(
5353
// A bit of manual magic
5454
result = any(DataFlow::PropWrite w | exists(w.getPropertyName())).getBase()
@@ -62,35 +62,43 @@ class NodeModule extends Module {
6262

6363
/** Gets a symbol exported by this module. */
6464
override string getAnExportedSymbol() {
65-
result = super.getAnExportedSymbol() or
65+
result = super.getAnExportedSymbol()
66+
or
6667
result = getAnImplicitlyExportedSymbol()
68+
or
69+
// getters and the like.
70+
exists(DataFlow::PropWrite pwn |
71+
pwn.getBase() = this.getAModuleExportsNode() and
72+
result = pwn.getPropertyName()
73+
)
6774
}
6875

69-
override predicate exports(string name, ASTNode export) {
76+
override DataFlow::Node getAnExportedValue(string name) {
7077
// a property write whose base is `exports` or `module.exports`
71-
exists(DataFlow::PropWrite pwn | export = pwn.getAstNode() |
78+
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
7279
pwn.getBase() = getAModuleExportsNode() and
7380
name = pwn.getPropertyName()
7481
)
7582
or
7683
// a re-export using spread-operator. E.g. `const foo = require("./foo"); module.exports = {bar: bar, ...foo};`
7784
exists(ObjectExpr obj | obj = getAModuleExportsNode().asExpr() |
78-
obj
79-
.getAProperty()
80-
.(SpreadProperty)
81-
.getInit()
82-
.(SpreadElement)
83-
.getOperand()
84-
.flow()
85-
.getALocalSource()
86-
.asExpr()
87-
.(Import)
88-
.getImportedModule()
89-
.exports(name, export)
85+
result =
86+
obj
87+
.getAProperty()
88+
.(SpreadProperty)
89+
.getInit()
90+
.(SpreadElement)
91+
.getOperand()
92+
.flow()
93+
.getALocalSource()
94+
.asExpr()
95+
.(Import)
96+
.getImportedModule()
97+
.getAnExportedValue(name)
9098
)
9199
or
92100
// an externs definition (where appropriate)
93-
exists(PropAccess pacc | export = pacc |
101+
exists(PropAccess pacc | result = DataFlow::valueNode(pacc) |
94102
pacc.getBase() = getAModuleExportsNode().asExpr() and
95103
name = pacc.getPropertyName() and
96104
isExterns() and

javascript/ql/src/semmle/javascript/PackageExports.qll

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,5 @@ DataFlow::Node getAValueExportedBy(PackageJSON packageJSON) {
6868
private DataFlow::Node getAnExportFromModule(Module mod) {
6969
result.analyze().getAValue() = mod.(NodeModule).getAModuleExportsValue()
7070
or
71-
result = getAnExportedValue(mod, _)
72-
}
73-
74-
/**
75-
* Gets a value exported from `mod` under `name`.
76-
*/
77-
DataFlow::Node getAnExportedValue(Module mod, string name) {
78-
exists(Property export | result.asExpr() = export.getInit() | mod.exports(name, export))
79-
or
80-
result =
81-
DataFlow::valueNode(any(ASTNode export | mod.exports(name, export)))
82-
.(DataFlow::PropWrite)
83-
.getRhs()
84-
or
85-
exists(ExportDeclaration export |
86-
result = export.getSourceNode(name) and
87-
mod = export.getEnclosingModule()
88-
)
71+
result = mod.getAnExportedValue(_)
8972
}

javascript/ql/src/semmle/javascript/dataflow/Portals.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,9 @@ private module NpmPackagePortal {
233233
apw.writes(m.(AnalyzedModule).getModuleObject(), "exports", exp)
234234
)
235235
or
236-
m.(ES2015Module).exports("default", exp.(DataFlow::ValueNode).getAstNode())
236+
exists(DataFlow::PropWrite export | exp = export |
237+
export.getRhs() = m.(ES2015Module).getAnExportedValue("default")
238+
)
237239
)
238240
}
239241
}

javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ module NodeJSLib {
647647
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
648648
exists(Import imp, string name |
649649
succ = DataFlow::valueNode(imp).(DataFlow::SourceNode).getAPropertyRead(name) and
650-
pred = Exports::getAnExportedValue(imp.getImportedModule(), name)
650+
pred = imp.getImportedModule().getAnExportedValue(name)
651651
)
652652
}
653653
}
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
| a.js:1:1:3:3 | <toplevel> | foo | a.js:2:14:2:20 | foo: 42 |
2-
| dir/b.js:1:1:3:3 | <toplevel> | bar | dir/b.js:2:5:2:11 | bar: 42 |
3-
| lib/a.js:1:1:3:3 | <toplevel> | foo | lib/a.js:2:14:2:20 | foo: 42 |
4-
| lib/foo.js:1:1:4:0 | <toplevel> | foo | lib/foo.js:2:5:2:11 | foo: 23 |
5-
| lib/nested/a.js:1:1:3:3 | <toplevel> | foo | lib/nested/a.js:2:14:2:20 | foo: 42 |
6-
| tst2.js:1:1:3:3 | <toplevel> | foo | tst2.js:2:5:2:15 | exports.foo |
7-
| tst3.js:1:1:3:3 | <toplevel> | foo | tst3.js:2:29:2:39 | exports.foo |
8-
| tst4.js:1:1:11:3 | <toplevel> | bar | tst4.js:9:9:9:18 | bar: b.bar |
9-
| tst4.js:1:1:11:3 | <toplevel> | foo | tst4.js:8:9:8:18 | foo: a.foo |
10-
| tst.js:1:1:6:3 | <toplevel> | bar | tst.js:4:9:4:18 | bar: b.bar |
11-
| tst.js:1:1:6:3 | <toplevel> | foo | tst.js:3:9:3:18 | foo: a.foo |
12-
| umd.js:1:1:14:4 | <toplevel> | bar | umd.js:11:9:11:18 | bar: a.foo |
13-
| umd.js:1:1:14:4 | <toplevel> | foo | umd.js:12:9:12:18 | foo: b.bar |
1+
| a.js:1:1:3:3 | <toplevel> | foo | a.js:2:19:2:20 | 42 |
2+
| dir/b.js:1:1:3:3 | <toplevel> | bar | dir/b.js:2:10:2:11 | 42 |
3+
| lib/a.js:1:1:3:3 | <toplevel> | foo | lib/a.js:2:19:2:20 | 42 |
4+
| lib/foo.js:1:1:4:0 | <toplevel> | foo | lib/foo.js:2:10:2:11 | 23 |
5+
| lib/nested/a.js:1:1:3:3 | <toplevel> | foo | lib/nested/a.js:2:19:2:20 | 42 |
6+
| tst2.js:1:1:3:3 | <toplevel> | foo | tst2.js:2:19:2:20 | 42 |
7+
| tst3.js:1:1:3:3 | <toplevel> | foo | tst3.js:2:43:2:44 | 42 |
8+
| tst4.js:1:1:11:3 | <toplevel> | bar | tst4.js:9:14:9:18 | b.bar |
9+
| tst4.js:1:1:11:3 | <toplevel> | foo | tst4.js:8:14:8:18 | a.foo |
10+
| tst.js:1:1:6:3 | <toplevel> | bar | tst.js:4:14:4:18 | b.bar |
11+
| tst.js:1:1:6:3 | <toplevel> | foo | tst.js:3:14:3:18 | a.foo |
12+
| umd.js:1:1:14:4 | <toplevel> | bar | umd.js:11:14:11:18 | a.foo |
13+
| umd.js:1:1:14:4 | <toplevel> | foo | umd.js:12:14:12:18 | b.bar |

0 commit comments

Comments
 (0)