Skip to content

Commit ac34b92

Browse files
authored
Merge pull request #16241 from asgerf/js/re-export
JS: Improve support for `export * as ...` declarations
2 parents 18acad5 + da33c22 commit ac34b92

File tree

13 files changed

+67
-3
lines changed

13 files changed

+67
-3
lines changed

javascript/ql/lib/semmle/javascript/ApiGraphs.qll

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import javascript
99
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
10+
private import semmle.javascript.dataflow.internal.PreCallGraphStep
1011
private import internal.CachedStages
1112

1213
/**
@@ -782,6 +783,13 @@ module API {
782783
rhs = m.getAnExportedValue(prop)
783784
)
784785
or
786+
// In general, turn store steps into member steps for def-nodes
787+
exists(string prop |
788+
PreCallGraphStep::storeStep(rhs, pred, prop) and
789+
lbl = Label::member(prop) and
790+
not DataFlow::PseudoProperties::isPseudoProperty(prop)
791+
)
792+
or
785793
exists(DataFlow::FunctionNode fn |
786794
fn = pred and
787795
lbl = Label::return()
@@ -947,7 +955,6 @@ module API {
947955
(base instanceof TNonModuleDef or base instanceof TUse)
948956
)
949957
or
950-
// invocations
951958
exists(DataFlow::SourceNode src, DataFlow::SourceNode pred |
952959
use(base, src) and pred = trackUseNode(src)
953960
|
@@ -968,6 +975,13 @@ module API {
968975
or
969976
ref = cls.getAClassReference().getAnInstantiation()
970977
)
978+
or
979+
exists(string prop |
980+
PreCallGraphStep::loadStep(pred.getALocalUse(), ref, prop) and
981+
lbl = Label::member(prop) and
982+
// avoid generating member edges like "$arrayElement$"
983+
not DataFlow::PseudoProperties::isPseudoProperty(prop)
984+
)
971985
)
972986
or
973987
exists(DataFlow::Node def, DataFlow::FunctionNode fn |
@@ -1535,7 +1549,9 @@ module API {
15351549
prop = any(CanonicalName c).getName() or
15361550
prop = any(DataFlow::PropRef p).getPropertyName() or
15371551
exists(Impl::MkTypeUse(_, prop)) or
1538-
exists(any(Module m).getAnExportedValue(prop))
1552+
exists(any(Module m).getAnExportedValue(prop)) or
1553+
PreCallGraphStep::loadStep(_, _, prop) or
1554+
PreCallGraphStep::storeStep(_, _, prop)
15391555
} or
15401556
MkLabelUnknownMember() or
15411557
MkLabelParameter(int i) {

javascript/ql/lib/semmle/javascript/ES2015Modules.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,9 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
510510
or
511511
exists(ReExportDeclaration red | red = this |
512512
result = red.getReExportedES2015Module().getAnExport().getSourceNode(spec.getLocalName())
513+
or
514+
spec instanceof ExportNamespaceSpecifier and
515+
result = DataFlow::valueNode(spec)
513516
)
514517
)
515518
}
@@ -524,6 +527,19 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
524527
ExportSpecifier getASpecifier() { result = this.getSpecifier(_) }
525528
}
526529

530+
private import semmle.javascript.dataflow.internal.PreCallGraphStep
531+
532+
private class ExportNamespaceStep extends PreCallGraphStep {
533+
override predicate storeStep(DataFlow::Node pred, DataFlow::SourceNode succ, string prop) {
534+
exists(ExportNamedDeclaration exprt, ExportNamespaceSpecifier spec |
535+
spec = exprt.getASpecifier() and
536+
pred =
537+
exprt.(ReExportDeclaration).getReExportedES2015Module().getAnExport().getSourceNode(prop) and
538+
succ = DataFlow::valueNode(spec)
539+
)
540+
}
541+
}
542+
527543
/**
528544
* An export declaration with the `type` modifier.
529545
*/

javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,10 @@ module SharedFlowStep {
704704
* For use with load/store steps in `DataFlow::SharedFlowStep` and TypeTracking.
705705
*/
706706
module PseudoProperties {
707+
/** Holds if `s` is a pseudo-property. */
708+
bindingset[s]
709+
predicate isPseudoProperty(string s) { s.matches("$%$") }
710+
707711
bindingset[s]
708712
private string pseudoProperty(string s) { result = "$" + s + "$" }
709713

javascript/ql/lib/semmle/javascript/dataflow/Sources.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ module SourceNode {
322322
astNode instanceof FunctionBindExpr or
323323
astNode instanceof DynamicImportExpr or
324324
astNode instanceof ImportSpecifier or
325+
astNode instanceof ExportNamespaceSpecifier or
325326
astNode instanceof ImportMetaExpr or
326327
astNode instanceof TaggedTemplateExpr or
327328
astNode instanceof Templating::PipeRefExpr or

javascript/ql/test/ApiGraphs/custom-use-steps/VerifyAssertions.expected

Whitespace-only changes.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import ApiGraphs.VerifyAssertions
2+
private import semmle.javascript.dataflow.internal.PreCallGraphStep
3+
4+
class CustomUseStep extends PreCallGraphStep {
5+
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
6+
exists(DataFlow::CallNode call |
7+
call.getCalleeName() = "customLoad" and
8+
pred = call.getArgument(0) and
9+
succ = call and
10+
prop = call.getArgument(1).getStringValue()
11+
)
12+
}
13+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
const foo = require("foo");
2+
3+
foo.bar; // use=moduleImport("foo").getMember("exports").getMember("bar")
4+
customLoad(foo, "baz") // use=moduleImport("foo").getMember("exports").getMember("baz")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"name": "custom-use-steps"
3+
}

javascript/ql/test/ApiGraphs/reexport/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ module.exports = {
44
impl,
55
util: require("./lib/utils"),
66
other: require("./lib/stuff"),
7-
util2: require("./lib/utils2")
7+
util2: require("./lib/utils2"),
8+
esmodule: require("./lib/esmodule-reexport"),
89
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./esmodule-reexported1";
2+
export * as lib2 from "./esmodule-reexported2";
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export function one() {} /* def=moduleImport("reexport").getMember("exports").getMember("esmodule").getMember("one") */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export function two() {} /* def=moduleImport("reexport").getMember("exports").getMember("esmodule").getMember("lib2").getMember("two") */

javascript/ql/test/library-tests/Modules/tests.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ test_Module_exports
8181
| export-in-mjs.mjs:1:1:1:34 | <toplevel> | exported_from_mjs | export-in-mjs.mjs:1:32:1:33 | 42 |
8282
| f.ts:1:1:6:0 | <toplevel> | foo | f.ts:5:8:5:24 | function foo() {} |
8383
| m/c.js:1:1:6:0 | <toplevel> | h | b.js:5:10:5:10 | f |
84+
| reExportNamespace.js:1:1:2:0 | <toplevel> | ns | reExportNamespace.js:1:8:1:14 | * as ns |
8485
| tst.html:4:23:8:0 | <toplevel> | y | tst.html:7:20:7:21 | 42 |
8586
test_NamedImportSpecifier
8687
| d.js:1:10:1:21 | default as g |
@@ -149,4 +150,5 @@ test_getSourceNode
149150
| export-in-mjs.mjs:1:1:1:34 | export ... s = 42; | exported_from_mjs | export-in-mjs.mjs:1:32:1:33 | 42 |
150151
| f.ts:5:1:5:24 | export ... oo() {} | foo | f.ts:5:8:5:24 | function foo() {} |
151152
| m/c.js:5:1:5:30 | export ... '../b'; | h | b.js:5:10:5:10 | f |
153+
| reExportNamespace.js:1:1:1:26 | export ... "./a"; | ns | reExportNamespace.js:1:8:1:14 | * as ns |
152154
| tst.html:7:3:7:22 | export const y = 42; | y | tst.html:7:20:7:21 | 42 |

0 commit comments

Comments
 (0)