Repository navigation
Conversation
mcollina
approved these changes
Oct 20, 2026
mcollina
left a comment
Member
There was a problem hiding this comment.
lgtm
I’m unsure about the semversiness of this, as it’s a substantial behavior change.
Member
|
unfortunately it needs a rebase now |
moeghassi
force-pushed
the
module-fail-closed-denied-package-json
branch
from
October 20, 2026 20:29
9759adf to
9c6c2a4
Compare
Author
|
@mcollina rebased. |
Member
|
@nodejs/tsc ptal |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #65030 +/- ##
=======================================
Coverage 90.49% 90.49%
=======================================
Files 797 797
Lines 278362 278367 +5
Branches 53491 53489 -2
=======================================
+ Hits 251906 251916 +10
+ Misses 16815 16812 -3
+ Partials 9641 9639 -2
🚀 New features to boost your workflow:
|
GetPackageJSON() now reports unreadable manifests, but treats a directory named package.json as absent and reports other read failures as ERR_INVALID_PACKAGE_CONFIG. Only ENOENT and ENOTDIR now mean "no package.json here". Any other read error is treated as a security-relevant signal and throws ERR_ACCESS_DENIED instead of falling back, so a denied or quarantined manifest aborts resolution for every package style (default index.js, "main", and "exports"). The failure is intentionally not negative-cached so the deny is not latched for the lifetime of the process. The parent-scope walks and FindNodeModulesPackage now stop when an exception is pending so the thrown error propagates instead of being swallowed by continuing up the tree. Cover directory manifests, retry after a failed read, and unreadable files with regression tests. Signed-off-by: Moe Ghasemi <moeghasemi@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: c9e5a501-399c-443b-9241-2c71246b185f
Cover pending exceptions in package JSON traversal and scope lookups, including failed-read recovery, and update the loader error expectation. Apply the C++ formatting requested by CI. Signed-off-by: Moe Ghasemi <moeghasemi@microsoft.com> Assisted-by: GitHub Copilot Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 39bc512f-a84d-4c8b-a2f8-a2f25e9ee33f
moeghassi
force-pushed
the
module-fail-closed-denied-package-json
branch
from
October 27, 2026 21:54
9c6c2a4 to
3e07400
Compare
Author
|
@mcollina fixed the outdated error assertion and C++ formatting, and added tests for the uncovered exception paths and recovery behavior. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GetPackageJSON()insrc/node_modules.cctreated every negative returnfrom
ReadFileSync()identically: it negative-cached the path and reported themanifest as absent. As a result a denied read (
EACCES/EPERM, or ananti-malware/EDR block that surfaces as a failed open) is indistinguishable from
ENOENT, so resolution silently falls back toindex.jsand the package loadsanyway.
This means an on-endpoint scanner cannot stop a
require()/importof aquarantined package by denying/quarantining its
package.json— Node treats thedenied manifest as "no manifest" and runs the code.
Change
ENOENTandENOTDIRnow mean "there is nopackage.jsonhere"(unchanged behavior, still negative-cached).
ERR_ACCESS_DENIEDinstead of falling back — so a denied/quarantined manifestaborts resolution for every package style (default
index.js,"main", and"exports"), not justexports-based ones. The failure is intentionallynot negative-cached so the deny isn't latched for the process lifetime.
TraverseParentandGetPackageScopeConfignow stop walking parent scopeswhen an exception is pending, so the thrown error propagates instead of being
swallowed by continuing up the tree.
Adds a POSIX regression test (
chmod(0)the manifest → assertsERR_ACCESS_DENIEDand that theindex.jsfallback did not run; skipped onWindows and when running as root).
Notes for reviewers
non-
ENOENTread errors). It may warrant a semver-major label: users witha legitimately unreadable ancestor directory (
EACCESwhile walking up forpackage-type detection) would now get a thrown error instead of silent
fallback. I'm happy to scope the throw more narrowly (e.g. only the target
package's own manifest, not ancestor
typewalks) if preferred.scope is very welcome.
Local validation
Validated on Windows x64 on October 27, 2026, after rebasing onto
nodejs/nodemain at75191c45afebca2e633eeb94ab95ccfdd7cfae76.node.exeandcctest.exebuilt successfully withuser-local LLVM/clang-cl 21.1.8, Visual Studio 2022 libraries, and the Windows SDK.
es-moduleandmodule-hookssuites and the parallel module, require, andpermission tests. Skips reflect platform/environment constraints, including
the POSIX-only
chmod(0)case.passed for default
index.js,"main", and"exports"packages. Bothrequire()and ESM import rejected denied manifests withERR_ACCESS_DENIED,no fallback code executed, and loading recovered after access was restored.
These supplemental checks are separate from the JavaScript test count above.
src/node_modules.ccpassed the repository-pinned clang-format 15,cpplint, and checkimports checks;
git diff --checkpassed.The added portable regression test covers malformed and directory manifests,
preservation of the first error, repeated failed lookups, and recovery after
repair. It exercises parent traversal, private-package root traversal, and both
ESM package-scope lookup variants.
Before the rebase, a separate LLVM-instrumented build confirmed execution of all
three previously uncovered exception-return paths and both outcomes of their
guards, including both ESM template variants. The new regression test was rerun
successfully on the rebased build; no refreshed Codecov percentage is claimed.