fix(deps): take the fix for the fifteen OSV advisories blocking every PR - #17029
Merged
Conversation
`Validate Package Dependencies` (OSV-Scanner against pnpm-lock.yaml) exits 1 on fifteen advisories across seven packages, so every PR is red and main's next scheduled run goes red on its own. All fifteen name a published fix version, so this is the take-the-fix path osv-scanner.toml's header describes, not the exemption path; that ledger keeps its zero entries and is untouched, as is .github/workflows/validate-deps.yml. GHSA-2xp9-vwfh-vxw4 9.5 next 16.3.1 -> 16.3.3 GHSA-p293-qw3h-jr36 9.0 next 16.3.1 -> 16.3.3 GHSA-rgj7-g3m4-5g8c 8.9 sharp 0.35.3 -> 0.35.4 GHSA-2x7j-588g-ccc2 7.5 nodemailer 9.0.5 -> 9.1.1 GHSA-cc9r-2j5m-2m83 6.5 nodemailer 9.0.5 -> 9.1.1 GHSA-wmmp-3585-3rmp 6.5 nodemailer 9.0.5 -> 9.1.1 GHSA-8m3c-c648-2xjj 5.9 nodemailer 9.0.5 -> 9.1.1 GHSA-gqvv-2mrq-wpjv 6.5 hono 4.12.34 + 4.13.2 -> 4.13.7 GHSA-crvj-82cr-hjcx 5.9 hono 4.12.34 + 4.13.2 -> 4.13.7 GHSA-g6gw-c38x-mqfc 5.3 hono 4.12.34 + 4.13.2 -> 4.13.7 GHSA-82fw-gwwq-j7x9 5.9 vitest 4.1.10 -> 4.1.11 GHSA-82fw-gwwq-j7x9 5.9 @vitest/mocker 4.1.10 -> 4.1.11 hono was resolved at TWO versions and both were flagged. The transitive copy @modelcontextprotocol/sdk pulled sat exactly on the old `^4.12.34` override floor, so nothing ever re-resolved it, while our own three declarations floated up to 4.13.2. Lifting the target to `^4.13.5` excludes the floor, forces both edges to re-resolve, and the tree deduplicates onto one hono@4.13.7 — moving only our declarations would have left the transitive copy flagged. sharp and hono are TARGET-ONLY override lifts: both selectors already sit at the compatibility boundary this file's header mandates (`<0.36.0` for the 0.x line, `<5.0.0` for hono), so neither moves. next is an exact pin in the private docs app. nodemailer and hono move their declared ranges in lockstep with the override, per the downstream-install rule the overrides header states — the @objectstack/hono PEER range stays the permissive `^4.12.8`, and metadata-core's optional `vitest` peer stays `^4.0.0`, both for the reason that header gives: a peer states what host we work against. @vitest/coverage-v8 moves in lockstep with vitest because its peer on vitest is EXACT (`"vitest": "4.1.10"`), so a lone vitest bump would have broken the peer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37
Takes the tip that already carries the plugin-audit landing, per the ordering constraint on this branch. Re-derives pnpm-lock.yaml from the merged manifests in the next commit.
… merge
The vitest move was originally taken with `pnpm update`, which re-resolves the
whole updated SUBTREE and not just the package named: it dragged second,
patch-level copies of postcss (8.5.28 beside 8.5.26) and picomatch (4.0.7
beside 4.0.5) into the tree, plus @inquirer/*, ip-address, type-fest and
seroval — none of them flagged, none of them forced by a flagged bump.
Regenerating from origin/main's lockfile with a plain `pnpm install`, so the
only inputs are the declared ranges and the two override targets this PR
changes, gives a strictly tighter result:
registry tuples 1372 -> 1370 (was 1372 -> 1373 under `pnpm update`)
removed 50, added 48, and EVERY added tuple is in a flagged family
Two duplicates collapse and nothing else moves:
hono 4.12.34 + 4.13.2 -> 4.13.7 (the OSV duplicate)
tinyexec 0.3.2 + 1.2.4 + 1.3.0 -> 0.3.2 + 1.3.0
vitest 4.1.10 held 1.2.4; 4.1.11 declares the same `^1.0.2` and dedupes
onto the 1.3.0 copy that was already in the tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zTkyNHJ7TkuN2oXtP5x37
Contributor
📓 Docs Drift Check
What this run could not see
Coarse fallback — 11 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
os-trump
marked this pull request as ready for review
September 9, 2026 03:35
os-trump
enabled auto-merge
September 9, 2026 03:35
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.
Fixes #16999
Clause-②: no — re-derived from the delivered diff, not inherited. The diff moves dependency versions and two
pnpm-workspace.yamloverride targets. It adds no exported symbol, no key on any published payload, and changes no accept/reject behaviour of ours; no file underpackages/spec/src/is touched. Two published manifests change a declared dependency RANGE (@objectstack/plugin-email→nodemailer,@objectstack/plugin-hono-server→hono), which is what the changeset gradespatch— a dependency range is not our published surface. Nothing here forced a change to our own API, so there is nothing to stop and report on that axis.What was failing, and why it was nobody's PR
Validate Package Dependencies→Audit dependencies for known vulnerabilities (OSV-Scanner)exited 1 on 15 advisories across 7 packages (2 Critical, 2 High, 11 Medium). The card established that no lockfile moved; this PR re-confirmed it independently before touching anything:origin/main'spnpm-lock.yamlat the time this branch was cut was byte-identical (sha256704c58a3e3212f67…) to the tree of the PR whose CI run produced the red. Same scanner input, opposite verdict, ~22h apart — the advisory database moved, not the repository.Every one of the fifteen names a published fix version, so this is the take-the-fix path
osv-scanner.toml's own header describes. No advisory is suppressed, allow-listed or ignored.osv-scanner.tomlkeeps its zero exemptions and is untouched, as is.github/workflows/validate-deps.yml.Scanner verdicts — measured, on real CI runs
No OSV scanner is reachable from the dev container (
api.osv.devand thegoogle/osv-scannerrelease asset are both refused by the egress proxy — CONNECT 403, reported rather than routed around, and there is noosv-scannerpackage on npm). So every verdict below is a realvalidate-depsrun, dispatched viaworkflow_dispatchon the same workflow the gate uses.origin/main@8d4690b8f(current, post-#16996)Total 7 packages affected by 15 known vulnerabilities (2 Critical, 2 High, 11 Medium, 0 Low, 0 Unknown),Exit code: 12b856a472(the head this PR ships)The
origin/mainrun is the "before" in its own right — not a stand-in. It scanned the current tip and reported the identical fifteen rows, so this PR clears all 15 and leaves none behind. Nothing was un-clearable: every advisory had a fix version and every fix version was takeable.What moved
nextapps/docssharp^0.35.0→^0.35.4nodemailer@objectstack/plugin-emaildeclares^9.1.1hono^4.12.34→^4.13.5, declarations in lockstepvitest^4.1.10→^4.1.11workspace-wide@vitest/mockervitestnexttook 16.3.3, the version the advisories name, not the newer 16.3.4 — the fix line, minimally.nodemailertook^9.1.1because the four advisories split across two fix lines (9.1.0 and 9.1.1) and one floor has to cover both; the 10.x major is deliberately not taken.honoresolves to 4.13.7 because the override target is a floating^4.13.5, which is the shapepnpm-workspace.yaml's header mandates for an OSV floor.The
honoduplicate — resolved by moving both, not onehonowas flagged at two resolved versions and the card is right that clearing one is not clearing the gate. Both are gone, and the override lift is what did it:^4.12.34target.@modelcontextprotocol/sdk. It sat exactly on the old target's floor, so it already satisfied^4.12.34and lockfile inertia never re-resolved it — which is precisely why a duplicate existed at all.Lifting the target to
^4.13.5excludes 4.12.34, forces both edges to re-resolve, and the tree deduplicates onto a singlehono@4.13.7. Measured after: onehonotuple in the lockfile, down from two.@hono/node-serverpeershono@^4.12.34, which 4.13.x satisfies, so both of its resolved versions (2.0.12, 2.1.1) re-peered onto the single copy and neither had to move.A bump that moved only our three declarations would have left the
@modelcontextprotocol/sdkcopy flagged and the gate red — the failure mode the card named.Scope — what did NOT move
The lockfile's registry tuples were diffed base-to-head: 1372 → 1373, 62 removed and 63 added, and every one of them belongs to a flagged package or is forced by one:
sharpplatform binaries (@img/sharp-*0.35.3 → 0.35.4,@img/sharp-libvips-*1.3.2 → 1.3.3) andnode-abi, whichsharp's prebuild chain pins;nextplatform binaries (@next/swc-*,@next/env16.3.1 → 16.3.3) and its own transitivesisbot,seroval,seroval-plugins;@vitest/*packages, moving withvitest;honotwo → one,nodemailer,@inquirer/*,ip-address,type-fest, and second patch-level copies ofpicomatchandpostcssre-resolved inside the updated subtree.No unrelated package was refreshed.
The ordering constraint — its stated mechanism does not exist, and the branch took
origin/mainanywayThis branch was dispatched with an ordering constraint:
pnpm-lock.yamlwas said to be claimed by open PR #16996 underNo other open PR may claim the same single-writer path, with instructions to wait rather than push through if that gate refused. It cannot refuse on this path.scripts/check-single-claim-paths.mjsdeclares exactly one single-claim path:and its own header rules the lock file out by name: "Anything that is merely hot (a lock file, a root manifest, a shared registry) does NOT belong here" — because keying repo-wide on shared paths measured 68 false accusations per 300 PRs, the lock file being the top collision at 33. So no refusal was possible, none occurred, and nothing was pushed through. Recorded because the premise, not the instruction, was wrong.
The substance of the constraint was honoured regardless: #16996 landed as
8d4690b8fwhile this work was in progress, and this branch merged that tip (3b79bc400) and re-derived the lockfile on top of it. Nothing raced.Verification
A dependency bump's risk is breakage, so this is a full run, not a narrowed one.
Full workspace build
Full test suite — not a narrowed run
origin/main. It is not a regression from this PR, and the control is measured rather than asserted.The control, run in a separate clean worktree at
origin/main=8d4690b8fwith the same build scope as this branch:pnpm build(73/73)Tests 1 failed | 6 passed (7)origin/main8d4690b8fpnpm build(73/73)Tests 1 failed | 6 passed (7)— same assertion, verbatimorigin/main8d4690b8fpnpm --filter '@objectstack/verify^...' buildonlyTests 7 passed (7)The first control I ran was the third row, and it was invalid — the narrower build leaves
packages/plugins/organizations/distabsent, which is itself the discriminator. Recorded because it briefly read as "this PR broke it".The mechanism:
turbo.json'stesttask declaresdependsOn: ["^build"], and@objectstack/organizationsis in neither thedependenciesnor thedevDependenciesof@objectstack/verify— it is the dynamically resolved subject of the test. So a full workspace build gives the fixture a resolvable copy the test's "declared, not installed" premise assumes cannot exist, and boot walks past package resolution into the next fatal. Filed as its own card — #17025, unassigned, carrying the repro; ⛔ not fixed here, and out of this card's scope.The docs /
next/sharpsurface, whichpnpm buildexcludes by designRun with the command production runs, read from
apps/docs/vercel.json:next16.3.3 andsharp0.35.4 build and render all 1221 pages. (The log carriesFailed to load dynamic font … self-signed certificate in certificate chainfor two CJK strings — the sandbox's egress proxy refusing a font fetch, not a build error; the build exits 0 and the pages render.)The build also leaves the tree clean —
git status --porcelainis empty afterwards, sogen:schemaregeneratedauthorable-surface.base.jsonbyte-identically, as it must when no spec source moved.Gate union — derived from a fresh tree, reconciled with
--ranEvery one of the 86 exited 0. No exit 3
PREREQUISITE NOT METanywhere — the full build ran first, so the build-dependent families (check:dts-closure,check:lean-entry-closure,check:type-source-resolution,check:dual-build-cjs-loads,check:driver-conformance,check:vendor-export-contract-resolve) measured rather than skipped.One honesty note the tool itself raised and this PR closed rather than declared: the reconciliation warned
STALE TREE — 1 file(s) it derives from CHANGED, namingscripts/check-adr-0087-registration.mjs. That family was re-run fromorigin/main's current copy of the script (fetched to a temp probe beside the original, run, then removed —git statusclean afterwards):--self-test338 assertions ✓,--base origin/main✓this PR adds no declared-breaking changeset. Both exit 0.The gates most load-bearing for this diff specifically:
Both censuses are byte-for-byte the lists the pre-PR CI run printed, so neither lift introduced a self-expiring selector or an idle override.
The
vitestbump cannot move suite counts — read from the published diff, not assumedThe card's warning is the right one to take seriously, so the 4.1.10 → 4.1.11 diff was unpacked from the registry and read rather than reasoned about. Outside version strings and the chunk-hash renames they cause, the entire change in both flagged packages is this:
vitestadds one function,maybeCollectChromiumGarbage, called on browser-session teardown. It returns immediately unlessprovider.name === 'playwright'andconfig.browser.name === 'chromium'and the platform is linux andconfig.browser.isolateis set.@vitest/mocker— this is the advisory itself (GHSA-82fw-gwwq-j7x9). Itsvitest:interceptor:registerdev-server WebSocket event accepted a redirect path that could escape the Vite root; 4.1.11 gates it throughisFileLoadingAllowedand adds aregisterWebSocketEventsopt-out.Both live entirely in browser mode. This repo configures none:
@vitest/browseris declared in no manifest (grep exit 1, captured before any pipe), and the onlybrowserstring in any vitest config is a comment inexamples/app-showcase/vitest.config.tspointing at the separate Playwrighte2e/suite. Nothing in the changed code is reachable frompnpm test, so collection and reporting are byte-identical by construction — and the full run below confirms it empirically rather than resting on that.Where each bumped package is actually exercised
nodemailer@objectstack/plugin-email(transports/smtp.ts)sys-email-payload.wire.test.tsdrives real nodemailer against an in-process fake SMTP server and byte-compares the DATA payload the server received — a MIME-encoding change between 9.0.5 and 9.1.1 could not pass it silentlyhono@objectstack/plugin-hono-serverand@objectstack/hono@objectstack/http-conformancepackage, whose conformance suite includes an integration testsharpnextas its image dependency; this repo imports it nowhere (grep exit 1)nextapps/docssite, excluded frompnpm buildby design (turbo run build --filter=!@objectstack/docs)vitest/@vitest/coverage-v8/@vitest/mockerChangeset grade, and why
patch, naming@objectstack/plugin-emailand@objectstack/plugin-hono-server.Those two are the only published packages whose manifests change something a downstream install reads: a declared
dependenciesrange. Everything else in the diff releases nothing —apps/docsisprivate: true, so itsnextpin ships nowhere;pnpm-workspace.yamloverrides apply only inside this workspace and are never published (that is the whole reason the two declared ranges move in lockstep with thehonooverride);vitest/@vitest/coverage-v8moves aredevDependencies, which no consumer installs.patchrather thanminorbecause the rule in theCheck Changesetstep's prose grades on the ACT: aminoris owed for a purely additive widening of a published package's public surface, and a dependency range is not that surface. No exported symbol, accepted key or accepted value moves.skip-changesetwould have been wrong in the other direction — two published manifests really do change, so this PR is not "releases nothing".Two peer ranges deliberately left alone
Both are the same shape, and
pnpm-workspace.yaml's own header states the rule: "a peer states what host hono we work against, and a host that pins an old hono owns that copy; narrowing it fixes nothing here and only breaks compatibility."@objectstack/honokeepspeerDependencies.hono: ^4.12.8.@objectstack/metadata-corekeeps its optionalpeerDependencies.vitest: ^4.0.0.Neither produces a resolution of its own, so neither can put a flagged version in the lockfile.
Acceptance notes — observed, not filed, not fixed here
pnpm-workspace.yaml's@hono/node-servernote says the resolved version is "unchanged at 2.0.12". The tree now resolves two copies, 2.0.12 and 2.1.1, because@objectstack/plugin-hono-serverdeclares^2.1.1while the override target is^2.0.10.check:override-consistencyis green (the ranges intersect) and neither copy is flagged, so this is a stale comment rather than a defect. Both re-peered onto the singlehono@4.13.7in this PR.pnpm installreportsioredis-mock 8.13.1 → unmet peer ioredis@^5: found 6.0.0. Verified pre-existing and untouched:origin/mainand this branch resolveioredis@6.0.0andioredis-mock@8.13.1identically. pnpm only prints it on a run that re-resolves, which is why the sweep surfaced it and a plain install does not.next16.3.4 andhono4.13.6/4.13.7 exist above the fix lines.nextwas deliberately held at the named fix 16.3.3;honofloats to 4.13.7 because an OSV override target must be a floating range inside the major, per this file's mandated selector shape.Generated by Claude Code