Skip to content

path: match Windows reserved names by component boundary - #61545

Open
HassanFleyah wants to merge 1 commit into
nodejs:mainfrom
HassanFleyah:fix/windows-device-paths
Open

path: match Windows reserved names by component boundary#61545
HassanFleyah wants to merge 1 commit into
nodejs:mainfrom
HassanFleyah:fix/windows-device-paths

Conversation

@HassanFleyah

@HassanFleyah HassanFleyah commented Jan 27, 2026

Copy link
Copy Markdown

What

win32.normalize() prefixes a path with .\ when it starts with a Windows
reserved device name (CON, NUL, PRN, COM1, LPT1, ...), so the result
cannot be read as a device reference.

The no-colon case was handled by calling isWindowsReservedName(path, -1),
which slices off the last character and compares the remainder against the
reserved list. That is an accident of String.prototype.slice(0, -1), not a
rule, and it is wrong in both directions.

False positives — ordinary files rewritten as devices:

normalize('CONx')   // '.\CONx'   ('CONx'.slice(0,-1) === 'CON')
normalize('NULs')   // '.\NULs'

False negatives — real device references left untouched:

normalize('NUL.txt')     // 'NUL.txt'
normalize('COM9.tar.gz') // 'COM9.tar.gz'

Per Microsoft's file-naming documentation, "avoid these names followed
immediately by an extension; NUL.txt and NUL.tar.gz are both equivalent to
NUL", so the second group are device references and the prefix is what they
need.

How

Take the leading path component, and if the part before its first . or :
is a reserved name, treat it as a device. A bare reserved name carrying
neither is left alone, so normalize('CON') === 'CON' is preserved. One rule
covers both directions.

What this breaks

Two existing assertions change, both in the false-negative group:

'LPT9.foo'  'LPT9.foo'  ->  '.\LPT9.foo'
'CON.TXT'   'CON.TXT'   ->  '.\CON.TXT'

They are updated here. This is user-visible, so the PR should carry
semver-major.

Nothing else moves. The CVE-2024-36139 assertions, the UNC cases, the
drive-letter cases and the \\.\ / \\?\ cases are unchanged — 80 of the 82
assertions across test-path-win32-normalize-device-names.js and
test-path-normalize.js produce identical output.

Verification

I built executable copies of lib/path.js — current main, the narrowing in
#64266, and this patch — and confirmed the unmodified copy matches
require('path') exactly before comparing.

Against #64266 over 297,026 generated inputs: 7,219 inputs differ, every one
of them a reserved name followed by a dot and an extension. Zero differences
of any other kind, so this is a strict superset of that narrowing rather than
a competing rule.

Refs

This PR previously proposed the same colonIndex !== -1 guard, in January.
See the comment below for why it was rewritten.

Copilot AI review requested due to automatic review settings January 27, 2026 14:03
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/path

@nodejs-github-bot nodejs-github-bot added needs-ci PRs that need a full CI run. path Issues and PRs related to the path subsystem. labels Jan 27, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens path.win32.normalize() handling for Windows device paths so that reserved device names prefixed with \\.\ or \\?\ are correctly detected even when the trailing colon is missing. It aims to close an edge case where such paths could bypass existing normalization logic.

Changes:

  • Refactors the UNC/device-root detection branch in win32.normalize() to special-case firstPart === '.' || firstPart === '?' for device roots.
  • Adds new logic to detect reserved device names when a \\.\ or \\?\ namespace prefix is present but the path lacks a colon, using WINDOWS_RESERVED_NAMES directly on the substring after the prefix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/path.js Outdated
@HassanFleyah

Copy link
Copy Markdown
Author

Gentle reminder for the maintainers to authorize the pending workflows

1 similar comment
@HassanFleyah

Copy link
Copy Markdown
Author

Gentle reminder for the maintainers to authorize the pending workflows

@codecov

codecov Bot commented Feb 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.21053% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.68%. Comparing base (4579957) to head (86e06aa).
⚠️ Report is 126 commits behind head on main.

Files with missing lines Patch % Lines
lib/path.js 84.21% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main   #61545   +/-   ##
=======================================
  Coverage   89.67%   89.68%           
=======================================
  Files         676      676           
  Lines      206555   206567   +12     
  Branches    39554    39555    +1     
=======================================
+ Hits       185230   185250   +20     
+ Misses      13461    13451   -10     
- Partials     7864     7866    +2     
Files with missing lines Coverage Δ
lib/path.js 97.39% <84.21%> (-0.10%) ⬇️

