Skip to content

Commit ddefc5d

Browse files
bmeckRafaelGSS
authored andcommitted
module: ensure relative requires work from deleted directories
PR-URL: #42384 Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 95377a3 commit ddefc5d

File tree

2 files changed

+70
-10
lines changed

2 files changed

+70
-10
lines changed

lib/internal/modules/cjs/loader.js

+40-10
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,10 @@ const { validateString } = require('internal/validators');
130130
const pendingDeprecation = getOptionValue('--pending-deprecation');
131131

132132
const {
133-
CHAR_FORWARD_SLASH,
134133
CHAR_BACKWARD_SLASH,
135-
CHAR_COLON
134+
CHAR_COLON,
135+
CHAR_DOT,
136+
CHAR_FORWARD_SLASH,
136137
} = require('internal/constants');
137138

138139
const {
@@ -538,7 +539,12 @@ function resolveExports(nmPath, request) {
538539
}
539540
}
540541

541-
const trailingSlashRegex = /(?:^|\/)\.?\.$/;
542+
/**
543+
* @param {string} request a relative or absolute file path
544+
* @param {Array<string>} paths file system directories to search as file paths
545+
* @param {boolean} isMain if the request is the main app entry point
546+
* @returns {string | false}
547+
*/
542548
Module._findPath = function(request, paths, isMain) {
543549
const absoluteRequest = path.isAbsolute(request);
544550
if (absoluteRequest) {
@@ -553,18 +559,42 @@ Module._findPath = function(request, paths, isMain) {
553559
return entry;
554560

555561
let exts;
556-
let trailingSlash = request.length > 0 &&
557-
StringPrototypeCharCodeAt(request, request.length - 1) ===
558-
CHAR_FORWARD_SLASH;
559-
if (!trailingSlash) {
560-
trailingSlash = RegExpPrototypeExec(trailingSlashRegex, request) !== null;
562+
const trailingSlash = request.length > 0 &&
563+
(StringPrototypeCharCodeAt(request, request.length - 1) === CHAR_FORWARD_SLASH || (
564+
StringPrototypeCharCodeAt(request, request.length - 1) === CHAR_DOT &&
565+
(
566+
request.length === 1 ||
567+
StringPrototypeCharCodeAt(request, request.length - 2) === CHAR_FORWARD_SLASH ||
568+
(StringPrototypeCharCodeAt(request, request.length - 2) === CHAR_DOT && (
569+
request.length === 2 ||
570+
StringPrototypeCharCodeAt(request, request.length - 3) === CHAR_FORWARD_SLASH
571+
))
572+
)
573+
));
574+
575+
const isRelative = StringPrototypeCharCodeAt(request, 0) === CHAR_DOT &&
576+
(
577+
request.length === 1 ||
578+
StringPrototypeCharCodeAt(request, 1) === CHAR_FORWARD_SLASH ||
579+
(isWindows && StringPrototypeCharCodeAt(request, 1) === CHAR_BACKWARD_SLASH) ||
580+
(StringPrototypeCharCodeAt(request, 1) === CHAR_DOT && ((
581+
request.length === 2 ||
582+
StringPrototypeCharCodeAt(request, 2) === CHAR_FORWARD_SLASH) ||
583+
(isWindows && StringPrototypeCharCodeAt(request, 2) === CHAR_BACKWARD_SLASH)))
584+
);
585+
let insidePath = true;
586+
if (isRelative) {
587+
const normalizedRequest = path.normalize(request);
588+
if (StringPrototypeStartsWith(normalizedRequest, '..')) {
589+
insidePath = false;
590+
}
561591
}
562592

563593
// For each path
564594
for (let i = 0; i < paths.length; i++) {
565-
// Don't search further if path doesn't exist
595+
// Don't search further if path doesn't exist and request is inside the path
566596
const curPath = paths[i];
567-
if (curPath && _stat(curPath) < 1) continue;
597+
if (insidePath && curPath && _stat(curPath) < 1) continue;
568598

569599
if (!absoluteRequest) {
570600
const exportsResolved = resolveExports(curPath, request);
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const tmpdir = require('../common/tmpdir');
5+
const assert = require('assert');
6+
const fs = require('fs');
7+
const path = require('path');
8+
9+
tmpdir.refresh();
10+
11+
const fooPath = path.join(tmpdir.path, 'foo.cjs');
12+
fs.writeFileSync(fooPath, '');
13+
14+
const dirPath = path.join(tmpdir.path, 'delete_me');
15+
fs.mkdirSync(dirPath, {
16+
recursive: true
17+
});
18+
19+
const barPath = path.join(dirPath, 'bar.cjs');
20+
fs.writeFileSync(barPath, `
21+
module.exports = () => require('../foo.cjs').call()
22+
`);
23+
24+
const foo = require(fooPath);
25+
const unique = Symbol('unique');
26+
foo.call = common.mustCall(() => unique);
27+
const bar = require(barPath);
28+
29+
fs.rmSync(dirPath, { recursive: true });
30+
assert.strict.equal(bar(), unique);

0 commit comments

Comments
 (0)