Skip to content

Commit 23ed2bc

Browse files
authored
Merge pull request github#2782 from asger-semmle/js/export-as-ns
Approved by erik-krogh, max-schaefer
2 parents 03ae783 + e4844bf commit 23ed2bc

31 files changed

+222
-206
lines changed

change-notes/1.24/analysis-javascript.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
* Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
1111

12+
* Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
13+
1214
* The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
1315

1416
* Support for the following frameworks and libraries has been improved:

javascript/ql/src/definitions.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ predicate importLookup(ASTNode path, Module target, string kind) {
7878
or
7979
exists(ReExportDeclaration red |
8080
path = red.getImportedPath() and
81-
target = red.getImportedModule()
81+
target = red.getReExportedModule()
8282
)
8383
)
8484
}

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

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -273,12 +273,12 @@ class BulkReExportDeclaration extends ReExportDeclaration, @exportalldeclaration
273273
override ConstantString getImportedPath() { result = getChildExpr(0) }
274274

275275
override predicate exportsAs(LexicalName v, string name) {
276-
getImportedModule().exportsAs(v, name) and
276+
getReExportedES2015Module().exportsAs(v, name) and
277277
not isShadowedFromBulkExport(this, name)
278278
}
279279

280280
override DataFlow::Node getSourceNode(string name) {
281-
result = getImportedModule().getAnExport().getSourceNode(name)
281+
result = getReExportedES2015Module().getAnExport().getSourceNode(name)
282282
}
283283
}
284284

@@ -379,7 +379,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @exportnameddeclaration
379379
exists(ExportSpecifier spec | spec = getASpecifier() and name = spec.getExportedName() |
380380
v = spec.getLocal().(LexicalAccess).getALexicalName()
381381
or
382-
this.(ReExportDeclaration).getImportedModule().exportsAs(v, spec.getLocalName())
382+
this.(ReExportDeclaration).getReExportedES2015Module().exportsAs(v, spec.getLocalName())
383383
)
384384
}
385385

@@ -393,7 +393,7 @@ class ExportNamedDeclaration extends ExportDeclaration, @exportnameddeclaration
393393
not exists(getImportedPath()) and result = DataFlow::valueNode(spec.getLocal())
394394
or
395395
exists(ReExportDeclaration red | red = this |
396-
result = red.getImportedModule().getAnExport().getSourceNode(spec.getLocalName())
396+
result = red.getReExportedES2015Module().getAnExport().getSourceNode(spec.getLocalName())
397397
)
398398
)
399399
}
@@ -545,14 +545,18 @@ class ReExportDefaultSpecifier extends ExportDefaultSpecifier {
545545
}
546546

547547
/**
548-
* A namespace export specifier.
548+
* A namespace export specifier, that is `*` or `* as x` occuring in an export declaration.
549549
*
550-
* Example:
550+
* Examples:
551551
*
552552
* ```
553553
* export
554554
* * // namespace export specifier
555555
* from 'a';
556+
*
557+
* export
558+
* * as x // namespace export specifier
559+
* from 'a';
556560
* ```
557561
*/
558562
class ExportNamespaceSpecifier extends ExportSpecifier, @exportnamespacespecifier { }
@@ -564,6 +568,7 @@ class ExportNamespaceSpecifier extends ExportSpecifier, @exportnamespacespecifie
564568
*
565569
* ```
566570
* export * from 'a'; // bulk re-export declaration
571+
* export * as x from 'a'; // namespace re-export declaration
567572
* export { x } from 'a'; // named re-export declaration
568573
* export x from 'a'; // default re-export declaration
569574
* ```
@@ -572,8 +577,23 @@ abstract class ReExportDeclaration extends ExportDeclaration {
572577
/** Gets the path of the module from which this declaration re-exports. */
573578
abstract ConstantString getImportedPath();
574579

575-
/** Gets the module from which this declaration re-exports. */
580+
/**
581+
* DEPRECATED. Use `getReExportedES2015Module()` instead.
582+
*
583+
* Gets the module from which this declaration re-exports.
584+
*/
585+
deprecated
576586
ES2015Module getImportedModule() {
587+
result = getReExportedModule()
588+
}
589+
590+
/** Gets the module from which this declaration re-exports, if it is an ES2015 module. */
591+
ES2015Module getReExportedES2015Module() {
592+
result = getReExportedModule()
593+
}
594+
595+
/** Gets the module from which this declaration re-exports. */
596+
Module getReExportedModule() {
577597
result.getFile() = getEnclosingModule().resolve(getImportedPath().(PathExpr))
578598
or
579599
result = resolveFromTypeRoot()
@@ -641,4 +661,4 @@ class OriginalExportDeclaration extends ExportDeclaration {
641661
result = this.(ExportDefaultDeclaration).getSourceNode(name) or
642662
result = this.(ExportNamedDeclaration).getSourceNode(name)
643663
}
644-
}
664+
}

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ private predicate mayDynamicallyComputeExports(Module m) {
6565
or
6666
// `m` re-exports all exports of some other module that dynamically computes its exports
6767
exists(BulkReExportDeclaration rexp | rexp = m.(ES2015Module).getAnExport() |
68-
mayDynamicallyComputeExports(rexp.getImportedModule())
68+
mayDynamicallyComputeExports(rexp.getReExportedModule())
6969
)
7070
}
7171

