From 513ccb7f366682b3aef2f191261f84c1a765d606 Mon Sep 17 00:00:00 2001 From: HassanFleyah Date: Tue, 27 Jan 2026 16:40:16 +0300 Subject: [PATCH 1/3] path: fix normalization of Windows device paths missing colon --- lib/path.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/lib/path.js b/lib/path.js index 63b037cddfb986..8fb0a966a5602b 100644 --- a/lib/path.js +++ b/lib/path.js @@ -397,18 +397,29 @@ const win32 = { j++; } if (j === len || j !== last) { - if (firstPart === '.' || firstPart === '?') { - // We matched a device root (e.g. \\\\.\\PHYSICALDRIVE0) - device = `\\\\${firstPart}`; - rootEnd = 4; - const colonIndex = StringPrototypeIndexOf(path, ':'); - // Special case: handle \\?\COM1: or similar reserved device paths + if (firstPart === '.' || firstPart === '?') { + // We matched a device root (e.g. \\\\.\\PHYSICALDRIVE0) + device = `\\\\${firstPart}`; + rootEnd = 4; + const colonIndex = StringPrototypeIndexOf(path, ':'); + + if (colonIndex !== -1) { + // Handle \\?\COM1: or similar reserved device paths with colon const possibleDevice = StringPrototypeSlice(path, 4, colonIndex + 1); if (isWindowsReservedName(possibleDevice, possibleDevice.length - 1)) { device = `\\\\?\\${possibleDevice}`; rootEnd = 4 + possibleDevice.length; } - } else if (j === len) { + } else { + // Handle \\.\CON or \\?\CON where the colon is missing + const possibleDevice = StringPrototypeSlice(path, 4); + const upper = StringPrototypeToUpperCase(possibleDevice); + if (ArrayPrototypeIncludes(WINDOWS_RESERVED_NAMES, upper)) { + device = `\\\\.\\${possibleDevice}`; + rootEnd = 4 + possibleDevice.length; + } + } + } else if (j === len) { // We matched a UNC root only // Return the normalized version of the UNC root since there // is nothing left to process From 732e182da3207ce2db28a08f3940355fe72c33eb Mon Sep 17 00:00:00 2001 From: HassanFleyah Date: Tue, 27 Jan 2026 17:28:15 +0300 Subject: [PATCH 2/3] test: add test case for device paths missing colon --- ...th-win32-normalize-device-missing-colon.js | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test/parallel/test-path-win32-normalize-device-missing-colon.js diff --git a/test/parallel/test-path-win32-normalize-device-missing-colon.js b/test/parallel/test-path-win32-normalize-device-missing-colon.js new file mode 100644 index 00000000000000..567df5ce6dc97c --- /dev/null +++ b/test/parallel/test-path-win32-normalize-device-missing-colon.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const path = require('path'); + +// هذا السطر يمنع تشغيل الاختبار على غير ويندوز لأنه خاص بويندوز فقط +if (!common.isWindows) + common.skip('this test is for win32 only'); + +// Test cases for reserved device names missing the trailing colon +// See: https://github.com/nodejs/node/pull/[YOUR_PR_NUMBER] (optional) + +// 1. Check \\.\CON (Missing colon) +assert.strictEqual(path.win32.normalize('\\\\.\\CON'), '\\\\.\\CON'); +assert.strictEqual(path.win32.normalize('\\\\.\\con'), '\\\\.\\con'); // Case insensitive + +// 2. Check \\?\CON (Missing colon) +assert.strictEqual(path.win32.normalize('\\\\?\\CON'), '\\\\?\\CON'); +assert.strictEqual(path.win32.normalize('\\\\?\\con'), '\\\\?\\con'); + +// 3. Check that regular files are NOT affected (Sanity check) +assert.strictEqual(path.win32.normalize('\\\\.\\PhysicalDrive0'), '\\\\.\\PhysicalDrive0'); + +// 4. Check join behavior (to ensure it acts as a root) +// If it's a root, joining '..' should not strip the device. +const joined = path.win32.join('\\\\.\\CON', '..'); +assert.strictEqual(joined, '\\\\.\\CON'); \ No newline at end of file From 6ffabb1cc7e17582a3f059e189a0b2791cb912c9 Mon Sep 17 00:00:00 2001 From: HassanFleyah Date: Wed, 28 Jan 2026 23:07:46 +0300 Subject: [PATCH 3/3] style: fix linter errors --- lib/path.js | 44 +++++++++---------- ...th-win32-normalize-device-missing-colon.js | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/path.js b/lib/path.js index 8fb0a966a5602b..c4350558b4e2d0 100644 --- a/lib/path.js +++ b/lib/path.js @@ -397,29 +397,29 @@ const win32 = { j++; } if (j === len || j !== last) { - if (firstPart === '.' || firstPart === '?') { - // We matched a device root (e.g. \\\\.\\PHYSICALDRIVE0) - device = `\\\\${firstPart}`; - rootEnd = 4; - const colonIndex = StringPrototypeIndexOf(path, ':'); - - if (colonIndex !== -1) { - // Handle \\?\COM1: or similar reserved device paths with colon - const possibleDevice = StringPrototypeSlice(path, 4, colonIndex + 1); - if (isWindowsReservedName(possibleDevice, possibleDevice.length - 1)) { - device = `\\\\?\\${possibleDevice}`; - rootEnd = 4 + possibleDevice.length; - } - } else { - // Handle \\.\CON or \\?\CON where the colon is missing - const possibleDevice = StringPrototypeSlice(path, 4); - const upper = StringPrototypeToUpperCase(possibleDevice); - if (ArrayPrototypeIncludes(WINDOWS_RESERVED_NAMES, upper)) { - device = `\\\\.\\${possibleDevice}`; - rootEnd = 4 + possibleDevice.length; + if (firstPart === '.' || firstPart === '?') { + // We matched a device root (e.g. \\\\.\\PHYSICALDRIVE0) + device = `\\\\${firstPart}`; + rootEnd = 4; + const colonIndex = StringPrototypeIndexOf(path, ':'); + + if (colonIndex !== -1) { + // Handle \\?\COM1: or similar reserved device paths with colon + const possibleDevice = StringPrototypeSlice(path, 4, colonIndex + 1); + if (isWindowsReservedName(possibleDevice, possibleDevice.length - 1)) { + device = `\\\\?\\${possibleDevice}`; + rootEnd = 4 + possibleDevice.length; + } + } else { + // Handle \\.\CON or \\?\CON where the colon is missing + const possibleDevice = StringPrototypeSlice(path, 4); + const upper = StringPrototypeToUpperCase(possibleDevice); + if (ArrayPrototypeIncludes(WINDOWS_RESERVED_NAMES, upper)) { + device = `\\\\.\\${possibleDevice}`; + rootEnd = 4 + possibleDevice.length; + } } - } - } else if (j === len) { + } else if (j === len) { // We matched a UNC root only // Return the normalized version of the UNC root since there // is nothing left to process diff --git a/test/parallel/test-path-win32-normalize-device-missing-colon.js b/test/parallel/test-path-win32-normalize-device-missing-colon.js index 567df5ce6dc97c..5f171bbdaeec2f 100644 --- a/test/parallel/test-path-win32-normalize-device-missing-colon.js +++ b/test/parallel/test-path-win32-normalize-device-missing-colon.js @@ -24,4 +24,4 @@ assert.strictEqual(path.win32.normalize('\\\\.\\PhysicalDrive0'), '\\\\.\\Physic // 4. Check join behavior (to ensure it acts as a root) // If it's a root, joining '..' should not strip the device. const joined = path.win32.join('\\\\.\\CON', '..'); -assert.strictEqual(joined, '\\\\.\\CON'); \ No newline at end of file +assert.strictEqual(joined, '\\\\.\\CON');