From a608caa5ffb428994c4f508b426d25dae2dbda54 Mon Sep 17 00:00:00 2001
From: cjihrig <cjihrig@gmail.com>
Date: Wed, 8 May 2019 13:42:22 -0400
Subject: [PATCH] module: improve resolve paths validation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This commit adds input validation to require.resolve()'s
paths option. Prior to this change, passing in a non-array
value lead to a misleading 'module not found' error.

Refs: https://github.com/nodejs/node/issues/27583

PR-URL: https://github.com/nodejs/node/pull/27613
Reviewed-By: Michaƫl Zasso <targos@protonmail.com>
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
---
 lib/internal/modules/cjs/loader.js | 38 +++++++++++++++++-------------
 test/fixtures/require-resolve.js   |  8 +++++++
 2 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index d02c632bcee5a7..43cffd89cb819d 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -53,6 +53,7 @@ const { compileFunction } = internalBinding('contextify');
 
 const {
   ERR_INVALID_ARG_VALUE,
+  ERR_INVALID_OPT_VALUE,
   ERR_REQUIRE_ESM
 } = require('internal/errors').codes;
 const { validateString } = require('internal/validators');
@@ -573,28 +574,33 @@ Module._resolveFilename = function(request, parent, isMain, options) {
 
   var paths;
 
-  if (typeof options === 'object' && options !== null &&
-      Array.isArray(options.paths)) {
-    const isRelative = request.startsWith('./') || request.startsWith('../') ||
-        (isWindows && request.startsWith('.\\') || request.startsWith('..\\'));
+  if (typeof options === 'object' && options !== null) {
+    if (Array.isArray(options.paths)) {
+      const isRelative = request.startsWith('./') ||
+          request.startsWith('../') ||
+          (isWindows && request.startsWith('.\\') ||
+          request.startsWith('..\\'));
 
-    if (isRelative) {
-      paths = options.paths;
-    } else {
-      const fakeParent = new Module('', null);
+      if (isRelative) {
+        paths = options.paths;
+      } else {
+        const fakeParent = new Module('', null);
 
-      paths = [];
+        paths = [];
 
-      for (var i = 0; i < options.paths.length; i++) {
-        const path = options.paths[i];
-        fakeParent.paths = Module._nodeModulePaths(path);
-        const lookupPaths = Module._resolveLookupPaths(request, fakeParent);
+        for (var i = 0; i < options.paths.length; i++) {
+          const path = options.paths[i];
+          fakeParent.paths = Module._nodeModulePaths(path);
+          const lookupPaths = Module._resolveLookupPaths(request, fakeParent);
 
-        for (var j = 0; j < lookupPaths.length; j++) {
-          if (!paths.includes(lookupPaths[j]))
-            paths.push(lookupPaths[j]);
+          for (var j = 0; j < lookupPaths.length; j++) {
+            if (!paths.includes(lookupPaths[j]))
+              paths.push(lookupPaths[j]);
+          }
         }
       }
+    } else if (options.paths !== undefined) {
+      throw new ERR_INVALID_OPT_VALUE('options.paths', options.paths);
     }
   } else {
     paths = Module._resolveLookupPaths(request, parent);
diff --git a/test/fixtures/require-resolve.js b/test/fixtures/require-resolve.js
index a398e59cfa496f..3d8500e5097a70 100644
--- a/test/fixtures/require-resolve.js
+++ b/test/fixtures/require-resolve.js
@@ -84,3 +84,11 @@ assert.throws(() => {
     );
   }
 }
+
+// Test paths option validation
+common.expectsError(() => {
+  require.resolve('.\\three.js', { paths: 'foo' })
+}, {
+  code: 'ERR_INVALID_OPT_VALUE',
+  type: TypeError,
+});