@@ -79,7 +79,7 @@ private predicate relevantExport(ES2015Module m, string x) {
7979
)
8080
or
8181
exists(ReExportDeclaration rexp, string y |
82-
rexp.getImportedModule() = m and
82+
rexp.getReExportedModule() = m and
8383
reExportsAs(rexp, x, y)
8484
)
8585
}
@@ -110,9 +110,9 @@ private predicate incompleteExport(ES2015Module m, string y) {
110110
mayDependOnLookupPath(rexp.getImportedPath().getStringValue())
111111
or
112112
// unresolvable path
113-
not exists(rexp.getImportedModule())
113+
not exists(rexp.getReExportedModule())
114114
or
115-
exists(Module n | n = rexp.getImportedModule() |
115+
exists(Module n | n = rexp.getReExportedModule() |
116116
// re-export from CommonJS/AMD
117117
mayDynamicallyComputeExports(n)
118118
or
@@ -399,3 +399,16 @@ private class AnalyzedClosureGlobalAccessPath extends AnalyzedNode, AnalyzedProp
399399
)
400400
}
401401
}
402+
403+
/**
404+
* A namespace export declaration analyzed as a property write.
405+
*/
406+
private class AnalyzedExportNamespaceSpecifier extends AnalyzedPropertyWrite, DataFlow::ValueNode {
407+
override ExportNamespaceSpecifier astNode;
408+
409+
override predicate writesValue(AbstractValue baseVal, string propName, AbstractValue value) {
410+
baseVal = TAbstractExportsObject(getTopLevel()) and
411+
propName = astNode.getExportedName() and
412+
value = TAbstractExportsObject(astNode.getExportDeclaration().(ReExportDeclaration).getReExportedModule())
413+
}
414+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
semmle-extractor-options: --experimental
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * as ns from "./lib";
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { ns } from "./reExportLib";
2+
3+
/** calls:lib.f */
4+
ns.f();

javascript/ql/test/library-tests/Flow/abseval.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,10 @@
146146
| import.js:5:5:5:7 | myf | import.js:5:11:5:11 | f | n.js:1:1:1:15 | function f |
147147
| import.js:8:5:8:11 | someVar | import.js:8:15:8:23 | someStuff | file://:0:0:0:0 | indefinite value (call) |
148148
| import.js:11:5:11:6 | h1 | import.js:11:10:11:10 | h | file://:0:0:0:0 | indefinite value (import) |
149+
| import.js:11:5:11:6 | h1 | import.js:11:10:11:10 | h | h.js:1:1:3:0 | exports object of module h |
149150
| import.js:12:5:12:6 | hf | import.js:12:10:12:12 | h.f | file://:0:0:0:0 | indefinite value (heap) |
150151
| import.js:12:5:12:6 | hf | import.js:12:10:12:12 | h.f | file://:0:0:0:0 | indefinite value (import) |
152+
| import.js:12:5:12:6 | hf | import.js:12:10:12:12 | h.f | h.js:1:8:1:22 | function f |
151153
| imports.ts:2:5:2:6 | ax | imports.ts:2:10:2:11 | Ax | file://:0:0:0:0 | indefinite value (global) |
152154
| imports.ts:2:5:2:6 | ax | imports.ts:2:10:2:11 | Ax | file://:0:0:0:0 | indefinite value (heap) |
153155
| imports.ts:5:5:5:7 | fs_ | imports.ts:5:11:5:12 | fs | file://:0:0:0:0 | indefinite value (import) |

javascript/ql/test/library-tests/Modules/BulkReExportDeclarations.qll

Lines changed: 0 additions & 3 deletions
This file was deleted.

javascript/ql/test/library-tests/Modules/ExportDeclarations.qll

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)