SIGN IN SIGN UP

fix(arborist): match allowScripts keys for local paths (#9914)

## Summary

Fixes `allowScripts` matching for local file and tarball dependencies,
particularly on Windows.

Arborist stores a resolved Windows file source in this form:

```text
file:C:\project\vendor\pkg.tgz
```

However,  `npm-package-arg` parses an equivalent policy key into:

`saveSpec: file:C:/project/vendor/pkg.tgz`
`fetchSpec: C:\project\vendor\pkg.tgz`

The existing matcher compared `node.resolved` only with `saveSpec` and
`fetchSpec`. Neither comparison could match because one uses different
separators and the other lacks the `file:`  prefix.

## Changes

• Match local file and directory sources against
`file:${parsed.fetchSpec}`, which is the same representation produced by
Arborist's `consistentResolve()`.
• Resolve relative `allowScripts` file keys from the Arborist project
root rather than the process working directory.
• Reuse the Arborist file/directory matcher in `allow-scripts-writer`,
keeping runtime enforcement and policy updates consistent.
• Preserve the existing exact matching behavior for remote URLs.
• Preserve deny-wins behavior when an existing local-file policy entry
is `false`.

The implementation intentionally does not globally normalize path
separators. On POSIX systems, a backslash can be a literal filename
character, so replacing every  `\`  with  `/`  could cause two different
sources to share an approval.

The change also does not modify dependency resolution, lockfile
generation, fetching, linking, or `consistentResolve()`.

## Security considerations

Local file and tarball dependencies continue to require an exact source
identity.

This change does not:

• Match local packages by their self-reported name or version.
• Case-fold paths.
• Match by basename.
• Resolve paths through `realpath()`.
• Treat remote URLs as local file sources.
• Change registry or Git identity matching.

This preserves the manifest-confusion protections in `allowScripts`
while recognizing the exact Windows representation npm already
generates.

## Testing

Added regression coverage for:

• Backslash and forward-slash absolute Windows keys matching the same
local tarball.
• Relative keys resolving from the project root.
• `versionedKeyFor()` producing a key accepted by `isScriptAllowed()`.
• Different local paths remaining unmatched.
• Existing `false` file entries continuing to block approval.
• Windows UNC paths matching through the same `file:${fetchSpec}`
representation.
• POSIX filenames containing literal backslashes remaining distinct.
• Remote URL policies not matching `file:  sources`.
• `npm install-scripts prune` retaining valid local-file entries.

The focused Arborist matcher, policy-writer, and prune tests pass
locally. Native Windows path and UNC behavior is covered by
platform-specific tests and will run in the repository's Windows CI
matrix.

Fixes #9900

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
M
martin committed
d6c612258c571c71a00f496c1f8980ed13b8a4d9
Parent: 6e40f73
Committed by GitHub <noreply@github.com> on 8/25/2026, 8:24:11 PM