Add a pin-override action for advisories Dependabot cannot fix - #29
Merged
Conversation
Dependabot fixes a transitive dependency with `yarn up -R <pkg>@<fixed>`, which operates on a package NAME across the whole tree. A single parent pinning that name to an exact version blocks it, even when other copies of the same package could move freely. Strapi does this everywhere: @strapi/upload requires sharp@0.35.3, not ^0.35.3. The failure takes two shapes, and the quiet one is worse. A dedicated security job fails the run with security_update_not_possible, which is at least visible. The daily version-update job instead logs "No update possible for qs 6.15.3" and reports SUCCESS — a qs advisory sat open for a week that way behind a green run history. Either way the fix a human writes is identical every time: a range entry in `resolutions` that moves the shared lockfile entry. This writes those. Three rules keep it honest: * Inside the compatibility line only, mirroring the org's no-majors policy — and for 0.x the MINOR is the line. esbuild ships 0.21.5 and 0.28.1 in one tree with a fix in 0.25.0; keyed on the major alone those look like one line and the "fix" would be a breaking bump. * Never a direct dependency. Dependabot edits package.json perfectly well on its own, and shadowing a declared dependency creates a second pin to maintain. * Every entry is verified by re-running the audit rather than trusting version arithmetic. Anything that does not actually clear its advisory is reverted; a pin that fixes nothing is worse than no pin, because somebody has to explain it later. Scoped vs unscoped is decided from the lockfile, not guessed. Unscoped when the package has one compatibility line: qs was pinned exactly by ~20 @strapi/* packages that Yarn had deduped onto ONE entry, so a scoped @strapi/admin/qs would have forked a second entry and left the vulnerable version for the other nineteen. Scoped to the parent NAME (never its version, so it survives a bump) when several lines coexist and one single-version parent pulls the vulnerable one: undici 7.28.0 comes only from cheerio, while 6.28.0 is Strapi's exact pin and is not even in the advisory. Refused, with the reason named, when the parent itself has several versions — brace-expansion is pulled by minimatch 3.1.5, 9.0.9 and 10.2.5 at once, and the only correct key embeds a parent version that rots on the next minimatch bump. That is a human's call. Measured against wemove.eu/backend: two resolutions covering six alerts (tar -> ^7.5.22, cheerio/undici -> ^7.29.1 clearing five advisories), with eleven named for a human and Strapi's undici 6.28.0 pin left untouched. 40 unit tests, no network. Wired into the `ci` gate alongside python-pins. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Why
Dependabot fixes a transitive dependency with
yarn up -R <pkg>@<fixed>, which operates on a package name across the whole tree. One parent pinning that name to an exact version blocks it, even when other copies could move freely. Strapi does this everywhere (@strapi/uploadrequiressharp@0.35.3, not^0.35.3).Two failure shapes, and the quiet one is worse:
security_update_not_possible. At least visible.No update possible for qs 6.15.3and reports success. A qs advisory sat open for a week behind a green run history.The fix a human writes is identical every time: a range entry in
resolutions. This action writes those, and nothing else.Three rules that keep it honest
Inside the compatibility line only, mirroring the no-majors policy — and for
0.xthe minor is the line. esbuild ships 0.21.5 and 0.28.1 in one tree with a fix in 0.25.0; keyed on the major alone those look like one line and the "fix" is a breaking bump.Never a direct dependency. Dependabot edits
package.jsonfine on its own; shadowing a declared dependency just creates a second pin to maintain.Verified by re-running the audit, not by version arithmetic. Anything that does not actually clear its advisory is reverted. A pin that fixes nothing is worse than no pin, because someone has to explain it later.
Scoped vs unscoped is read from the lockfile, not guessed
qs@strapi/*parents deduped onto one entry. A scoped@strapi/admin/qsforks a second entry and leaves 6.15.3 for the other nineteen.cheerio/undiciMeasured against wemove.eu/backend
Two resolutions covering six alerts; Strapi's
undici@npm:6.28.0left untouched in the lockfile, verified.Verification
publishedis monkeypatched). Wired into thecigate next to python-pins.Not wired to anything yet
This is the action only. The scheduled workflow that calls it and opens the PR ships as a template in
dependency-policy, with thin callers per repo, so scheduling stays per-repo the wayyarn-bump.ymldoes.🤖 Generated with Claude Code