Skip to content

fix(yarn): stop a dependency named "version" from clobbering the entry version - #76

Open
shoemoney wants to merge 1 commit into
perplexityai:mainfrom
shoemoney:fix/yarn-lock-version-dependency-clobber
Open

fix(yarn): stop a dependency named "version" from clobbering the entry version#76
shoemoney wants to merge 1 commit into
perplexityai:mainfrom
shoemoney:fix/yarn-lock-version-dependency-clobber

Conversation

@shoemoney

Copy link
Copy Markdown

parseYarnLock captures the version field from every indented line of an entry with no first-wins guard (internal/ecosystem/yarn/yarn.go:160 on main). A line inside a dependencies:/peerDependencies:/optionalDependencies: block whose package is literally named "version" (a real npm package, ~19K weekly downloads) matches the same prefix and overwrites the already-captured entry version.

Concretely, a Classic entry for board2d@1.0.0 that depends on version@^0.1.0 was inventoried as version "^0.1.0", and the Berry form as "npm:^0.1.0". Exposure-catalog matching is exact name+version, so a compromised board2d@1.0.0 on the endpoint would silently not be flagged, and the inventory record carries a range spec instead of a version.

Fix: guard the capture with && cur.version == "", mirroring parsePnpmPackages in internal/ecosystem/pnpm/pnpm.go:328, which already guards the same field. This is safe because yarn (Classic and Berry) always emits the entry's own version as the first field of an entry.

Added TestScanLockfile_DependencyNamedVersion with classic and berry subtests; both fail on main with exactly the clobbered values above and pass with the guard. go test ./..., go test -race, go vet ./..., gofmt -l ., and ./bumblebee selftest are clean.

One thing I deliberately did not do: require the version line to sit at exactly 2 spaces of indent. First-wins alone is sufficient given yarn's emission order, and an indent requirement would be a second behaviour change with its own edge cases (tab-indented hand-edited lockfiles), better considered separately if wanted.

…y version

parseYarnLock scans every indented line of an entry with no first-wins
guard, so a line inside a dependencies:/peerDependencies: block whose
package is literally named "version" (a real npm package) matches the
version-field prefix and overwrites the already-captured entry version.
A Classic entry for board2d@1.0.0 that depends on version@^0.1.0 was
inventoried as version "^0.1.0"; the Berry form emitted "npm:^0.1.0".
Since exposure matching is exact name+version, a compromised package at
the pinned version was silently not flagged.

Guard the capture with first-wins, the same way parsePnpmPackages in
internal/ecosystem/pnpm/pnpm.go already guards the same field
(strings.HasPrefix(trim, "version:") && cur.version == ""); the yarn
twin never got the guard. Safe because yarn Classic and Berry always
emit the entry's own version as the first field of an entry.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant