Update qhelp for js/path-injection.

This commit is contained in:
Max Schaefer
2023-11-16 11:55:28 +00:00
parent 8b6a9180dc
commit d147faba4e
6 changed files with 254 additions and 31 deletions

View File

@@ -13,40 +13,47 @@ attacker being able to influence behavior by modifying unexpected files.
<recommendation>
<p>
Validate user input before using it to construct a file path, either using an off-the-shelf library
like the <code>sanitize-filename</code> npm package, or by performing custom validation.
Validate user input before using it to construct a file path.
</p>
<p>
Ideally, follow these rules:
The choice of validation depends on whether you want to allow the user to specify complex paths with multiple components that may span multiple folders, or only simple filenames without a path component.
</p>
<ul>
<li>Do not allow more than a single "." character.</li>
<li>Do not allow directory separators such as "/" or "\" (depending on the file system).</li>
<li>Do not rely on simply replacing problematic sequences such as "../". For example, after
applying this filter to ".../...//", the resulting string would still be "../".</li>
<li>Use a whitelist of known good patterns.</li>
</ul>
<p>
In the former case, a common strategy is to make sure that the constructed file path is contained within a safe root folder.
First, normalize the path using <code>path.resolve</code> or <code>fs.realpathSync</code> to remove any ".." segments.
Then check that the normalized path starts with the root folder.
Note that the normalization step is important, since otherwise even a path that starts with the root folder could be used to access files outside the root folder.
</p>
<p>
In the latter case, you can use a library like the <code>sanitize-filename</code> npm package to eliminate any special characters from the file path.
Note that it is <i>not</i> sufficient to only remove "../" sequences: for example, applying this filter to ".../...//" would still result in the string "../".
</p>
<p>
Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that the user input matches one of these patterns.
</p>
</recommendation>
<example>
<p>
In the first example, a file name is read from an HTTP request and then used to access a file.
However, a malicious user could enter a file name which is an absolute path, such as
<code>"/etc/passwd"</code>.
</p>
<p>
In the second example, it appears that the user is restricted to opening a file within the
<code>"user"</code> home directory. However, a malicious user could enter a file name containing
special characters. For example, the string <code>"../../etc/passwd"</code> will result in the code
reading the file located at <code>"/home/user/../../etc/passwd"</code>, which is the system's
password file. This file would then be sent back to the user, giving them access to all the
system's passwords.
In the first example, a file name is read from an HTTP request and then used to access a file within a root folder.
However, a malicious user could enter a file name containing "../" segments to navigate outside the root folder and access sensitive files.
</p>
<sample src="examples/TaintedPath.js" />
<p>
The second example shows how to fix this.
First, the file name is resolved relative to a root folder, which has the side effect of normalizing the path and removing any "../" segments.
Then, <code>fs.realpathSync</code> is used to resolve any symbolic links in the path.
Finally, we check that the normalized path starts with the root folder's path, which ensures that the file is contained within the root folder.
</p>
<sample src="examples/TaintedPathGood.js" />
</example>
<references>

View File

@@ -1,13 +1,12 @@
var fs = require('fs'),
http = require('http'),
url = require('url');
const fs = require('fs'),
http = require('http'),
url = require('url');
const ROOT = "/var/www/";
var server = http.createServer(function(req, res) {
let path = url.parse(req.url, true).query.path;
let filePath = url.parse(req.url, true).query.path;
// BAD: This could read any file on the file system
res.write(fs.readFileSync(path));
// BAD: This could still read any file on the file system
res.write(fs.readFileSync("/home/user/" + path));
});
res.write(fs.readFileSync(ROOT + filePath, 'utf8'));
});

View File

@@ -0,0 +1,19 @@
const fs = require('fs'),
http = require('http'),
path = require('path'),
url = require('url');
const ROOT = "/var/www/";
var server = http.createServer(function(req, res) {
let filePath = url.parse(req.url, true).query.path;
// GOOD: Verify that the file path is under the root directory
filePath = fs.realpathSync(path.resolve(ROOT, filePath));
if (!filePath.startsWith(ROOT)) {
res.statusCode = 403;
res.end();
return;
}
res.write(fs.readFileSync(filePath, 'utf8'));
});

