CVE-2018-3732: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') (CWE-22)
value: resolve-path node module before 1.4.0 suffers from a Path Traversal vulnerability due to lack of validation of paths with certain special characters, which allows a malicious user to read content of any file with known path.
CWE description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
CVSS v3 base score: 7.5
Published: 2018-06-07T02:29Z
Repository: https://github.com/pillarjs/resolve-path
Language: JavaScript
Files changed: HISTORY.md, index.js, test/resolvePath.js
Commit date: 2018-02-13 12:21:56 -0500
Commit message: Fix resolving paths with certain special characters

Diff: diff --git a/HISTORY.md b/HISTORY.md
index b404882..f1b70fa 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,8 +1,9 @@
 unreleased
 ==========
 
+  * Fix resolving paths with certain special characters
   * deps: http-errors@~1.6.2
     - Make `message` property enumerable for `HttpError`s
     - deps: depd@1.1.1
     - deps: setprototypeof@1.0.3
 
diff --git a/index.js b/index.js
index f62b8a2..1e668c2 100644
--- a/index.js
+++ b/index.js
@@ -11,10 +11,11 @@
  * Module dependencies.
  * @private
  */
 
 var createError = require('http-errors')
+var join = require('path').join
 var normalize = require('path').normalize
 var pathIsAbsolute = require('path-is-absolute')
 var resolve = require('path').resolve
 var sep = require('path').sep
 
@@ -80,11 +81,8 @@ function resolvePath (rootPath, relativePath) {
   // path outside root
   if (UP_PATH_REGEXP.test(normalize('.' + sep + path))) {
     throw createError(403)
   }
 
-  // resolve & normalize the root path
-  root = normalize(resolve(root) + sep)
-
-  // resolve the path
-  return resolve(root, path)
+  // join the relative path
+  return normalize(join(resolve(root), path))
 }
diff --git a/test/resolvePath.js b/test/resolvePath.js
index 2b1377d..9267540 100644
--- a/test/resolvePath.js
+++ b/test/resolvePath.js
@@ -29,10 +29,15 @@ describe('resolvePath(relativePath)', function () {
       it('should resolve relative to cwd', function () {
         assert.equal(normalize(resolvePath('index.js')),
           normalize(join(process.cwd(), 'index.js')))
       })
 
+      it('should resolve relative with special characters', function () {
+        assert.equal(normalize(resolvePath('f:oo$bar')),
+          normalize(join(process.cwd(), './f:oo$bar')))
+      })
+
       it('should accept empty string', function () {
         assert.equal(normalize(resolvePath('')),
           normalize(process.cwd()))
       })
     })
@@ -87,10 +92,15 @@ describe('resolvePath(rootPath, relativePath)', function () {
       it('should resolve relative to rootPath', function () {
         assert.equal(normalize(resolvePath(__dirname, 'index.js')),
           normalize(resolve(__dirname, 'index.js')))
       })
 
+      it('should resolve relative to rootPath with special characters', function () {
+        assert.equal(normalize(resolvePath(__dirname, 'f:oo$bar')),
+        normalize(resolve(__dirname, './f:oo$bar')))
+      })
+
       it('should accept relative path', function () {
         assert.equal(normalize(resolvePath(join(__dirname, '..'), 'index.js')),
           normalize(resolve(join(__dirname, '..'), 'index.js')))
       })
     })

Vulnerable code:   // resolve & normalize the root path
  root = normalize(resolve(root) + sep)

  // resolve the path
  return resolve(root, path)
Fixed code:   * Fix resolving paths with certain special characters
var join = require('path').join
  // join the relative path
  return normalize(join(resolve(root), path))
      it('should resolve relative with special characters', function () {
        assert.equal(normalize(resolvePath('f:oo$bar')),
          normalize(join(process.cwd(), './f:oo$bar')))
      })

      it('should resolve relative to rootPath with special characters', function () {
        assert.equal(normalize(resolvePath(__dirname, 'f:oo$bar')),
        normalize(resolve(__dirname, './f:oo$bar')))
      })