Nothing re-evaluates dependency overrides when renovate bumps the parent package
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 8
- Forks
- 7
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 62
Description
spec.overrides and spec.constraints (added in #669) are static. When renovate bumps the parent package, nothing checks whether the override should stay, move, or go away. Raised while reviewing #469, where the override happened to remain correct across the bump, but only because the upstream pin did not move.
What existing CI already catches
The Grype gate runs on every bump, so these are covered today:
- Override became insufficient — a new advisory affects our target version. The gate fails.
- Override became incompatible — the new parent version cannot work with our forced version. The build or scan fails.
What nothing catches
- Silent downgrade. Upstream raises its own floor above our pin, and because npx overrides are exact versions, ours drags it back down. The gate still passes, so this is invisible and is the only case that causes active harm.
- Lingering unnecessary override. Upstream fixed their pin and ours is now dead weight. The gate passes either way, so it just accumulates.
The two kinds behave differently
Worth separating, because it concentrates the risk:
- npx
overridesare exact pins (version: "1.26.0"). They go stale and can downgrade. - uvx
constraintsare usually floors (cryptography>=50.0.0,mcp[cli]>=1.28.1,<2). A floor cannot downgrade anything, and insufficiency is already caught by the gate.
So the problem is almost entirely on the npx side. Current inventory is 5 specs and 6 overrides:
| Spec | Override | Direct or transitive |
|---|---|---|
npx/brightdata-mcp |
@modelcontextprotocol/sdk 1.26.0 |
direct |
npx/mcp-jetbrains |
@modelcontextprotocol/sdk 1.26.0 |
direct |
npx/onchain-mcp |
@modelcontextprotocol/sdk 1.26.0 |
direct |
npx/astra-db-mcp |
undici 6.28.0 |
transitive, via @actions/http-client |
npx/browserbase-mcp-server |
sharp 0.35.3 |
direct |
npx/browserbase-mcp-server |
undici 6.28.0 |
transitive, via @ai-sdk/provider-utils |
Proposed starting point: a downgrade check
Deterministic, cheap, and it targets the one failure mode that silently causes harm.
Reading what upstream declares is not sufficient, because two of the six overrides are transitive and the parent does not declare them at all. The approach that handles both uniformly is to resolve the tree without the override and compare:
npm install --package-lock-only --no-audit --no-fund
That takes about 11 seconds for two servers and downloads no tarballs, so it is viable in CI.
One design subtlety found while prototyping. The lockfile resolves a package to a set of versions, not one, because npm keeps multiple copies. For the two servers above:
@modelcontextprotocol/sdkresolves to both1.21.2and1.30.0undiciresolves to both5.29.0and7.29.0
npm overrides are tree-global, so undici: 6.28.0 is simultaneously an upgrade of the 5.29.0 copy and a downgrade of the 7.29.0 one. A scalar "resolved vs pinned" comparison would be wrong. The check should:
- Resolve without overrides and collect every version of the overridden package in the tree.
- Report which copies the override raises and which it lowers.
- Flag when the pin sits below the highest version otherwise present, since that is the downgrade case.
Open question for implementation: whether that flag should hard-fail the build or post a comment. A legitimate case exists where upstream's floor exceeds our pin and our pin is still the security-correct target, which is exactly why undici was pinned to the last 6.x rather than 7.x. Leaning toward a comment plus a label, with the Grype gate remaining the only hard gate.
Follow-ons, not part of the starting point
- A renovate
customManagerfor npx override versions. Idiomatic here:renovate.jsonalready has 4, all matchingspec: -> package: -> version:per protocol directory, and an override entry is the same shape. That turns "nothing re-assesses the target" into "the target is bumped like any other dependency", with the Grype gate as the safety net. Deliberately not for uvx constraints, since rewriting inside a PEP 508 range is fiddly and a floor does not go stale the same way. - A periodic necessity audit. Answering "is this override still needed?" means building without it and comparing, which is too expensive per-PR. Better as a weekly report, and it could hang off the periodic scan fixed in #834.
Rejected
A blocking comment on every renovate PR touching an override-bearing spec. It would only fire on 5 specs so it is not especially noisy, but it is toil: a human re-deriving by hand what the check above can answer mechanically.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the npx spec directories and the existing renovate.json configuration, then inspect the current Grype gate and run npm install --package-lock-only --no-audit --no-fund without overrides. The implementation should collect every resolved version, identify when an exact override lowers the highest unoverridden version, and establish whether that result produces a comment and label or a hard failure.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- ci-cd, devops, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100