View File

@@ -1535,6 +1535,76 @@ nodes
| TaintedPath.js:214:35:214:38 | path |
| TaintedPath.js:214:35:214:38 | path |
| TaintedPath.js:214:35:214:38 | path |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:28:8:34 | req.url |
| examples/TaintedPath.js:8:28:8:34 | req.url |
| examples/TaintedPath.js:8:28:8:34 | req.url |
| examples/TaintedPath.js:8:28:8:34 | req.url |
| examples/TaintedPath.js:8:28:8:34 | req.url |
| examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath |
| express.js:8:20:8:32 | req.query.bar |
| express.js:8:20:8:32 | req.query.bar |
| express.js:8:20:8:32 | req.query.bar |
@@ -6635,6 +6705,102 @@ edges
| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) |
| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) |
| TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:211:14:211:37 | url.par ... , true) |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:7:8:52 | filePath | examples/TaintedPath.js:11:36:11:43 | filePath |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:41 | url.par ... , true) | examples/TaintedPath.js:8:18:8:47 | url.par ... ).query |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:47 | url.par ... ).query | examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:18:8:52 | url.par ... ry.path | examples/TaintedPath.js:8:7:8:52 | filePath |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:8:18:8:41 | url.par ... , true) |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| examples/TaintedPath.js:11:36:11:43 | filePath | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath |
| express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
| handlebars.js:10:51:10:58 | filePath | handlebars.js:11:32:11:39 | filePath |
@@ -10345,6 +10511,7 @@ edges
| TaintedPath.js:212:31:212:34 | path | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:212:31:212:34 | path | This path depends on a $@. | TaintedPath.js:211:24:211:30 | req.url | user-provided value |
| TaintedPath.js:213:45:213:48 | path | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:213:45:213:48 | path | This path depends on a $@. | TaintedPath.js:211:24:211:30 | req.url | user-provided value |
| TaintedPath.js:214:35:214:38 | path | TaintedPath.js:211:24:211:30 | req.url | TaintedPath.js:214:35:214:38 | path | This path depends on a $@. | TaintedPath.js:211:24:211:30 | req.url | user-provided value |
| examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | examples/TaintedPath.js:8:28:8:34 | req.url | examples/TaintedPath.js:11:29:11:43 | ROOT + filePath | This path depends on a $@. | examples/TaintedPath.js:8:28:8:34 | req.url | user-provided value |
| express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | express.js:8:20:8:32 | req.query.bar | This path depends on a $@. | express.js:8:20:8:32 | req.query.bar | user-provided value |
| handlebars.js:11:32:11:39 | filePath | handlebars.js:29:46:29:60 | req.params.path | handlebars.js:11:32:11:39 | filePath | This path depends on a $@. | handlebars.js:29:46:29:60 | req.params.path | user-provided value |
| handlebars.js:15:25:15:32 | filePath | handlebars.js:43:15:43:29 | req.params.path | handlebars.js:15:25:15:32 | filePath | This path depends on a $@. | handlebars.js:43:15:43:29 | req.params.path | user-provided value |

View File

@@ -0,0 +1,12 @@
const fs = require('fs'),
http = require('http'),
url = require('url');
const ROOT = "/var/www/";
var server = http.createServer(function(req, res) {
let filePath = url.parse(req.url, true).query.path;
// BAD: This could read any file on the file system
res.write(fs.readFileSync(ROOT + filePath, 'utf8'));
});

View File

@@ -0,0 +1,19 @@
const fs = require('fs'),
http = require('http'),
path = require('path'),
url = require('url');
const ROOT = "/var/www/";
var server = http.createServer(function(req, res) {
let filePath = url.parse(req.url, true).query.path;
// GOOD: Verify that the file path is under the root directory
filePath = fs.realpathSync(path.resolve(ROOT, filePath));
if (!filePath.startsWith(ROOT)) {
res.statusCode = 403;
res.end();
return;
}
res.write(fs.readFileSync(filePath, 'utf8'));
});