Skip to content

Commit

Permalink
module: ensure relative requires work from deleted directories
Browse files Browse the repository at this point in the history
PR-URL: #42384
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
  • Loading branch information
bmeck authored and danielleadams committed Jan 3, 2023
1 parent a93c4f7 commit bf8d48a
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 10 deletions.
50 changes: 40 additions & 10 deletions lib/internal/modules/cjs/loader.js
Expand Up @@ -130,9 +130,10 @@ const { validateString } = require('internal/validators');
const pendingDeprecation = getOptionValue('--pending-deprecation');

const {
CHAR_FORWARD_SLASH,
CHAR_BACKWARD_SLASH,
CHAR_COLON
CHAR_COLON,
CHAR_DOT,
CHAR_FORWARD_SLASH,
} = require('internal/constants');

const {
Expand Down Expand Up @@ -538,7 +539,12 @@ function resolveExports(nmPath, request) {
}
}

const trailingSlashRegex = /(?:^|\/)\.?\.$/;
/**
* @param {string} request a relative or absolute file path
* @param {Array<string>} 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) {
Expand All @@ -553,18 +559,42 @@ Module._findPath = function(request, paths, isMain) {
return entry;

let exts;
let trailingSlash = request.length > 0 &&
StringPrototypeCharCodeAt(request, request.length - 1) ===
CHAR_FORWARD_SLASH;
if (!trailingSlash) {
trailingSlash = RegExpPrototypeExec(trailingSlashRegex, request) !== null;
const trailingSlash = request.length > 0 &&
(StringPrototypeCharCodeAt(request, request.length - 1) === CHAR_FORWARD_SLASH || (
StringPrototypeCharCodeAt(request, request.length - 1) === CHAR_DOT &&
(
request.length === 1 ||
StringPrototypeCharCodeAt(request, request.length - 2) === CHAR_FORWARD_SLASH ||
(StringPrototypeCharCodeAt(request, request.length - 2) === CHAR_DOT && (
request.length === 2 ||
StringPrototypeCharCodeAt(request, request.length - 3) === CHAR_FORWARD_SLASH
))
)
));

const isRelative = StringPrototypeCharCodeAt(request, 0) === CHAR_DOT &&
(
request.length === 1 ||
StringPrototypeCharCodeAt(request, 1) === CHAR_FORWARD_SLASH ||
(isWindows && StringPrototypeCharCodeAt(request, 1) === CHAR_BACKWARD_SLASH) ||
(StringPrototypeCharCodeAt(request, 1) === CHAR_DOT && ((
request.length === 2 ||
StringPrototypeCharCodeAt(request, 2) === CHAR_FORWARD_SLASH) ||
(isWindows && StringPrototypeCharCodeAt(request, 2) === CHAR_BACKWARD_SLASH)))
);
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);
Expand Down
30 changes: 30 additions & 0 deletions 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);

0 comments on commit bf8d48a

Please sign in to comment.