Skip to content

Commit 0584a6a

Browse files
committed
recognize a nodejs re-exports in a loop
1 parent ac03fab commit 0584a6a

File tree

4 files changed

+36
-2
lines changed

4 files changed

+36
-2
lines changed

javascript/ql/lib/semmle/javascript/NodeJS.qll

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import javascript
44
private import NodeModuleResolutionImpl
5+
private import semmle.javascript.DynamicPropertyAccess as DynamicPropertyAccess
56

67
/**
78
* A Node.js module.
@@ -90,6 +91,18 @@ class NodeModule extends Module {
9091
.getAnExportedValue(name)
9192
)
9293
or
94+
// var imp = require('./imp');
95+
// for (var name in imp){
96+
// module.exports[name] = imp[name];
97+
// }
98+
exists(DynamicPropertyAccess::EnumeratedPropName read, Import imp, DataFlow::PropWrite write |
99+
read.getSourceObject().getALocalSource().asExpr() = imp and
100+
read.getASourceProp() = write.getRhs() and
101+
write.getBase() = this.getAModuleExportsNode() and
102+
write.getPropertyNameExpr().flow().getImmediatePredecessor*() = read and
103+
result = imp.getImportedModule().getAnExportedValue(name)
104+
)
105+
or
93106
// an externs definition (where appropriate)
94107
exists(PropAccess pacc | result = DataFlow::valueNode(pacc) |
95108
pacc.getBase() = this.getAModuleExportsNode().asExpr() and
@@ -158,7 +171,7 @@ class NodeModule extends Module {
158171
pragma[noinline]
159172
private DataFlow::Node getAModuleExportsCandidate() {
160173
// A bit of manual magic
161-
result = any(DataFlow::PropWrite w | exists(w.getPropertyName())).getBase()
174+
result = any(DataFlow::PropWrite w).getBase()
162175
or
163176
result = DataFlow::valueNode(any(PropAccess p | exists(p.getPropertyName())).getBase())
164177
or

javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction.expected

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
nodes
2+
| lib/isImported.js:5:49:5:52 | name |
3+
| lib/isImported.js:5:49:5:52 | name |
4+
| lib/isImported.js:6:22:6:25 | name |
5+
| lib/isImported.js:6:22:6:25 | name |
26
| lib/lib2.js:3:28:3:31 | name |
37
| lib/lib2.js:3:28:3:31 | name |
48
| lib/lib2.js:4:22:4:25 | name |
@@ -271,6 +275,10 @@ nodes
271275
| lib/subLib/index.js:8:22:8:25 | name |
272276
| lib/subLib/index.js:8:22:8:25 | name |
273277
edges
278+
| lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name |
279+
| lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name |
280+
| lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name |
281+
| lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name |
274282
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
275283
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
276284
| lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name |
@@ -587,6 +595,7 @@ edges
587595
| lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name |
588596
| lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name |
589597
#select
598+
| lib/isImported.js:6:10:6:25 | "rm -rf " + name | lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name | $@ based on $@ is later used in $@. | lib/isImported.js:6:10:6:25 | "rm -rf " + name | String concatenation | lib/isImported.js:5:49:5:52 | name | library input | lib/isImported.js:6:2:6:26 | cp.exec ... + name) | shell command |
590599
| lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/lib2.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib2.js:3:28:3:31 | name | library input | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command |
591600
| lib/lib2.js:8:10:8:25 | "rm -rf " + name | lib/lib2.js:7:32:7:35 | name | lib/lib2.js:8:22:8:25 | name | $@ based on $@ is later used in $@. | lib/lib2.js:8:10:8:25 | "rm -rf " + name | String concatenation | lib/lib2.js:7:32:7:35 | name | library input | lib/lib2.js:8:2:8:26 | cp.exec ... + name) | shell command |
592601
| lib/lib.js:4:10:4:25 | "rm -rf " + name | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:22:4:25 | name | $@ based on $@ is later used in $@. | lib/lib.js:4:10:4:25 | "rm -rf " + name | String concatenation | lib/lib.js:3:28:3:31 | name | library input | lib/lib.js:4:2:4:26 | cp.exec ... + name) | shell command |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// is imported from lib.js
2+
3+
const cp = require("child_process");
4+
5+
module.exports.thisMethodIsImported = function (name) {
6+
cp.exec("rm -rf " + name); // NOT OK
7+
}

javascript/ql/test/query-tests/Security/CWE-078/lib/lib.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,4 +499,9 @@ module.exports.myCommand = function (myCommand) {
499499
MyThing.cp.exec("rm -rf " + name); // NOT OK
500500
}
501501
});
502-
502+
503+
504+
var imp = require('./isImported');
505+
for (var name in imp){
506+
module.exports[name] = imp[name];
507+
}

0 commit comments

Comments
 (0)