diff --git a/lib/path.js b/lib/path.js index 63b037cddfb986..253f5dc6165804 100644 --- a/lib/path.js +++ b/lib/path.js @@ -471,7 +471,7 @@ const win32 = { } while ((index = StringPrototypeIndexOf(path, ':', index + 1)) !== -1); } const colonIndex = StringPrototypeIndexOf(path, ':'); - if (isWindowsReservedName(path, colonIndex)) { + if (colonIndex !== -1 && isWindowsReservedName(path, colonIndex)) { return `.\\${device ?? ''}${tail}`; } if (device === undefined) { diff --git a/test/parallel/test-path-normalize.js b/test/parallel/test-path-normalize.js index 8b537676dbf45d..c0e7309602b60b 100644 --- a/test/parallel/test-path-normalize.js +++ b/test/parallel/test-path-normalize.js @@ -69,6 +69,17 @@ assert.strictEqual(path.win32.normalize('//server/share/dir/../../../?/D:/file') assert.strictEqual(path.win32.normalize('//server/goodshare/../badshare/file'), '\\\\server\\goodshare\\badshare\\file'); +// A path is only a Windows reserved device name when the reserved name is +// followed by a colon. A name that merely starts with a reserved name (and has +// no colon) must be left untouched and not be prefixed with `.\`. +assert.strictEqual(path.win32.normalize('CONx'), 'CONx'); +assert.strictEqual(path.win32.normalize('NULs'), 'NULs'); +assert.strictEqual(path.win32.normalize('LPT1x'), 'LPT1x'); +assert.strictEqual(path.win32.normalize('PRNzzz'), 'PRNzzz'); +assert.strictEqual(path.win32.normalize('CON'), 'CON'); +// With a trailing colon the reserved-name handling still applies. +assert.strictEqual(path.win32.normalize('CON:'), '.\\CON:.'); + assert.strictEqual(path.posix.normalize('./fixtures///b/../b/c.js'), 'fixtures/b/c.js'); assert.strictEqual(path.posix.normalize('/foo/../../../bar'), '/bar');