@@ -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