From af0ee7933a40f3688cad09c71197ad8ba6aca90d Mon Sep 17 00:00:00 2001 From: Bradley Farias Date: Thu, 17 Mar 2022 15:00:30 -0500 Subject: [PATCH] module: ensure relative requires work from deleted directories --- lib/internal/modules/cjs/loader.js | 21 +++++++++++++++-- test/parallel/test-require-enoent-dir.js | 30 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-require-enoent-dir.js diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js index 7e6cad0f6f3388..75dab92794795a 100644 --- a/lib/internal/modules/cjs/loader.js +++ b/lib/internal/modules/cjs/loader.js @@ -539,6 +539,14 @@ function resolveExports(nmPath, request) { } const trailingSlashRegex = /(?:^|\/)\.?\.$/; +const nixRelativeCheck = /^\.\.?(?:[/]|$)/; +const windowsRelativeCheck = /^\.\.?(?:[/\\]|$)/; +/** + * @param {string} request a relative or absolute file path + * @param {Array} paths file system directories to search as file paths + * @param {boolean} isMain if the request is the main app entry point + * @returns {string | false} + */ Module._findPath = function(request, paths, isMain) { const absoluteRequest = path.isAbsolute(request); if (absoluteRequest) { @@ -560,11 +568,20 @@ Module._findPath = function(request, paths, isMain) { trailingSlash = RegExpPrototypeExec(trailingSlashRegex, request) !== null; } + const isRelative = RegExpPrototypeExec(isWindows ? windowsRelativeCheck : nixRelativeCheck, request) !== null; + let insidePath = true; + if (isRelative) { + const normalizedRequest = path.normalize(request); + if (StringPrototypeStartsWith(normalizedRequest, '..')) { + insidePath = false; + } + } + // For each path for (let i = 0; i < paths.length; i++) { - // Don't search further if path doesn't exist + // Don't search further if path doesn't exist and request is inside the path const curPath = paths[i]; - if (curPath && _stat(curPath) < 1) continue; + if (insidePath && curPath && _stat(curPath) < 1) continue; if (!absoluteRequest) { const exportsResolved = resolveExports(curPath, request); diff --git a/test/parallel/test-require-enoent-dir.js b/test/parallel/test-require-enoent-dir.js new file mode 100644 index 00000000000000..519c3e4ea720a5 --- /dev/null +++ b/test/parallel/test-require-enoent-dir.js @@ -0,0 +1,30 @@ +'use strict'; + +const common = require('../common'); +const tmpdir = require('../common/tmpdir'); +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +tmpdir.refresh(); + +const fooPath = path.join(tmpdir.path, 'foo.cjs'); +fs.writeFileSync(fooPath, ''); + +const dirPath = path.join(tmpdir.path, 'delete_me'); +fs.mkdirSync(dirPath, { + recursive: true +}); + +const barPath = path.join(dirPath, 'bar.cjs'); +fs.writeFileSync(barPath, ` + module.exports = () => require('../foo.cjs').call() +`); + +const foo = require(fooPath); +const unique = Symbol('unique'); +foo.call = common.mustCall(() => unique); +const bar = require(barPath); + +fs.rmSync(dirPath, { recursive: true }); +assert.strict.equal(bar(), unique);