Skip to content

Commit 326522c

Browse files
authored
Merge pull request github#2846 from erik-krogh/CVE481
Approved by asgerf, esbena
2 parents db33c36 + dc6bfad commit 326522c

File tree

4 files changed

+402
-119
lines changed

4 files changed

+402
-119
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ module TaintedPath {
3636
guard instanceof StartsWithDirSanitizer or
3737
guard instanceof IsAbsoluteSanitizer or
3838
guard instanceof ContainsDotDotSanitizer or
39+
guard instanceof RelativePathStartsWithDotDotSanitizer or
3940
guard instanceof IsInsideCheckSanitizer
4041
}
4142

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

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,15 @@ module TaintedPath {
1212
*/
1313
abstract class Source extends DataFlow::Node {
1414
/** Gets a flow label denoting the type of value for which this is a source. */
15-
DataFlow::FlowLabel getAFlowLabel() {
16-
result instanceof Label::PosixPath
17-
}
15+
DataFlow::FlowLabel getAFlowLabel() { result instanceof Label::PosixPath }
1816
}
1917

2018
/**
2119
* A data flow sink for tainted-path vulnerabilities.
2220
*/
2321
abstract class Sink extends DataFlow::Node {
2422
/** Gets a flow label denoting the type of value for which this is a sink. */
25-
DataFlow::FlowLabel getAFlowLabel() {
26-
result instanceof Label::PosixPath
27-
}
23+
DataFlow::FlowLabel getAFlowLabel() { result instanceof Label::PosixPath }
2824
}
2925

3026
/**
@@ -364,6 +360,42 @@ module TaintedPath {
364360
}
365361
}
366362

363+
/**
364+
* A sanitizer that recognizes the following pattern:
365+
* ```
366+
* var relative = path.relative(webroot, pathname);
367+
* if(relative.startsWith(".." + path.sep) || relative == "..") {
368+
* // pathname is unsafe
369+
* } else {
370+
* // pathname is safe
371+
* }
372+
* ```
373+
*/
374+
class RelativePathStartsWithDotDotSanitizer extends DataFlow::BarrierGuardNode {
375+
StringOps::StartsWith startsWith;
376+
DataFlow::CallNode relativeCall;
377+
378+
RelativePathStartsWithDotDotSanitizer() {
379+
this = startsWith and
380+
relativeCall = NodeJSLib::Path::moduleMember("relative").getACall() and
381+
(
382+
startsWith.getBaseString().getALocalSource() = relativeCall
383+
or
384+
startsWith
385+
.getBaseString()
386+
.getALocalSource()
387+
.(NormalizingPathCall)
388+
.getInput()
389+
.getALocalSource() = relativeCall
390+
) and
391+
isDotDotSlashPrefix(startsWith.getSubstring())
392+
}
393+
394+
override predicate blocks(boolean outcome, Expr e) {
395+
e = relativeCall.getArgument(1).asExpr() and outcome = startsWith.getPolarity().booleanNot()
396+
}
397+
}
398+
367399
/**
368400
* A guard node for a variable in a negative condition, such as `x` in `if(!x)`.
369401
*/
@@ -443,9 +475,7 @@ module TaintedPath {
443475
* A path argument to a file system access, which disallows upward navigation.
444476
*/
445477
private class FsPathSinkWithoutUpwardNavigation extends FsPathSink {
446-
FsPathSinkWithoutUpwardNavigation() {
447-
fileSystemAccess.isUpwardNavigationRejected(this)
448-
}
478+
FsPathSinkWithoutUpwardNavigation() { fileSystemAccess.isUpwardNavigationRejected(this) }
449479

450480
override DataFlow::FlowLabel getAFlowLabel() {
451481
// The protection is ineffective if the ../ segments have already

0 commit comments

Comments
 (0)