Skip to content

Commit 2885d48

Browse files
committed
changes based on review
1 parent a6d644b commit 2885d48

File tree

4 files changed

+60
-19
lines changed

4 files changed

+60
-19
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ module TaintedPath {
3636
guard instanceof StartsWithDirSanitizer or
3737
guard instanceof IsAbsoluteSanitizer or
3838
guard instanceof ContainsDotDotSanitizer or
39-
guard instanceof RelativePathContainsDotDotGuard
39+
guard instanceof RelativePathStartsWithDotDotSanitizer
4040
}
4141

4242
override predicate isAdditionalFlowStep(

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

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -353,20 +353,22 @@ module TaintedPath {
353353

354354
/**
355355
* A sanitizer that recognizes the following pattern:
356-
* var relative = path.relative(webroot, pathname);
357-
* if(relative.startsWith(".." + path.sep) || relative == "..") {
358-
* // pathname is unsafe
359-
* } else {
360-
* // pathname is safe
361-
* }
356+
* ```
357+
* var relative = path.relative(webroot, pathname);
358+
* if(relative.startsWith(".." + path.sep) || relative == "..") {
359+
* // pathname is unsafe
360+
* } else {
361+
* // pathname is safe
362+
* }
363+
* ```
362364
*/
363-
class RelativePathContainsDotDotGuard extends DataFlow::BarrierGuardNode {
365+
class RelativePathStartsWithDotDotSanitizer extends DataFlow::BarrierGuardNode {
364366
StringOps::StartsWith startsWith;
365367
DataFlow::CallNode relativeCall;
366368

367-
RelativePathContainsDotDotGuard() {
369+
RelativePathStartsWithDotDotSanitizer() {
368370
this = startsWith and
369-
relativeCall = DataFlow::moduleImport("path").getAMemberCall("relative") and
371+
relativeCall = NodeJSLib::Path::moduleMember("relative").getACall() and
370372
(
371373
startsWith.getBaseString().getALocalSource() = relativeCall
372374
or
@@ -377,18 +379,11 @@ module TaintedPath {
377379
.getInput()
378380
.getALocalSource() = relativeCall
379381
) and
380-
exists(DataFlow::Node subString, string prefix |
381-
subString = startsWith.getSubstring() and
382-
(prefix = ".." or prefix = "../")
383-
|
384-
subString.mayHaveStringValue(prefix)
385-
or
386-
subString.(StringOps::ConcatenationRoot).getFirstLeaf().mayHaveStringValue(prefix)
387-
)
382+
isDotDotSlashPrefix(startsWith.getSubstring())
388383
}
389384

390385
override predicate blocks(boolean outcome, Expr e) {
391-
e = relativeCall.getArgument(1).asExpr() and outcome = false
386+
e = relativeCall.getArgument(1).asExpr() and outcome = startsWith.getPolarity().booleanNot()
392387
}
393388
}
394389

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

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,23 @@ nodes
13331333
| normalizedPaths.js:286:21:286:27 | newpath |
13341334
| normalizedPaths.js:286:21:286:27 | newpath |
13351335
| normalizedPaths.js:286:21:286:27 | newpath |
1336+
| normalizedPaths.js:291:7:291:42 | newpath |
1337+
| normalizedPaths.js:291:7:291:42 | newpath |
1338+
| normalizedPaths.js:291:7:291:42 | newpath |
1339+
| normalizedPaths.js:291:7:291:42 | newpath |
1340+
| normalizedPaths.js:291:17:291:42 | pathMod ... e(path) |
1341+
| normalizedPaths.js:291:17:291:42 | pathMod ... e(path) |
1342+
| normalizedPaths.js:291:17:291:42 | pathMod ... e(path) |
1343+
| normalizedPaths.js:291:17:291:42 | pathMod ... e(path) |
1344+
| normalizedPaths.js:291:38:291:41 | path |
1345+
| normalizedPaths.js:291:38:291:41 | path |
1346+
| normalizedPaths.js:291:38:291:41 | path |
1347+
| normalizedPaths.js:291:38:291:41 | path |
1348+
| normalizedPaths.js:296:21:296:27 | newpath |
1349+
| normalizedPaths.js:296:21:296:27 | newpath |
1350+
| normalizedPaths.js:296:21:296:27 | newpath |
1351+
| normalizedPaths.js:296:21:296:27 | newpath |
1352+
| normalizedPaths.js:296:21:296:27 | newpath |
13361353
| tainted-require.js:7:19:7:37 | req.param("module") |
13371354
| tainted-require.js:7:19:7:37 | req.param("module") |
13381355
| tainted-require.js:7:19:7:37 | req.param("module") |
@@ -3732,6 +3749,10 @@ edges
37323749
| normalizedPaths.js:254:7:254:47 | path | normalizedPaths.js:283:38:283:41 | path |
37333750
| normalizedPaths.js:254:7:254:47 | path | normalizedPaths.js:283:38:283:41 | path |
37343751
| normalizedPaths.js:254:7:254:47 | path | normalizedPaths.js:283:38:283:41 | path |
3752+
| normalizedPaths.js:254:7:254:47 | path | normalizedPaths.js:291:38:291:41 | path |
3753+
| normalizedPaths.js:254:7:254:47 | path | normalizedPaths.js:291:38:291:41 | path |
3754+
| normalizedPaths.js:254:7:254:47 | path | normalizedPaths.js:291:38:291:41 | path |
3755+
| normalizedPaths.js:254:7:254:47 | path | normalizedPaths.js:291:38:291:41 | path |
37353756
| normalizedPaths.js:254:14:254:47 | pathMod ... y.path) | normalizedPaths.js:254:7:254:47 | path |
37363757
| normalizedPaths.js:254:14:254:47 | pathMod ... y.path) | normalizedPaths.js:254:7:254:47 | path |
37373758
| normalizedPaths.js:254:14:254:47 | pathMod ... y.path) | normalizedPaths.js:254:7:254:47 | path |
@@ -3792,6 +3813,22 @@ edges
37923813
| normalizedPaths.js:283:38:283:41 | path | normalizedPaths.js:283:17:283:42 | pathMod ... e(path) |
37933814
| normalizedPaths.js:283:38:283:41 | path | normalizedPaths.js:283:17:283:42 | pathMod ... e(path) |
37943815
| normalizedPaths.js:283:38:283:41 | path | normalizedPaths.js:283:17:283:42 | pathMod ... e(path) |
3816+
| normalizedPaths.js:291:7:291:42 | newpath | normalizedPaths.js:296:21:296:27 | newpath |
3817+
| normalizedPaths.js:291:7:291:42 | newpath | normalizedPaths.js:296:21:296:27 | newpath |
3818+
| normalizedPaths.js:291:7:291:42 | newpath | normalizedPaths.js:296:21:296:27 | newpath |
3819+
| normalizedPaths.js:291:7:291:42 | newpath | normalizedPaths.js:296:21:296:27 | newpath |
3820+
| normalizedPaths.js:291:7:291:42 | newpath | normalizedPaths.js:296:21:296:27 | newpath |
3821+
| normalizedPaths.js:291:7:291:42 | newpath | normalizedPaths.js:296:21:296:27 | newpath |
3822+
| normalizedPaths.js:291:7:291:42 | newpath | normalizedPaths.js:296:21:296:27 | newpath |
3823+
| normalizedPaths.js:291:7:291:42 | newpath | normalizedPaths.js:296:21:296:27 | newpath |
3824+
| normalizedPaths.js:291:17:291:42 | pathMod ... e(path) | normalizedPaths.js:291:7:291:42 | newpath |
3825+
| normalizedPaths.js:291:17:291:42 | pathMod ... e(path) | normalizedPaths.js:291:7:291:42 | newpath |
3826+
| normalizedPaths.js:291:17:291:42 | pathMod ... e(path) | normalizedPaths.js:291:7:291:42 | newpath |
3827+
| normalizedPaths.js:291:17:291:42 | pathMod ... e(path) | normalizedPaths.js:291:7:291:42 | newpath |
3828+
| normalizedPaths.js:291:38:291:41 | path | normalizedPaths.js:291:17:291:42 | pathMod ... e(path) |
3829+
| normalizedPaths.js:291:38:291:41 | path | normalizedPaths.js:291:17:291:42 | pathMod ... e(path) |
3830+
| normalizedPaths.js:291:38:291:41 | path | normalizedPaths.js:291:17:291:42 | pathMod ... e(path) |
3831+
| normalizedPaths.js:291:38:291:41 | path | normalizedPaths.js:291:17:291:42 | pathMod ... e(path) |
37953832
| tainted-require.js:7:19:7:37 | req.param("module") | tainted-require.js:7:19:7:37 | req.param("module") |
37963833
| tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") |
37973834
| tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") |
@@ -4578,6 +4615,7 @@ edges
45784615
| normalizedPaths.js:270:21:270:27 | newpath | normalizedPaths.js:254:33:254:46 | req.query.path | normalizedPaths.js:270:21:270:27 | newpath | This path depends on $@. | normalizedPaths.js:254:33:254:46 | req.query.path | a user-provided value |
45794616
| normalizedPaths.js:278:21:278:27 | newpath | normalizedPaths.js:254:33:254:46 | req.query.path | normalizedPaths.js:278:21:278:27 | newpath | This path depends on $@. | normalizedPaths.js:254:33:254:46 | req.query.path | a user-provided value |
45804617
| normalizedPaths.js:286:21:286:27 | newpath | normalizedPaths.js:254:33:254:46 | req.query.path | normalizedPaths.js:286:21:286:27 | newpath | This path depends on $@. | normalizedPaths.js:254:33:254:46 | req.query.path | a user-provided value |
4618+
| normalizedPaths.js:296:21:296:27 | newpath | normalizedPaths.js:254:33:254:46 | req.query.path | normalizedPaths.js:296:21:296:27 | newpath | This path depends on $@. | normalizedPaths.js:254:33:254:46 | req.query.path | a user-provided value |
45814619
| 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 |
45824620
| tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:8:16:8:33 | req.param("gimme") | a user-provided value |
45834621
| tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | This path depends on $@. | tainted-sendFile.js:10:16:10:33 | req.param("gimme") | a user-provided value |

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,4 +287,12 @@ app.get('/relative-startswith', (req, res) => {
287287
} else {
288288
fs.readFileSync(newpath); // OK!
289289
}
290+
291+
let newpath = pathModule.normalize(path);
292+
var relativePath = pathModule.relative(pathModule.normalize(workspaceDir), newpath);
293+
if (pathModule.normalize(relativePath).indexOf('../')) {
294+
fs.readFileSync(newpath); // OK!
295+
} else {
296+
fs.readFileSync(newpath); // NOT OK!
297+
}
290298
});

0 commit comments

Comments
 (0)