... and 36 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Flarna Flarna added author ready PRs that have at least one approval, no pending requests for changes, and a CI started. review wanted PRs that need reviews. labels Feb 22, 2026
@HassanFleyah
HassanFleyah force-pushed the fix/windows-device-paths branch 2 times, most recently from e01e1c1 to 1fb9ea6 Compare March 16, 2026 14:42
@HassanFleyah

Copy link
Copy Markdown
Author

Enhanced coverage with a new test suite, squashed all changes into a single commit, and updated the existing tests for better comprehensiveness. @Flarna could you please approve the pending workflows to trigger the CI checks?

@HassanFleyah
HassanFleyah force-pushed the fix/windows-device-paths branch from 1fb9ea6 to ab22425 Compare March 16, 2026 20:07
@HassanFleyah

Copy link
Copy Markdown
Author

Fixed the no-lonely-if linter error. @Flarna , could you please approve the pending workflows to trigger the CI checks? Thank you

@HassanFleyah
HassanFleyah force-pushed the fix/windows-device-paths branch from ab22425 to 86e06aa Compare March 16, 2026 20:34
@HassanFleyah

Copy link
Copy Markdown
Author

Hi @Flarna , gentle follow-up on this. The PR is now green with all checks passing and coverage improved. Would appreciate a final review when you have a moment. Thank you!

@Flarna Flarna added the windows Issues and PRs related to the Windows platform. label Apr 2, 2026
@Flarna

Flarna commented Apr 2, 2026

Copy link
Copy Markdown
Member

I'm not really an expert in this area. Maybe anyone from @nodejs/path could take a look please?

Comment thread lib/path.js Outdated
const colonIndex = StringPrototypeIndexOf(path, ':');
if (isWindowsReservedName(path, colonIndex)) {
// Ensure colonIndex is valid before calling isWindowsReservedName
if (colonIndex !== -1 && isWindowsReservedName(path, colonIndex)) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this breaks the existing { input: 'COM9.', expected: '.\\COM9.' }, testcase.

@trivikr trivikr removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Jun 22, 2026
@StefanStojanovic

Copy link
Copy Markdown
Contributor

I see that at least these 2 tests (maybe more) fail now:

  • test\parallel\test-path-win32-normalize-device-missing-colon.js
  • test\parallel\test-path-win32-normalize-device-names.js

Please look into that (or even better, run the full test suite) and fix all of the issues. Thanks in advance.

@HassanFleyah
HassanFleyah force-pushed the fix/windows-device-paths branch from 86e06aa to 68f19a1 Compare July 30, 2026 16:11
@HassanFleyah HassanFleyah changed the title path: fix normalization of Windows device paths missing colon path: match Windows reserved names by component boundary Jul 30, 2026
@HassanFleyah

Copy link
Copy Markdown
Author

I've rewritten this PR completely — new title, new description, nothing left
of the original diff. Summary of why:

@Flarna was right about COM9.. The colonIndex !== -1 guard I had here does
break it, and the same guard later landed as #64159 and was reverted in #64216
for what looks like exactly that reason.

@StefanStojanovic was right about the failing tests. The device-root branch I
added made rootEnd equal the path length, so tail came out empty and the
final `${device}\\${tail}` appended a trailing separator — \\.\\CON
became \\.\\CON\\. The premise in my original description was also wrong:
\\.\\CON without a colon is a namespace prefix plus an ordinary component,
like \\.\\PHYSICALDRIVE0, so returning it unchanged was already correct.

What replaces it addresses the underlying problem rather than the symptom. The
no-colon path was relying on isWindowsReservedName(path, -1), i.e. on
slice(0, -1) trimming the last character. That is wrong in both directions:
CONx matches and gets rewritten, NUL.txt does not match even though
Windows resolves it to the NUL device. Matching on the leading component up
to its first . or : covers both with one rule.

Details, the two assertions this changes, and the fuzzing I used to bound the
behaviour change are in the description above.

Sorry for the noise on the earlier revisions, and thanks to both of you for
the reviews — they're what pointed at the actual defect.

@HassanFleyah

Copy link
Copy Markdown
Author

request-ci

@HassanFleyah

Copy link
Copy Markdown
Author

I couldn't apply the \semver-major\ label due repository permissions. Could a maintainer please add it?

@HassanFleyah
HassanFleyah force-pushed the fix/windows-device-paths branch from 4ec2201 to 49fb702 Compare July 30, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-ci PRs that need a full CI run. path Issues and PRs related to the path subsystem. review wanted PRs that need reviews. windows Issues and PRs related to the Windows platform.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants