Skip to content

Commit 12ed2ca

Browse files
author
Max Schaefer
authored
Merge pull request #958 from esben-semmle/js/improve-tainted-path
JS: add taint steps for fs.realpath and fs.realpathSync
2 parents 70bccf8 + 305a249 commit 12ed2ca

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,31 @@ module NodeJSLib {
267267
}
268268
}
269269

270+
/**
271+
* A call to a fs-module method that preserves taint.
272+
*/
273+
private class FsFlowTarget extends TaintTracking::AdditionalTaintStep {
274+
DataFlow::Node tainted;
275+
276+
FsFlowTarget() {
277+
exists(DataFlow::CallNode call, string methodName |
278+
call = DataFlow::moduleMember("fs", methodName).getACall()
279+
|
280+
methodName = "realpathSync" and
281+
tainted = call.getArgument(0) and
282+
this = call
283+
or
284+
methodName = "realpath" and
285+
tainted = call.getArgument(0) and
286+
this = call.getCallback(1).getParameter(1)
287+
)
288+
}
289+
290+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
291+
pred = tainted and succ = this
292+
}
293+
}
294+
270295
/**
271296
* A model of taint propagation through `new Buffer` and `Buffer.from`.
272297
*/

javascript/ql/test/query-tests/Security/CWE-022/TaintedPath.expected

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ nodes
6767
| TaintedPath.js:102:30:102:31 | ev |
6868
| TaintedPath.js:103:24:103:25 | ev |
6969
| TaintedPath.js:103:24:103:30 | ev.data |
70+
| TaintedPath.js:107:6:107:47 | path |
71+
| TaintedPath.js:107:13:107:36 | url.par ... , true) |
72+
| TaintedPath.js:107:13:107:42 | url.par ... ).query |
73+
| TaintedPath.js:107:13:107:47 | url.par ... ry.path |
74+
| TaintedPath.js:107:23:107:29 | req.url |
75+
| TaintedPath.js:109:28:109:48 | fs.real ... c(path) |
76+
| TaintedPath.js:109:44:109:47 | path |
77+
| TaintedPath.js:110:14:110:17 | path |
78+
| TaintedPath.js:111:32:111:39 | realpath |
79+
| TaintedPath.js:112:45:112:52 | realpath |
7080
| tainted-array-steps.js:9:7:9:48 | path |
7181
| tainted-array-steps.js:9:14:9:37 | url.par ... , true) |
7282
| tainted-array-steps.js:9:14:9:43 | url.par ... ).query |
@@ -150,6 +160,15 @@ edges
150160
| TaintedPath.js:102:30:102:31 | ev | TaintedPath.js:103:24:103:25 | ev |
151161
| TaintedPath.js:103:24:103:25 | ev | TaintedPath.js:103:24:103:30 | ev.data |
152162
| TaintedPath.js:103:24:103:30 | ev.data | TaintedPath.js:78:26:78:45 | Cookie.get("unsafe") |
163+
| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:109:44:109:47 | path |
164+
| TaintedPath.js:107:6:107:47 | path | TaintedPath.js:110:14:110:17 | path |
165+
| TaintedPath.js:107:13:107:36 | url.par ... , true) | TaintedPath.js:107:13:107:42 | url.par ... ).query |
166+
| TaintedPath.js:107:13:107:42 | url.par ... ).query | TaintedPath.js:107:13:107:47 | url.par ... ry.path |
167+
| TaintedPath.js:107:13:107:47 | url.par ... ry.path | TaintedPath.js:107:6:107:47 | path |
168+
| TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:107:13:107:36 | url.par ... , true) |
169+
| TaintedPath.js:109:44:109:47 | path | TaintedPath.js:109:28:109:48 | fs.real ... c(path) |
170+
| TaintedPath.js:110:14:110:17 | path | TaintedPath.js:111:32:111:39 | realpath |
171+
| TaintedPath.js:111:32:111:39 | realpath | TaintedPath.js:112:45:112:52 | realpath |
153172
| tainted-array-steps.js:9:7:9:48 | path | tainted-array-steps.js:11:40:11:43 | path |
154173
| tainted-array-steps.js:9:7:9:48 | path | tainted-array-steps.js:13:26:13:29 | path |
155174
| tainted-array-steps.js:9:14:9:37 | url.par ... , true) | tainted-array-steps.js:9:14:9:43 | url.par ... ).query |
@@ -190,6 +209,8 @@ edges
190209
| TaintedPath.js:85:31:85:74 | require ... ).query | TaintedPath.js:85:61:85:67 | req.url | TaintedPath.js:85:31:85:74 | require ... ).query | This path depends on $@. | TaintedPath.js:85:61:85:67 | req.url | a user-provided value |
191210
| TaintedPath.js:86:31:86:73 | require ... ).query | TaintedPath.js:86:60:86:66 | req.url | TaintedPath.js:86:31:86:73 | require ... ).query | This path depends on $@. | TaintedPath.js:86:60:86:66 | req.url | a user-provided value |
192211
| TaintedPath.js:94:48:94:60 | req.params[0] | TaintedPath.js:94:48:94:60 | req.params[0] | TaintedPath.js:94:48:94:60 | req.params[0] | This path depends on $@. | TaintedPath.js:94:48:94:60 | req.params[0] | a user-provided value |
212+
| TaintedPath.js:109:28:109:48 | fs.real ... c(path) | TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:109:28:109:48 | fs.real ... c(path) | This path depends on $@. | TaintedPath.js:107:23:107:29 | req.url | a user-provided value |
213+
| TaintedPath.js:112:45:112:52 | realpath | TaintedPath.js:107:23:107:29 | req.url | TaintedPath.js:112:45:112:52 | realpath | This path depends on $@. | TaintedPath.js:107:23:107:29 | req.url | a user-provided value |
193214
| tainted-array-steps.js:11:29:11:54 | ['publi ... in('/') | tainted-array-steps.js:9:24:9:30 | req.url | tainted-array-steps.js:11:29:11:54 | ['publi ... in('/') | This path depends on $@. | tainted-array-steps.js:9:24:9:30 | req.url | a user-provided value |
194215
| tainted-array-steps.js:15:29:15:43 | parts.join('/') | tainted-array-steps.js:9:24:9:30 | req.url | tainted-array-steps.js:15:29:15:43 | parts.join('/') | This path depends on $@. | tainted-array-steps.js:9:24:9:30 | req.url | a user-provided value |
195216
| tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") | This path depends on $@. | tainted-require.js:7:19:7:37 | req.param("module") | a user-provided value |

javascript/ql/test/query-tests/Security/CWE-022/TaintedPath.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,15 @@ var server = http.createServer(function(req, res) {
102102
addEventListener('message', (ev) => {
103103
Cookie.set("unsafe", ev.data);
104104
});
105+
106+
var server = http.createServer(function(req, res) {
107+
let path = url.parse(req.url, true).query.path;
108+
109+
res.write(fs.readFileSync(fs.realpathSync(path)));
110+
fs.realpath(path,
111+
function(err, realpath){
112+
res.write(fs.readFileSync(realpath));
113+
}
114+
);
115+
116+
});

0 commit comments

Comments
 (0)