Skip to content

fix(scripts): scope a hoisted npm copy by the ranges it satisfies - #5529

Merged
cliffhall merged 1 commit into
v2/mainfrom
v2/chore/5506-alerts-hoisted-copy-scope
Nov 12, 2026
Merged

cliffhall merged 1 commit into
v2/mainfrom
v2/chore/5506-alerts-hoisted-copy-scope

Conversation

@cliffhall

Copy link
Copy Markdown
Member

Closes #5506

Description

The Dependabot alert sweep (scripts/dependabot-alerts.mjs) treated a hoisted root copy of a package as the declared copy of every manifest that declares the package. A workspace whose declared range that copy does not satisfy has its own nested copy, so for that workspace the root copy is only transitive. For #5481 this produced the wrong fix ("raise the declared range in src/filesystem/package.json", already ^11.0.4), no overrides step, and a wrong server-filesystem label.

  • npmDeclarers now returns { manifest, range } for each declaring manifest.
  • npmCopyDeclarers counts the hoisted root copy as a manifest's declared copy only when semver.satisfies(version, range). A range semver cannot read (an alias, a URL) keeps the old behavior. A workspace's own top-level copy is unchanged.
  • The EOVERRIDE warning, and the parent-scoped overrides form it requires, now apply only when the root package.json declares the package, which is npm's actual rule (verified below).

Server Details

None (repository-wide): the alert sweep script and its tests.

Motivation and Context

#5506, found while fixing #5481.

How Has This Been Tested?

No client-observable surface (a sweep script), so targeted probes:

  • Regression test for the chore(deps): bump minimatch to 11.0.1 in package-lock.json (1 advisory) #5481 lockfile shape: src/filesystem declares minimatch ^11.0.4 with its own nested 11.0.4, and a hoisted 10.2.5 pulled in only by dev tooling. Asserts the root copy has no declarer, the labels carry no server scope, and the body asks for an overrides entry with no "Raise the declared range" step and no EOVERRIDE note. Run against the script from v2/main, it fails (actual: [ 'src/filesystem/package.json' ]); against this branch, all 45 tests pass.
  • EOVERRIDE rule, against npm 11.20.0 (npm install --package-lock-only in scratch projects):
    • root with workspace a declaring minimatch ^11.0.4, root overrides: { "minimatch": "^11.0.1" }: installs cleanly;
    • root declaring minimatch ^10.0.0 itself with the same override: npm error code EOVERRIDE — Override for minimatch@^10.0.0 conflicts with direct dependency.
  • npm run local:gate exits 0.

Breaking Changes

None.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature
  • Breaking change
  • Documentation update

Checklist

  • I have read the MCP Protocol Documentation (not applicable: no protocol surface touched)
  • My changes follow MCP security best practices (not applicable: no server change)
  • I have updated the server's README accordingly (not applicable: no server change)
  • I have added a changeset (not applicable: nothing a TypeScript server publishes changes)
  • I have tested this with an LLM client (not applicable: no server-facing change; targeted probes above)
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling (an unreadable range falls back to the previous behavior)
  • I have documented all environment variables and configuration options (not applicable: none added)

🤖 Generated with Claude Code

The alert sweep treated the hoisted root copy of a package as serving every
manifest that declares it. A workspace whose declared range that copy does
not satisfy has a nested copy of its own, so for it the root copy is only
transitive. #5481 got the wrong fix (raise src/filesystem's range) and the
wrong scope label (server-filesystem) because of it.

npmDeclarers now returns each declarer's range, and npmCopyDeclarers counts
the root copy as a manifest's only when semver.satisfies holds. The
EOVERRIDE warning is now given only when the root package.json declares the
package, which is npm's actual rule (verified against npm 11.20: a
workspace's declaration installs cleanly under a package-wide override).

Closes #5506

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: cliffhall <[email protected]>
@cliffhall cliffhall added the v2 label Nov 12, 2026
@changeset-bot

changeset-bot Bot commented Nov 12, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 27122a6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No active review issues remain for the hoisted-dependency attribution and override-guidance changes.

No open issues 🎉

What changed in this PR

Fixes the Dependabot alert sweep’s hoisted npm dependency attribution, addressing #5506 so generated fix instructions and server labels reflect the declared ranges that a vulnerable copy satisfies.

Changes:

  • Records declared ranges and uses semver matching to identify which manifests a hoisted copy serves, preserving fallback behavior for unreadable ranges.
  • Limits parent-scoped override advice and EOVERRIDE warnings to root-package declarations.
  • Adds regression coverage for a vulnerable hoisted copy alongside a fixed workspace copy.
File Description
scripts/​dependabot-alerts.test.mjs Updates declaration fixtures and tests hoisted-copy attribution, labels, and override advice.
scripts/​dependabot-alerts.mjs Filters hoisted-copy declarers by range and corrects override scoping guidance.

Review effort: Balanced

@cliffhall

Copy link
Copy Markdown
Member Author

Copilot round 1: clean, no findings and no suppressed comments. Review loop converged on the first round.

@cliffhall
cliffhall merged commit bdfb2e3 into v2/main Nov 12, 2026
46 checks passed
@cliffhall
cliffhall deleted the v2/chore/5506-alerts-hoisted-copy-scope branch November 12, 2026 04:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dependabot-alerts: wrong fix and scope label for a hoisted copy no declared range resolves to

2 participants