Skip to content

Commit b14139f

Browse files
authored
Merge pull request github#6261 from max-schaefer/js/module-constructor
Approved by asgerf
2 parents 8321d5f + ce24215 commit b14139f

File tree

6 files changed

+72
-52
lines changed

6 files changed

+72
-52
lines changed

javascript/ql/src/semmle/javascript/ApiGraphs.qll

Lines changed: 4 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -617,11 +617,11 @@ module API {
617617
cached
618618
predicate use(TApiNode nd, DataFlow::Node ref) {
619619
exists(string m, Module mod | nd = MkModuleDef(m) and mod = importableModule(m) |
620-
ref.(ModuleVarNode).getModule() = mod
620+
ref = DataFlow::moduleVarNode(mod)
621621
)
622622
or
623623
exists(string m, Module mod | nd = MkModuleExport(m) and mod = importableModule(m) |
624-
ref.(ExportsVarNode).getModule() = mod
624+
ref = DataFlow::exportsVarNode(mod)
625625
or
626626
exists(DataFlow::Node base | use(MkModuleDef(m), base) |
627627
ref = trackUseNode(base).getAPropertyRead("exports")
@@ -742,12 +742,9 @@ module API {
742742
or
743743
// additional backwards step from `require('m')` to `exports` or `module.exports` in m
744744
exists(Import imp | imp.getImportedModuleNode() = trackDefNode(nd, t.continue()) |
745-
result.(ExportsVarNode).getModule() = imp.getImportedModule()
745+
result = DataFlow::exportsVarNode(imp.getImportedModule())
746746
or
747-
exists(ModuleVarNode mod |
748-
mod.getModule() = imp.getImportedModule() and
749-
result = mod.(DataFlow::SourceNode).getAPropertyRead("exports")
750-
)
747+
result = DataFlow::moduleVarNode(imp.getImportedModule()).getAPropertyRead("exports")
751748
)
752749
or
753750
t = defStep(nd, result)
@@ -981,46 +978,3 @@ private module Label {
981978
/** Gets the `promisedError` edge label connecting a promise to its rejected value. */
982979
string promisedError() { result = "promisedError" }
983980
}
984-
985-
private class NodeModuleSourcesNodes extends DataFlow::SourceNode::Range {
986-
Variable v;
987-
988-
NodeModuleSourcesNodes() {
989-
exists(NodeModule m |
990-
this = DataFlow::ssaDefinitionNode(SSA::implicitInit(v)) and
991-
v = [m.getModuleVariable(), m.getExportsVariable()]
992-
)
993-
}
994-
995-
Variable getVariable() { result = v }
996-
}
997-
998-
/**
999-
* A CommonJS/AMD `module` variable.
1000-
*/
1001-
private class ModuleVarNode extends DataFlow::Node {
1002-
Module m;
1003-
1004-
ModuleVarNode() {
1005-
this.(NodeModuleSourcesNodes).getVariable() = m.(NodeModule).getModuleVariable()
1006-
or
1007-
DataFlow::parameterNode(this, m.(AmdModule).getDefine().getModuleParameter())
1008-
}
1009-
1010-
Module getModule() { result = m }
1011-
}
1012-
1013-
/**
1014-
* A CommonJS/AMD `exports` variable.
1015-
*/
1016-
private class ExportsVarNode extends DataFlow::Node {
1017-
Module m;
1018-
1019-
ExportsVarNode() {
1020-
this.(NodeModuleSourcesNodes).getVariable() = m.(NodeModule).getExportsVariable()
1021-
or
1022-
DataFlow::parameterNode(this, m.(AmdModule).getDefine().getExportsParameter())
1023-
}
1024-
1025-
Module getModule() { result = m }
1026-
}

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

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,55 @@ module SourceNode {
347347
}
348348
}
349349

350+
private class NodeModuleSourcesNodes extends SourceNode::Range {
351+
Variable v;
352+
353+
NodeModuleSourcesNodes() {
354+
exists(NodeModule m |
355+
this = DataFlow::ssaDefinitionNode(SSA::implicitInit(v)) and
356+
v = [m.getModuleVariable(), m.getExportsVariable()]
357+
)
358+
}
359+
360+
Variable getVariable() { result = v }
361+
}
362+
363+
/**
364+
* A CommonJS/AMD `module` variable.
365+
*/
366+
private class ModuleVarNode extends DataFlow::Node {
367+
Module m;
368+
369+
ModuleVarNode() {
370+
this.(NodeModuleSourcesNodes).getVariable() = m.(NodeModule).getModuleVariable()
371+
or
372+
DataFlow::parameterNode(this, m.(AmdModule).getDefine().getModuleParameter())
373+
}
374+
375+
Module getModule() { result = m }
376+
}
377+
378+
/**
379+
* A CommonJS/AMD `exports` variable.
380+
*/
381+
private class ExportsVarNode extends DataFlow::Node {
382+
Module m;
383+
384+
ExportsVarNode() {
385+
this.(NodeModuleSourcesNodes).getVariable() = m.(NodeModule).getExportsVariable()
386+
or
387+
DataFlow::parameterNode(this, m.(AmdModule).getDefine().getExportsParameter())
388+
}
389+
390+
Module getModule() { result = m }
391+
}
392+
393+
/** Gets the CommonJS/AMD `module` variable for module `m`. */
394+
SourceNode moduleVarNode(Module m) { result.(ModuleVarNode).getModule() = m }
395+
396+
/** Gets the CommonJS/AMD `exports` variable for module `m`. */
397+
SourceNode exportsVarNode(Module m) { result.(ExportsVarNode).getModule() = m }
398+
350399
deprecated class DefaultSourceNode extends SourceNode {
351400
DefaultSourceNode() { this instanceof SourceNode::DefaultRange }
352401
}

javascript/ql/src/semmle/javascript/security/dataflow/CodeInjectionCustomizations.qll

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,19 @@ module CodeInjection {
169169
}
170170

171171
/**
172-
* The first argument to `Module.prototype._compile` from the Node.js built-in module `module`,
173-
* considered as a code-injection sink.
172+
* The first argument to `Module.prototype._compile`, considered as a code-injection sink.
174173
*/
175174
class ModuleCompileSink extends Sink {
176175
ModuleCompileSink() {
176+
// `require('module').prototype._compile`
177177
this =
178178
API::moduleImport("module").getInstance().getMember("_compile").getACall().getArgument(0)
179+
or
180+
// `module.constructor.prototype._compile`
181+
exists(DataFlow::SourceNode moduleConstructor |
182+
moduleConstructor = DataFlow::moduleVarNode(_).getAPropertyRead("constructor") and
183+
this = moduleConstructor.getAnInstantiation().getAMethodCall("_compile").getArgument(0)
184+
)
179185
}
180186
}
181187

javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ nodes
9797
| module.js:9:16:9:29 | req.query.code |
9898
| module.js:9:16:9:29 | req.query.code |
9999
| module.js:9:16:9:29 | req.query.code |
100+
| module.js:11:17:11:30 | req.query.code |
101+
| module.js:11:17:11:30 | req.query.code |
102+
| module.js:11:17:11:30 | req.query.code |
100103
| react-native.js:7:7:7:33 | tainted |
101104
| react-native.js:7:17:7:33 | req.param("code") |
102105
| react-native.js:7:17:7:33 | req.param("code") |
@@ -221,6 +224,7 @@ edges
221224
| express.js:19:37:19:70 | req.par ... odule") | express.js:19:37:19:70 | req.par ... odule") |
222225
| express.js:21:19:21:48 | req.par ... ntext") | express.js:21:19:21:48 | req.par ... ntext") |
223226
| module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code |
227+
| module.js:11:17:11:30 | req.query.code | module.js:11:17:11:30 | req.query.code |
224228
| react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted |
225229
| react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted |
226230
| react-native.js:7:7:7:33 | tainted | react-native.js:10:23:10:29 | tainted |
@@ -305,6 +309,7 @@ edges
305309
| express.js:19:37:19:70 | req.par ... odule") | express.js:19:37:19:70 | req.par ... odule") | express.js:19:37:19:70 | req.par ... odule") | $@ flows to here and is interpreted as code. | express.js:19:37:19:70 | req.par ... odule") | User-provided value |
306310
| express.js:21:19:21:48 | req.par ... ntext") | express.js:21:19:21:48 | req.par ... ntext") | express.js:21:19:21:48 | req.par ... ntext") | $@ flows to here and is interpreted as code. | express.js:21:19:21:48 | req.par ... ntext") | User-provided value |
307311
| module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code | $@ flows to here and is interpreted as code. | module.js:9:16:9:29 | req.query.code | User-provided value |
312+
| module.js:11:17:11:30 | req.query.code | module.js:11:17:11:30 | req.query.code | module.js:11:17:11:30 | req.query.code | $@ flows to here and is interpreted as code. | module.js:11:17:11:30 | req.query.code | User-provided value |
308313
| react-native.js:8:32:8:38 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:8:32:8:38 | tainted | $@ flows to here and is interpreted as code. | react-native.js:7:17:7:33 | req.param("code") | User-provided value |
309314
| react-native.js:10:23:10:29 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:10:23:10:29 | tainted | $@ flows to here and is interpreted as code. | react-native.js:7:17:7:33 | req.param("code") | User-provided value |
310315
| react.js:10:56:10:77 | documen ... on.hash | react.js:10:56:10:77 | documen ... on.hash | react.js:10:56:10:77 | documen ... on.hash | $@ flows to here and is interpreted as code. | react.js:10:56:10:77 | documen ... on.hash | User-provided value |

javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ nodes
101101
| module.js:9:16:9:29 | req.query.code |
102102
| module.js:9:16:9:29 | req.query.code |
103103
| module.js:9:16:9:29 | req.query.code |
104+
| module.js:11:17:11:30 | req.query.code |
105+
| module.js:11:17:11:30 | req.query.code |
106+
| module.js:11:17:11:30 | req.query.code |
104107
| react-native.js:7:7:7:33 | tainted |
105108
| react-native.js:7:17:7:33 | req.param("code") |
106109
| react-native.js:7:17:7:33 | req.param("code") |
@@ -229,6 +232,7 @@ edges
229232
| express.js:19:37:19:70 | req.par ... odule") | express.js:19:37:19:70 | req.par ... odule") |
230233
| express.js:21:19:21:48 | req.par ... ntext") | express.js:21:19:21:48 | req.par ... ntext") |
231234
| module.js:9:16:9:29 | req.query.code | module.js:9:16:9:29 | req.query.code |
235+
| module.js:11:17:11:30 | req.query.code | module.js:11:17:11:30 | req.query.code |
232236
| react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted |
233237
| react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted |
234238
| react-native.js:7:7:7:33 | tainted | react-native.js:10:23:10:29 | tainted |

javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/module.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ app.get('/some/path', function (req, res) {
77
let filename = req.query.filename;
88
var m = new Module(filename, module.parent);
99
m._compile(req.query.code, filename); // NOT OK
10+
var m2 = new module.constructor;
11+
m2._compile(req.query.code, filename); // NOT OK
1012
});

0 commit comments

Comments
 (0)