Merge pull request #4247 from erik-krogh/CVE760-reexport

Approved by asgerf
This commit is contained in:
CodeQL CI
2020-10-06 06:10:21 -07:00
committed by GitHub
39 changed files with 308 additions and 71 deletions

View File

@@ -20,9 +20,9 @@ predicate definedInModule(GlobalVariable v, NodeModule m) {
)
}
from NodeModule m, GlobalVariable f, InvokeExpr invk, ASTNode export, GlobalVarAccess acc
from NodeModule m, GlobalVariable f, InvokeExpr invk, DataFlow::Node export, GlobalVarAccess acc
where
m.exports(f.getName(), export) and
export = m.getAnExportedValue(f.getName()) and
acc = f.getAnAccess() and
invk.getCallee() = acc and
invk.getTopLevel() = m and

View File

@@ -295,8 +295,8 @@ class AmdModule extends Module {
/** Gets the definition of this module. */
AmdModuleDefinition getDefine() { amdModuleTopLevel(result, this) }
override predicate exports(string name, ASTNode export) {
exists(DataFlow::PropWrite pwn | export = pwn.getAstNode() |
override DataFlow::Node getAnExportedValue(string name) {
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
pwn.getBase().analyze().getAValue() = getDefine().getAModuleExportsValue() and
name = pwn.getPropertyName()
)

View File

@@ -165,9 +165,9 @@ module Closure {
result = getScope().getVariable("exports")
}
override predicate exports(string name, ASTNode export) {
override DataFlow::Node getAnExportedValue(string name) {
exists(DataFlow::PropWrite write, Expr base |
write.getAstNode() = export and
result = write.getRhs() and
write.writes(base.flow(), name, _) and
(
base = getExportsVariable().getAReference()

View File

@@ -27,8 +27,8 @@ class ES2015Module extends Module {
/** Gets an export declaration in this module. */
ExportDeclaration getAnExport() { result.getTopLevel() = this }
override predicate exports(string name, ASTNode export) {
exists(ExportDeclaration ed | ed = getAnExport() and ed = export | ed.exportsAs(_, name))
override DataFlow::Node getAnExportedValue(string name) {
exists(ExportDeclaration ed | ed = getAnExport() and result = ed.getSourceNode(name))
}
/** Holds if this module exports variable `v` under the name `name`. */
@@ -395,6 +395,13 @@ class ExportNamedDeclaration extends ExportDeclaration, @export_named_declaratio
result = DataFlow::valueNode(d.getSource())
)
or
exists(ObjectPattern obj | obj = getOperand().(DeclStmt).getADecl().getBindingPattern() |
exists(DataFlow::PropRead read | read = result |
read.getBase() = obj.flow() and
name = read.getPropertyName()
)
)
or
exists(ExportSpecifier spec | spec = getASpecifier() and name = spec.getExportedName() |
not exists(getImportedPath()) and result = DataFlow::valueNode(spec.getLocal())
or

View File

@@ -24,9 +24,11 @@ abstract class Module extends TopLevel {
Module getAnImportedModule() { result = getAnImport().getImportedModule() }
/** Gets a symbol exported by this module. */
string getAnExportedSymbol() { exports(result, _) }
string getAnExportedSymbol() { exists(getAnExportedValue(result)) }
/**
* DEPRECATED. Use `getAnExportedValue` instead.
*
* Holds if this module explicitly exports symbol `name` at the
* program element `export`.
*
@@ -36,9 +38,77 @@ abstract class Module extends TopLevel {
* that are explicitly defined on the module object.
*
* Symbols defined in another module that are re-exported by
* this module are not considered either.
* this module are only sometimes considered.
*/
abstract predicate exports(string name, ASTNode export);
deprecated predicate exports(string name, ASTNode export) {
this instanceof AmdModule and
exists(DataFlow::PropWrite pwn | export = pwn.getAstNode() |
pwn.getBase().analyze().getAValue() = this.(AmdModule).getDefine().getAModuleExportsValue() and
name = pwn.getPropertyName()
)
or
this instanceof Closure::ClosureModule and
exists(DataFlow::PropWrite write, Expr base |
write.getAstNode() = export and
write.writes(base.flow(), name, _) and
(
base = this.(Closure::ClosureModule).getExportsVariable().getAReference()
or
base = this.(Closure::ClosureModule).getExportsVariable().getAnAssignedExpr()
)
)
or
this instanceof NodeModule and
(
// a property write whose base is `exports` or `module.exports`
exists(DataFlow::PropWrite pwn | export = pwn.getAstNode() |
pwn.getBase() = this.(NodeModule).getAModuleExportsNode() and
name = pwn.getPropertyName()
)
or
// a re-export using spread-operator. E.g. `const foo = require("./foo"); module.exports = {bar: bar, ...foo};`
exists(ObjectExpr obj | obj = this.(NodeModule).getAModuleExportsNode().asExpr() |
obj
.getAProperty()
.(SpreadProperty)
.getInit()
.(SpreadElement)
.getOperand()
.flow()
.getALocalSource()
.asExpr()
.(Import)
.getImportedModule()
.exports(name, export)
)
or
// an externs definition (where appropriate)
exists(PropAccess pacc | export = pacc |
pacc.getBase() = this.(NodeModule).getAModuleExportsNode().asExpr() and
name = pacc.getPropertyName() and
isExterns() and
exists(pacc.getDocumentation())
)
)
or
this instanceof ES2015Module and
exists(ExportDeclaration ed | ed = this.(ES2015Module).getAnExport() and ed = export |
ed.exportsAs(_, name)
)
}
/**
* Get a value that is explicitly exported from this module with under `name`.
*
* Note that in some module systems (notably CommonJS and AMD)
* modules are arbitrary objects that export all their
* properties. This predicate only considers properties
* that are explicitly defined on the module object.
*
* Symbols defined in another module that are re-exported by
* this module are only sometimes considered.
*/
abstract DataFlow::Node getAnExportedValue(string name);
/**
* Gets the root folder relative to which the given import path (which must

View File

@@ -42,22 +42,64 @@ class NodeModule extends Module {
)
}
/** Gets a symbol exported by this module. */
override string getAnExportedSymbol() {
result = super.getAnExportedSymbol() or
result = getAnImplicitlyExportedSymbol()
/**
* Gets an expression that is an alias for `module.exports`.
* For performance this predicate only computes relevant expressions.
* So if using this predicate - consider expanding the list of relevant expressions.
*/
pragma[noinline]
DataFlow::Node getAModuleExportsNode() {
(
// A bit of manual magic
result = any(DataFlow::PropWrite w | exists(w.getPropertyName())).getBase()
or
result = DataFlow::valueNode(any(PropAccess p | exists(p.getPropertyName())).getBase())
or
result = DataFlow::valueNode(any(ObjectExpr obj))
) and
result.analyze().getAValue() = getAModuleExportsValue()
}
override predicate exports(string name, ASTNode export) {
/** Gets a symbol exported by this module. */
override string getAnExportedSymbol() {
result = super.getAnExportedSymbol()
or
result = getAnImplicitlyExportedSymbol()
or
// getters and the like.
exists(DataFlow::PropWrite pwn |
pwn.getBase() = this.getAModuleExportsNode() and
result = pwn.getPropertyName()
)
}
override DataFlow::Node getAnExportedValue(string name) {
// a property write whose base is `exports` or `module.exports`
exists(DataFlow::PropWrite pwn | export = pwn.getAstNode() |
pwn.getBase().analyze().getAValue() = getAModuleExportsValue() and
exists(DataFlow::PropWrite pwn | result = pwn.getRhs() |
pwn.getBase() = getAModuleExportsNode() and
name = pwn.getPropertyName()
)
or
// a re-export using spread-operator. E.g. `const foo = require("./foo"); module.exports = {bar: bar, ...foo};`
exists(ObjectExpr obj | obj = getAModuleExportsNode().asExpr() |
result =
obj
.getAProperty()
.(SpreadProperty)
.getInit()
.(SpreadElement)
.getOperand()
.flow()
.getALocalSource()
.asExpr()
.(Import)
.getImportedModule()
.getAnExportedValue(name)
)
or
// an externs definition (where appropriate)
exists(PropAccess pacc | export = pacc |
pacc.getBase().analyze().getAValue() = getAModuleExportsValue() and
exists(PropAccess pacc | result = DataFlow::valueNode(pacc) |
pacc.getBase() = getAModuleExportsNode().asExpr() and
name = pacc.getPropertyName() and
isExterns() and
exists(pacc.getDocumentation())

View File

@@ -40,7 +40,7 @@ DataFlow::Node getAValueExportedBy(PackageJSON packageJSON) {
exists(DataFlow::SourceNode callee |
callee = getAValueExportedBy(packageJSON).(DataFlow::NewNode).getCalleeNode().getALocalSource()
|
result = callee.getAPropertyRead("prototype").getAPropertyWrite()
result = callee.getAPropertyRead("prototype").getAPropertyWrite().getRhs()
or
result = callee.(DataFlow::ClassNode).getAnInstanceMethod()
)
@@ -68,10 +68,5 @@ DataFlow::Node getAValueExportedBy(PackageJSON packageJSON) {
private DataFlow::Node getAnExportFromModule(Module mod) {
result.analyze().getAValue() = mod.(NodeModule).getAModuleExportsValue()
or
exists(ASTNode export | result.getEnclosingExpr() = export | mod.exports(_, export))
or
exists(ExportDeclaration export |
result = export.getSourceNode(_) and
mod = export.getTopLevel()
)
result = mod.getAnExportedValue(_)
}

View File

@@ -233,7 +233,9 @@ private module NpmPackagePortal {
apw.writes(m.(AnalyzedModule).getModuleObject(), "exports", exp)
)
or
m.(ES2015Module).exports("default", exp.(DataFlow::ValueNode).getAstNode())
exists(DataFlow::PropWrite export | exp = export |
export.getRhs() = m.(ES2015Module).getAnExportedValue("default")
)
)
}
}

View File

@@ -4,6 +4,7 @@
private import javascript
private import semmle.javascript.dataflow.internal.StepSummary
private import semmle.javascript.dataflow.internal.PreCallGraphStep
cached
module CallGraph {
@@ -48,6 +49,10 @@ module CallGraph {
t.start() and
AccessPath::step(function, result)
or
t.start() and
imprecision = 0 and
PreCallGraphStep::step(any(DataFlow::Node n | function.flowsTo(n)), result)
or
imprecision = 0 and
exists(DataFlow::ClassNode cls |
exists(string name |

View File

@@ -643,6 +643,20 @@ module NodeJSLib {
}
}
private import semmle.javascript.PackageExports as Exports
/**
* A direct step from an named export to a property-read reading the exported value.
*/
private class ExportsStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(Import imp, string name |
succ = DataFlow::valueNode(imp).(DataFlow::SourceNode).getAPropertyRead(name) and
pred = imp.getImportedModule().getAnExportedValue(name)
)
}
}
/**
* A call to a method from module `child_process`.
*/

View File

@@ -1,15 +1,15 @@
| a.js:1:1:3:3 | <toplevel> | foo | a.js:2:14:2:20 | foo: 42 |
| dir/b.js:1:1:3:3 | <toplevel> | bar | dir/b.js:2:5:2:11 | bar: 42 |
| lib/a.js:1:1:3:3 | <toplevel> | foo | lib/a.js:2:14:2:20 | foo: 42 |
| lib/foo.js:1:1:4:0 | <toplevel> | foo | lib/foo.js:2:5:2:11 | foo: 23 |
| lib/nested/a.js:1:1:3:3 | <toplevel> | foo | lib/nested/a.js:2:14:2:20 | foo: 42 |
| tst2.js:1:1:3:3 | <toplevel> | foo | tst2.js:2:5:2:15 | exports.foo |
| tst3.js:1:1:3:3 | <toplevel> | foo | tst3.js:2:29:2:39 | exports.foo |
| tst4.js:1:1:11:3 | <toplevel> | bar | tst4.js:9:9:9:18 | bar: b.bar |
| tst4.js:1:1:11:3 | <toplevel> | foo | tst4.js:8:9:8:18 | foo: a.foo |
| tst5.js:1:1:6:3 | <toplevel> | bar | tst5.js:4:9:4:16 | bar: bar |
| tst5.js:1:1:6:3 | <toplevel> | foo | tst5.js:3:9:3:18 | foo: a.foo |
| tst.js:1:1:6:3 | <toplevel> | bar | tst.js:4:9:4:18 | bar: b.bar |
| tst.js:1:1:6:3 | <toplevel> | foo | tst.js:3:9:3:18 | foo: a.foo |
| umd.js:1:1:14:4 | <toplevel> | bar | umd.js:11:9:11:18 | bar: a.foo |
| umd.js:1:1:14:4 | <toplevel> | foo | umd.js:12:9:12:18 | foo: b.bar |
| a.js:1:1:3:3 | <toplevel> | foo | a.js:2:19:2:20 | 42 |
| dir/b.js:1:1:3:3 | <toplevel> | bar | dir/b.js:2:10:2:11 | 42 |
| lib/a.js:1:1:3:3 | <toplevel> | foo | lib/a.js:2:19:2:20 | 42 |
| lib/foo.js:1:1:4:0 | <toplevel> | foo | lib/foo.js:2:10:2:11 | 23 |
| lib/nested/a.js:1:1:3:3 | <toplevel> | foo | lib/nested/a.js:2:19:2:20 | 42 |
| tst2.js:1:1:3:3 | <toplevel> | foo | tst2.js:2:19:2:20 | 42 |
| tst3.js:1:1:3:3 | <toplevel> | foo | tst3.js:2:43:2:44 | 42 |
| tst4.js:1:1:11:3 | <toplevel> | bar | tst4.js:9:14:9:18 | b.bar |
| tst4.js:1:1:11:3 | <toplevel> | foo | tst4.js:8:14:8:18 | a.foo |
| tst5.js:1:1:6:3 | <toplevel> | bar | tst5.js:4:14:4:16 | bar |
| tst5.js:1:1:6:3 | <toplevel> | foo | tst5.js:3:14:3:18 | a.foo |
| tst.js:1:1:6:3 | <toplevel> | bar | tst.js:4:14:4:18 | b.bar |
| tst.js:1:1:6:3 | <toplevel> | foo | tst.js:3:14:3:18 | a.foo |
| umd.js:1:1:14:4 | <toplevel> | bar | umd.js:11:14:11:18 | a.foo |
| umd.js:1:1:14:4 | <toplevel> | foo | umd.js:12:14:12:18 | b.bar |

View File

@@ -1,5 +1,5 @@
import javascript
from Module m, string name, ASTNode export
where m.exports(name, export)
select m, name, export
from Module m, string name, DataFlow::Node exportValue
where exportValue = m.getAnExportedValue(name)
select m, name, exportValue

View File

@@ -0,0 +1,4 @@
module.exports = {
/** name:reexport.foo */
foo: function foo() {}
}

View File

@@ -0,0 +1,7 @@
const a = require("./a");
module.exports = {
/** name:reexport.bar */
bar: function bar() {},
...a
}

View File

@@ -0,0 +1,7 @@
const b = require("./b.js");
/** calls:reexport.foo */
b.foo();
/** calls:reexport.bar */
b.bar();

View File

@@ -69,19 +69,19 @@ test_Imports
| tst.html:5:3:5:20 | import f from 'a'; | tst.html:5:17:5:19 | 'a' | 1 |
| unresolved.js:1:1:1:18 | import f from 'a'; | unresolved.js:1:15:1:17 | 'a' | 1 |
test_Module_exports
| a.js:1:1:5:32 | <toplevel> | default | a.js:1:1:3:1 | export ... n 23;\\n} |
| a.js:1:1:5:32 | <toplevel> | x | a.js:5:1:5:32 | export ... } = o; |
| a.js:1:1:5:32 | <toplevel> | y | a.js:5:1:5:32 | export ... } = o; |
| b.js:1:1:8:0 | <toplevel> | f2 | b.js:7:1:7:21 | export ... './a'; |
| b.js:1:1:8:0 | <toplevel> | g | b.js:5:1:5:18 | export { f as g }; |
| e.js:1:1:4:0 | <toplevel> | g | e.js:3:1:3:35 | export ... './a'; |
| e.js:1:1:4:0 | <toplevel> | x | e.js:2:1:2:16 | export { x, y }; |
| e.js:1:1:4:0 | <toplevel> | y | e.js:2:1:2:16 | export { x, y }; |
| es2015_require.js:1:1:3:25 | <toplevel> | default | es2015_require.js:3:1:3:25 | export ... ss C {} |
| export-in-mjs.mjs:1:1:1:34 | <toplevel> | exported_from_mjs | export-in-mjs.mjs:1:1:1:34 | export ... s = 42; |
| f.ts:1:1:6:0 | <toplevel> | foo | f.ts:5:1:5:24 | export ... oo() {} |
| m/c.js:1:1:6:0 | <toplevel> | h | m/c.js:5:1:5:30 | export ... '../b'; |
| tst.html:4:23:8:0 | <toplevel> | y | tst.html:7:3:7:22 | export const y = 42; |
| a.js:1:1:5:32 | <toplevel> | default | a.js:1:16:3:1 | functio ... n 23;\\n} |
| a.js:1:1:5:32 | <toplevel> | x | a.js:5:18:5:20 | f() |
| a.js:1:1:5:32 | <toplevel> | y | a.js:5:25:5:25 | y |
| b.js:1:1:8:0 | <toplevel> | f2 | a.js:1:16:3:1 | functio ... n 23;\\n} |
| b.js:1:1:8:0 | <toplevel> | g | b.js:5:10:5:10 | f |
| e.js:1:1:4:0 | <toplevel> | g | a.js:1:16:3:1 | functio ... n 23;\\n} |
| e.js:1:1:4:0 | <toplevel> | x | e.js:2:10:2:10 | x |
| e.js:1:1:4:0 | <toplevel> | y | e.js:2:13:2:13 | y |
| es2015_require.js:1:1:3:25 | <toplevel> | default | es2015_require.js:3:16:3:25 | class C {} |
| export-in-mjs.mjs:1:1:1:34 | <toplevel> | exported_from_mjs | export-in-mjs.mjs:1:32:1:33 | 42 |
| f.ts:1:1:6:0 | <toplevel> | foo | f.ts:5:8:5:24 | function foo() {} |
| m/c.js:1:1:6:0 | <toplevel> | h | b.js:5:10:5:10 | f |
| tst.html:4:23:8:0 | <toplevel> | y | tst.html:7:20:7:21 | 42 |
test_NamedImportSpecifier
| d.js:1:10:1:21 | default as g |
| d.js:1:24:1:29 | x as y |
@@ -136,6 +136,7 @@ test_getLocalName
test_getSourceNode
| a.js:1:1:3:1 | export ... n 23;\\n} | default | a.js:1:16:3:1 | functio ... n 23;\\n} |
| a.js:5:1:5:32 | export ... } = o; | x | a.js:5:18:5:20 | f() |
| a.js:5:1:5:32 | export ... } = o; | y | a.js:5:25:5:25 | y |
| b.js:5:1:5:18 | export { f as g }; | g | b.js:5:10:5:10 | f |
| b.js:7:1:7:21 | export ... './a'; | f2 | a.js:1:16:3:1 | functio ... n 23;\\n} |
| e.js:2:1:2:16 | export { x, y }; | x | e.js:2:10:2:10 | x |

View File

@@ -26,8 +26,8 @@ query predicate test_Imports(ImportDeclaration id, PathExpr res0, int res1) {
res0 = id.getImportedPath() and res1 = count(id.getASpecifier())
}
query predicate test_Module_exports(Module m, string name, ASTNode export) {
m.exports(name, export)
query predicate test_Module_exports(Module m, string name, DataFlow::Node exportValue) {
exportValue = m.getAnExportedValue(name)
}
query predicate test_NamedImportSpecifier(NamedImportSpecifier nis) { any() }

View File

@@ -1,2 +1,4 @@
| b.js:7:22:7:27 | module |
| d.js:3:1:3:6 | module |
| reexport/a.js:1:1:1:6 | module |
| reexport/b.js:3:1:3:6 | module |

View File

@@ -1,4 +1,7 @@
| b.js:1:1:8:0 | <toplevel> | sneaky |
| d.js:1:1:7:15 | <toplevel> | baz |
| reexport/a.js:1:1:3:1 | <toplevel> | foo |
| reexport/b.js:1:1:6:1 | <toplevel> | bar |
| reexport/b.js:1:1:6:1 | <toplevel> | foo |
| sub/c.js:1:1:4:0 | <toplevel> | foo |
| sub/f.js:1:1:4:17 | <toplevel> | bar |

View File

@@ -15,4 +15,5 @@
| mjs-files/require-from-js.js:1:1:4:0 | <toplevel> | mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') |
| mjs-files/require-from-js.js:1:1:4:0 | <toplevel> | mjs-files/require-from-js.js:2:12:2:39 | require ... me.js') |
| mjs-files/require-from-js.js:1:1:4:0 | <toplevel> | mjs-files/require-from-js.js:3:12:3:40 | require ... e.mjs') |
| reexport/b.js:1:1:6:1 | <toplevel> | reexport/b.js:1:11:1:24 | require("./a") |
| sub/c.js:1:1:4:0 | <toplevel> | sub/c.js:1:1:1:15 | require('../a') |

View File

@@ -8,4 +8,5 @@
| index.js:1:1:3:0 | <toplevel> | b.js:1:1:8:0 | <toplevel> |
| mjs-files/require-from-js.js:1:1:4:0 | <toplevel> | mjs-files/depend-on-me.js:1:1:8:0 | <toplevel> |
| mjs-files/require-from-js.js:1:1:4:0 | <toplevel> | mjs-files/depend-on-me.mjs:1:1:7:1 | <toplevel> |
| reexport/b.js:1:1:6:1 | <toplevel> | reexport/a.js:1:1:3:1 | <toplevel> |
| sub/c.js:1:1:4:0 | <toplevel> | a.js:1:1:14:0 | <toplevel> |

View File

@@ -4,5 +4,7 @@
| e.js:1:1:6:0 | <toplevel> | e.js:0:0:0:0 | e.js | e.js | e |
| index.js:1:1:3:0 | <toplevel> | index.js:0:0:0:0 | index.js | index.js | index |
| mjs-files/require-from-js.js:1:1:4:0 | <toplevel> | mjs-files/require-from-js.js:0:0:0:0 | mjs-files/require-from-js.js | mjs-files/require-from-js.js | require-from-js |
| reexport/a.js:1:1:3:1 | <toplevel> | reexport/a.js:0:0:0:0 | reexport/a.js | reexport/a.js | a |
| reexport/b.js:1:1:6:1 | <toplevel> | reexport/b.js:0:0:0:0 | reexport/b.js | reexport/b.js | b |
| sub/c.js:1:1:4:0 | <toplevel> | sub/c.js:0:0:0:0 | sub/c.js | sub/c.js | c |
| sub/f.js:1:1:4:17 | <toplevel> | sub/f.js:0:0:0:0 | sub/f.js | sub/f.js | f |

View File

@@ -1,6 +1,9 @@
| b.js:1:1:8:0 | <toplevel> | sneaky | b.js:7:1:7:48 | (functi ... .sneaky |
| d.js:1:1:7:15 | <toplevel> | baz | d.js:4:2:4:8 | baz: 42 |
| mjs-files/depend-on-me.js:1:1:8:0 | <toplevel> | add | mjs-files/depend-on-me.js:5:1:7:1 | export ... + y;\\n} |
| mjs-files/depend-on-me.mjs:1:1:7:1 | <toplevel> | add | mjs-files/depend-on-me.mjs:5:1:7:1 | export ... + y;\\n} |
| sub/c.js:1:1:4:0 | <toplevel> | foo | sub/c.js:3:1:3:11 | exports.foo |
| sub/f.js:1:1:4:17 | <toplevel> | bar | sub/f.js:4:1:4:11 | exports.bar |
| b.js:1:1:8:0 | <toplevel> | sneaky | b.js:7:52:7:53 | 56 |
| d.js:1:1:7:15 | <toplevel> | baz | d.js:4:7:4:8 | 42 |
| mjs-files/depend-on-me.js:1:1:8:0 | <toplevel> | add | mjs-files/depend-on-me.js:5:8:7:1 | functio ... + y;\\n} |
| mjs-files/depend-on-me.mjs:1:1:7:1 | <toplevel> | add | mjs-files/depend-on-me.mjs:5:8:7:1 | functio ... + y;\\n} |
| reexport/a.js:1:1:3:1 | <toplevel> | foo | reexport/a.js:2:10:2:26 | function foo() {} |
| reexport/b.js:1:1:6:1 | <toplevel> | bar | reexport/b.js:4:10:4:26 | function bar() {} |
| reexport/b.js:1:1:6:1 | <toplevel> | foo | reexport/a.js:2:10:2:26 | function foo() {} |
| sub/c.js:1:1:4:0 | <toplevel> | foo | sub/c.js:3:15:3:16 | 23 |
| sub/f.js:1:1:4:17 | <toplevel> | bar | sub/f.js:4:15:4:16 | 42 |

View File

@@ -1,5 +1,5 @@
import javascript
from Module m, string name, ASTNode export
where m.exports(name, export)
select m, name, export
from Module m, string name, DataFlow::Node exportValue
where exportValue = m.getAnExportedValue(name)
select m, name, exportValue

View File

@@ -19,4 +19,5 @@
| mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') |
| mjs-files/require-from-js.js:2:12:2:39 | require ... me.js') |
| mjs-files/require-from-js.js:3:12:3:40 | require ... e.mjs') |
| reexport/b.js:1:11:1:24 | require("./a") |
| sub/c.js:1:1:1:15 | require('../a') |

View File

@@ -11,4 +11,5 @@
| mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') | ./depend-on-me | mjs-files/depend-on-me.mjs:1:1:7:1 | <toplevel> |
| mjs-files/require-from-js.js:2:12:2:39 | require ... me.js') | ./depend-on-me.js | mjs-files/depend-on-me.js:1:1:8:0 | <toplevel> |
| mjs-files/require-from-js.js:3:12:3:40 | require ... e.mjs') | ./depend-on-me.mjs | mjs-files/depend-on-me.mjs:1:1:7:1 | <toplevel> |
| reexport/b.js:1:11:1:24 | require("./a") | ./a | reexport/a.js:1:1:3:1 | <toplevel> |
| sub/c.js:1:1:1:15 | require('../a') | ../a | a.js:1:1:14:0 | <toplevel> |

View File

@@ -0,0 +1,3 @@
module.exports = {
foo: function foo() {}
}

View File

@@ -0,0 +1,6 @@
const a = require("./a");
module.exports = {
bar: function bar() {},
...a
}

View File

@@ -0,0 +1,5 @@
module.exports = {
// not imported from anywhere
foo: function foo() {},
bar: function bar() {}
}

View File

@@ -0,0 +1,3 @@
module.exports = {
reexported: function reexported() {}
}

View File

@@ -0,0 +1,6 @@
const a = require("./a");
module.exports = {
base: function base() {},
...a
}

View File

@@ -34,3 +34,15 @@ getAValueExportedBy
| lib1/sublib/package.json:1:1:3:1 | {\\n " ... b.js"\\n} | lib1/sublib/sublib.js:1:1:1:0 | this |
| lib1/sublib/package.json:1:1:3:1 | {\\n " ... b.js"\\n} | lib1/sublib/sublib.js:1:1:1:73 | module. ... rt() {} |
| lib1/sublib/package.json:1:1:3:1 | {\\n " ... b.js"\\n} | lib1/sublib/sublib.js:1:18:1:73 | functio ... rt() {} |
getAnExportedValue
| absent_main/index.js:1:1:2:0 | <toplevel> | foo | absent_main/index.js:1:22:1:34 | function() {} |
| esmodules/main.js:1:1:4:0 | <toplevel> | exported | esmodules/main.js:1:8:1:29 | functio ... ed() {} |
| lib1/baz.js:1:1:5:1 | <toplevel> | bar | lib1/baz.js:4:10:4:26 | function bar() {} |
| lib1/baz.js:1:1:5:1 | <toplevel> | foo | lib1/baz.js:3:10:3:26 | function foo() {} |
| lib1/foo.js:1:1:3:47 | <toplevel> | foo | lib1/foo.js:3:22:3:47 | functio ... ed() {} |
| lib1/main.js:1:1:17:30 | <toplevel> | Baz | lib1/main.js:17:22:17:30 | new Baz() |
| lib1/main.js:1:1:17:30 | <toplevel> | bar | lib1/main.js:5:22:9:1 | class B ... () {}\\n} |
| lib1/main.js:1:1:17:30 | <toplevel> | foo | lib1/main.js:3:22:3:40 | require("./foo.js") |
| lib1/reexport/a.js:1:1:3:1 | <toplevel> | reexported | lib1/reexport/a.js:2:17:2:40 | functio ... ed() {} |
| lib1/reexport/b.js:1:1:6:1 | <toplevel> | base | lib1/reexport/b.js:4:11:4:28 | function base() {} |
| lib1/reexport/b.js:1:1:6:1 | <toplevel> | reexported | lib1/reexport/a.js:2:17:2:40 | functio ... ed() {} |

View File

@@ -6,3 +6,7 @@ query PackageJSON getTopmostPackageJSON() { result = Exports::getTopmostPackageJ
query DataFlow::Node getAValueExportedBy(PackageJSON json) {
result = Exports::getAValueExportedBy(json)
}
query DataFlow::Node getAnExportedValue(Module mod, string name) {
result = mod.getAnExportedValue(name)
}

View File

@@ -70,3 +70,8 @@ dataValue
| tst.js:51:30:51:33 | data |
| tst.js:58:16:58:16 | x |
| tst.js:68:16:68:19 | data |
reexport
| reexport/a.js:2:10:2:26 | function foo() {} | reexport/a.js:2:10:2:26 | function foo() {} |
| reexport/a.js:2:10:2:26 | function foo() {} | reexport/test.js:3:13:3:17 | b.foo |
| reexport/b.js:4:10:4:26 | function bar() {} | reexport/b.js:4:10:4:26 | function bar() {} |
| reexport/b.js:4:10:4:26 | function bar() {} | reexport/test.js:4:13:4:17 | b.bar |

View File

@@ -46,3 +46,15 @@ DataFlow::SourceNode dataValue(DataFlow::TypeTracker t) {
}
query DataFlow::SourceNode dataValue() { result = dataValue(DataFlow::TypeTracker::end()) }
DataFlow::SourceNode reexport(DataFlow::TypeTracker t, DataFlow::FunctionNode func) {
t.start() and
func = result and
func.getFile().getParentContainer().getBaseName() = "reexport"
or
exists(DataFlow::TypeTracker t2 | result = reexport(t2, func).track(t2, t))
}
query DataFlow::SourceNode reexport(DataFlow::FunctionNode func) {
result = reexport(DataFlow::TypeTracker::end(), func)
}

View File

@@ -0,0 +1,3 @@
module.exports = {
foo: function foo() {}
}

View File

@@ -0,0 +1,6 @@
const a = require("./a");
module.exports = {
bar: function bar() {}, // name: bar
...a
}

View File

@@ -0,0 +1,4 @@
const b = require("./b");
const foo = b.foo;
const bar = b.bar;

View File

@@ -1 +1 @@
| MissingExports.js:7:5:7:19 | checkOne(xs[i]) | 'checkOne' references an undeclared global variable, not the variable exported $@. | MissingExports.js:1:1:1:16 | exports.checkOne | here |
| MissingExports.js:7:5:7:19 | checkOne(xs[i]) | 'checkOne' references an undeclared global variable, not the variable exported $@. | MissingExports.js:1:20:3:1 | functio ... or();\\n} | here |