Repository navigation
fix(deps): clear high-severity audit baseline - #50
Conversation
#44 and #45 landed the same class of floors first, so most of this branch is now already on main. Measured against main 85adee7 rather than assumed: of the 13 overrides this branch introduced, 12 are satisfied by what is there — brace-expansion 1.1.18/5.0.9, fast-uri 3.1.5, form-data 4.0.6, js-yaml 3.15.1/4.3.1, multer 2.3.0, nanoid 3.3.18, postcss 8.5.26, sharp 0.35.4, socket.io-parser 4.2.7, ws 8.21.0 — and `next` is already 15.5.21. The one floor main does NOT satisfy is deepmerge-ts: @prisma/config 7.8.0 pins 7.1.5 and the advisory is fixed only in 8.0.0. That is what this merge carries forward, in main's own union file, alongside this branch's apps/web/package.json axios declaration. Resolution: main's override set is taken whole and the deepmerge-ts floor is ADDED to it. This branch's narrower re-statements of the other twelve are dropped as duplicates, not as disagreements — none of them would have changed a resolved version. No hunk that carries something main lacks is discarded. pnpm-lock.yaml is main's lockfile re-resolved by pnpm 11.5.2 against the merged file, not a hand merge. Verified before taking the override, because it crosses a major boundary inside a dependency Prisma pins: deepmerge-ts resolves 8.0.2, `prisma generate` rc 0 (Prisma Client v7.8.0), `prisma validate` rc 0, pnpm lint rc 0, pnpm build rc 0 (3/3 tasks). Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01RFqkBcMs3UH6LP3avgi9Q8
…pmerge-ts tree pnpm-lock.yaml is a binding.files entry of the MUN-0022 evidence, so forcing deepmerge-ts to 8.0.2 invalidates the record by design. Full campaign against the merged tree, not a rebind: node v22.23.2 / pnpm 11.5.2 / jest 29.7.0 / typescript 5.9.3 baseline 394/394 tests, 20/20 suites 85 sites: 76 killed by test, 9 killed by the compiler, 0 survived Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01RFqkBcMs3UH6LP3avgi9Q8
DEC-AUP-0008 / AUP-GRAPH-006. Bound to 85adee7..d968284. 305 verified, 151 not_measured, 0 failed; every non-verified entity carries an exemption with an owner and a 2026-09-23 expiry -> admitted_with_exemptions. 130 of the 151 are not_measured only because v-canary is unconditional for inferred boundaries and no canary exists before the deploy. 9 are work items (I14), 8 live outside every tsconfig include and are unchanged here, 4 are historical receipts, one of which (#82's) this change archives out of the gate's discovery globs — the convention #82, #45 and #77 each followed. The notes record what this change does NOT do: it closes deepmerge-ts and nothing else, and next >=15.5.24 (critical), fast-uri, js-yaml, browserslist and mysql2 advisories remain open on main. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01RFqkBcMs3UH6LP3avgi9Q8
…t carries the receipt Adding the receipt and archiving #82's changed the tracked tree, and trackedTreeWithoutEvidence hashes all of it. Same campaign, same toolchain (node v22.23.2 / pnpm 11.5.2): 85 sites, 76 killed by test, 9 killed by the compiler, 0 survived; --verify-structure exits 0 on this tree. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01RFqkBcMs3UH6LP3avgi9Q8
Re-measured against
|
…h-admission discovery globs CI's graph-admission check refused this PR with a code unrelated to this change: change-admission-arca0209-deepmerge-20260909T171700Z.json (landed by #50, ARCA-0209's deepmerge work) has a receipt head that is not an ancestor of THIS PR's range, and the check discovers it anyway because ci_gate.py globs receipts/graph/**/*.json and receipts/**/change-admission-*.json unconditionally -- so it would poison every PR against main until archived, this one included. Same defect class as commit 1006f41 (the SEC-0052 receipt), same fix: move out of both globs (drop the change-admission- prefix, move out of receipts/graph/) into receipts/archive/, which is outside both. Regenerates apps/api/test/assembly/mutation-results.json once more: the rename changes the tracked tree, so trackedTreeWithoutEvidence must reflect it (0 survived / 76 killed-by-test / 9 killed-by-typecheck across the same 85 sites; --verify-structure rc 0). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01H4ywto9ubXNtjT5aLUt3Fr
Rebasing onto main (post #80's Node-pin reconciliation) moved the tracked tree, so the checked-in evidence's git-supplement binding went stale — same pattern #44/#45/#50/#80/#82 each hit. Regenerated: 0 survived / 76 killed-by-test / 9 killed-by-typecheck across the same 85 sites; --verify-structure rc 0. No source changes, evidence only. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY
* chore(deps): bump next from 15.5.21 to 15.5.24 Bumps [next](https://github.com/vercel/next.js) from 15.5.21 to 15.5.24. - [Release notes](https://github.com/vercel/next.js/releases) - [Commits](vercel/next.js@v15.5.21...v15.5.24) --- updated-dependencies: - dependency-name: next dependency-version: 15.5.24 dependency-type: direct:production ... Signed-off-by: dependabot[bot] <[email protected]> * test: regenerate assembly mutation evidence for the rebased tree Rebasing onto main (post #80's Node-pin reconciliation) moved the tracked tree, so the checked-in evidence's git-supplement binding went stale — same pattern #44/#45/#50/#80/#82 each hit. Regenerated: 0 survived / 76 killed-by-test / 9 killed-by-typecheck across the same 85 sites; --verify-structure rc 0. No source changes, evidence only. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * test: regenerate assembly mutation evidence for the reconciled merge tree Reconciles a concurrent-push collision on this branch (another session also regenerated evidence for the same rebase and pushed first) -- the merge of the two updates left the git-supplement binding stale again, so a third, fresh regen against the actual merged tree. 0 survived / 76 killed-by-test / 9 killed-by-typecheck across the same 85 sites; --verify-structure rc 0. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * receipt(next-critical-rce): file the ChangeAdmissionReceipt/v1 for this change (DEC-AUP-0008) Bound to c048c13..397a538 (the substantive commit, PR #79 next 15.5.21-> 15.5.24): global_fallback triggered on pnpm-lock.yaml, 457-entity impact set, all verified by the single whole-repository verifier appropriate to a dependency patch bump -- PR #79's own CI lint-and-test job on the real fleet runner (run 34396752144, job 102618256943, exit 0), not a local claim. Closes GHSA-2xp9-vwfh-vxw4 and GHSA-p293-qw3h-jr36 (both critical, unauthenticated RCE), first_patched_version 15.5.24 exactly matched. admission.verdict: admitted. Not eligible for the automated-author exemption despite dependabot[bot] authorship: the evidence-rebind commits touch apps/api/test/assembly/mutation-results.json, outside the dependency- manifest allowlist, so this is an agent-authored receipt like #80's/#83's. change_set includes the receipt's own path (kind=receipt, status=A, new=true) -- learned from #83's CHANGE_SET_INCOMPLETE round-trip: impact.py's skeleton predates the receipt file, so it never lists itself. Also regenerates apps/api/test/assembly/mutation-results.json for the third time on this branch: the harness's gitSupplement() seeds its git-supplement binding from `git write-tree` on the REAL INDEX, so the receipt had to be `git add`-ed before this run, not after -- learned the hard way on #83's first attempt. 0 survived / 76 killed-by-test / 9 killed-by-typecheck across the same 85 sites; --verify-structure rc 0. Locally confirmed with `admit_change.py gate --receipt ...`. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * chore(receipts): archive #80's stale MUN-node-pin receipt out of the discovery globs Same fix as muneral#83: CI's real graph-admission run (discovery mode) was refusing this PR with RECEIPT_NOT_BOUND_TO_RANGE for #80's own receipt, merged but never archived (36c7f46 archived an earlier one; this one was missed). Renamed off the change-admission- prefix to escape both discovery globs, added the rename itself as a change_set entry (kind=receipt, status=R) in this PR's own receipt. Regenerates evidence for the same tracked-tree reason as every commit on this branch: 0 survived / 76 killed-by-test / 9 killed-by-typecheck, --verify-structure rc 0. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Arcanada <[email protected]> Co-authored-by: Arcanada <[email protected]> Co-authored-by: Claude Sonnet 5 <[email protected]>
… js-yaml, mysql2, qs, hono, baseline-browser-mapping) (#83) * fix(deps): raise 7 unmatched security floors — fast-uri, browserslist, js-yaml, mysql2, qs, hono, baseline-browser-mapping 71 open muneral dependabot alerts closed to 21 by earlier PRs (#44/#45/#82/#80); these 7 packages had no dependabot PR covering their advisories at all: - fast-uri: prior floor ^3.1.5 resolved to exactly 3.1.5, which a newer advisory (GHSA-jqff-g426-hqxp / GHSA-f65p-4m7j-42xc / GHSA-fph4-wmhf-6fwf / GHSA-5jgf-p345-68v8, patched 3.1.6) outran — a real defect in the prior fix, not a gap. Raised to ^3.1.6, resolves to 3.1.7. - hono: same shape, ^4.12.34 -> ^4.13.5 (GHSA-crvj-82cr-hjcx / GHSA-gqvv-2mrq-wpjv). - js-yaml: ^3.15.1/^4.3.1 -> ^3.15.2/^4.3.2 (GHSA-2883-xcg3-v3hh, both major lines). - browserslist, baseline-browser-mapping, mysql2, qs: no override existed; added floors matching each advisory's first_patched_version (GHSA-73wf-gq98-2v4g/GHSA-c83g-rgw3-j3cx, GHSA-w5vr-8v7q-w6rv, GHSA-3f6p-5ww8-9rcr, GHSA-x5fp-wj9c-mxmx/GHSA-4mjr-xmp4-gh2g). Regenerates apps/api/test/assembly/mutation-results.json: the lockfile change moves the tracked tree, so the evidence document's git-supplement binding goes stale the same way it did for #44/#45/#50/#80/#82. 0 survived / 76 killed-by-test / 9 killed-by-typecheck across the same 85 sites; --verify-structure rc 0. apps/web and apps/api build clean; full non-e2e unit suite green (1280/1283, only the expected evidence-staleness failures before this regen); e2e suites need a live Postgres this shell doesn't have — deferred to CI's own service container, consistent with how this session has treated CI as the authority throughout this task. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * receipt(sec-floors-round2): file the ChangeAdmissionReceipt/v1 for this change (DEC-AUP-0008) Bound to c048c13..65b45b0 (the substantive commit): global_fallback triggered on pnpm-lock.yaml/pnpm-workspace.yaml, 458-entity impact set, all verified by the single whole-repository verifier appropriate to a dependency-floor change -- PR #83's own CI lint-and-test job on the real fleet runner (run 34395035927, job 102612514884, exit 0), not a local claim. admission.verdict: admitted. Confirmed via `admit_change.py gate --receipt ...`: ADMIT. Regenerates apps/api/test/assembly/mutation-results.json once more: the receipt itself is a newly tracked file, so trackedTreeWithoutEvidence must reflect its presence too (0 survived / 76 killed-by-test / 9 killed-by- typecheck across the same 85 sites; --verify-structure rc 0). Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * fix: correct the evidence binding — regenerate after the receipt was staged, not before The prior commit ran mutation-harness.js while the receipt file was still untracked on disk: gitSupplement()'s git write-tree seeds from the real index, so an unstaged file is invisible to it, and the binding it recorded matched the tree WITHOUT the receipt rather than the tree the commit actually produced. CI caught it (real lint-and-test failure, not a flake). Regenerated again with nothing else changed: 0 survived / 76 killed-by-test / 9 killed-by-typecheck across the same 85 sites, --verify-structure rc 0. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * fix: add the receipt's own path to its change_set — CHANGE_SET_INCOMPLETE impact.py's skeleton is computed before the receipt file exists, so it never lists the receipt itself as an added file. graph-admission caught it for real: "1/4 changed file(s) are absent from the receipt change_set". Added the receipt's own entry (kind=receipt, status=A, new=true), matching #80's own receipt's pattern. Locally re-verified with `admit_change.py gate --receipt ...`: ADMIT, c048c13..b66c46e, files=4. Regenerates the evidence once more for the same reason as before (the receipt content changed, moving the tracked tree): 0 survived / 76 killed-by-test / 9 killed-by-typecheck, --verify-structure rc 0. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * chore(receipts): archive #80's stale MUN-node-pin receipt out of the discovery globs CI's real graph-admission run (discovery mode, no explicit --receipt) was refusing this PR with RECEIPT_NOT_BOUND_TO_RANGE for change-admission-MUN-node-pin-20260909T175500Z.json -- #80's own receipt, merged days^H^Hhours ago, never archived afterward the way #77's and the ARCA-0209 one were (36c7f46). It sat in receipts/graph/, matched by both discovery globs, and its unrelated unbound-ness poisoned every subsequent PR's overall verdict even when that PR's own receipt was valid -- confirmed locally: `gate --receipt <mine>` (explicit, bypasses discovery) said ADMIT all along, while `gate` with no --receipt (discovery, what CI actually runs) said refused, for exactly this reason. Renamed off the change-admission- prefix (not just moved) so it also escapes receipts/**/change-admission-*.json, the second glob. Locally reconfirmed discovery-mode ADMIT after the move. Regenerates the evidence once more for the same tracked-tree reason as every other commit on this branch: 0 survived / 76 killed-by-test / 9 killed-by-typecheck, --verify-structure rc 0. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * fix: add the archive-rename to the receipt's change_set — CHANGE_SET_INCOMPLETE The prior archival commit's receipt still only listed its original 4 paths; the rename itself (receipts/graph/change-admission-MUN-node-pin-...json -> receipts/archive/node-pin-...json) is a 5th changed path in the range that the receipt didn't cover. Added it (kind=receipt, status=R). Regenerated evidence for the same tracked-tree reason as every commit on this branch: 0 survived / 76 killed-by-test / 9 killed-by-typecheck, --verify-structure rc 0. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * fix: correct the truncated commit sha in the receipt's change_set.head Wrote a 12-char abbreviation (4cef876) instead of the full 40-char sha -- schema_check.py didn't catch it (no format constraint on that field), but the range-membership check does exact/canonical comparison, not prefix resolution: RECEIPT_MISSING, 0/7 candidates bound. Full sha now. Regenerates evidence once more for the same reason as every commit on this branch: 0 survived / 76 killed-by-test / 9 killed-by-typecheck, --verify-structure rc 0. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY * chore(receipts): archive #79's own receipt too — same discovery-poisoning pattern #79 (critical next.js RCE) merged into main via squash, and its receipt (change-admission-next-critical-rce-...json) was never archived -- same oversight as #80's. Squash-merging also disconnects the receipt's declared head (397a538) from main's real ancestry, so even a same-day archival attempt referencing it correctly would still read as unbound; archiving it out of the discovery globs sidesteps that entirely, same as the MUN-node-pin fix earlier on this branch. Updated this branch's own receipt: dropped the now-irrelevant receipts/archive/node-pin-... entry from change_set (that rename is no longer part of the diff against a shifted base) and added this new rename. Kept change_set.base at c048c13 -- the commit the vendored graph was actually built against (graph.source_commit / graph_digest must match it; changing base to CI's now-later computed base without rebuilding the graph trips RECEIPT_ON_STALE_GRAPH). The range-membership check only requires receipt.head to be an ancestor of the current HEAD, not that declared base equals whatever CI happens to compute. Regenerates evidence once more: 0 survived / 76 killed-by-test / 9 killed-by-typecheck, --verify-structure rc 0. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NFNpX4JV82FtEi993EiUFY --------- Co-authored-by: Claude Sonnet 5 <[email protected]>
…ESM move dropped Dependabot #103 proposes node 24.20.0 -> 26.8-alpine. Measured against nodejs.org/dist/index.json: the 24.x line is the CURRENT LTS (Krypton, v24.21.0 released 2026-09-07) and every 26.x release carries `lts: false` — 26 is Current, not LTS. Moving production off a supported line is not a dependency bump, so this takes the security patch WITHIN the LTS line instead: 24.21.0 everywhere. There is a concrete reason to take it. v24.21.0 fixes an out-of-bounds write in `mkdtemp` for long prefixes (nodejs/node#64770), and this repository calls `fs.mkdtempSync` in three places, one of them the evidence path itself (mutation-harness.js gitSupplement). Five pins move together, because regenerate-derived-artefacts.yml REFUSES a mismatch between ci.yml and .github/workflows/.node-version-for-derived: apps/api/Dockerfile (x2), ci.yml, that pin file, and the root engines field. THE DEFENCE THAT WAS LOST. Two defects, both introduced by the ESM move in #113 and both invisible until a full mutation run was attempted: 1. The harness spawns jest directly and did not pass --experimental-vm-modules, so under ESM every battery failed with `SyntaxError: Cannot use import statement outside a module` and the run stopped at `baseline: FAIL`. Measured: 20 suites / 394 tests pass with the flag, 20 suites fail with 0 tests without it. `pnpm test` sets the flag; the harness never did. It went unnoticed because the only caller of a full run is regenerate-derived-artefacts.yml, gated on author == 'dependabot[bot]' — it never ran for #113. NODE_OPTIONS is appended, not assigned, so an inherited flag survives. 2. `isolatedModules: true` on the ts-jest transform removes type-checking at transform time, and nine of the 85 mutants were KILLED_BY_TYPECHECK. Measured with the flag: 79 killed by test, 6 SURVIVED — #11, #15, #16, #50, #52, #75, each a mutant the compiler used to reject and the tests do not catch. Three of the nine (#6, #13, #21) turned out to be caught by tests after all; six were not. A mutation baseline that records 0 survivors while the tree actually has 6 is worse than no baseline. The flag is removed. Its cost, measured: the local suite goes from 26s to 58s, and the CI job from about 3 minutes to an expected 3.5-4. Six mutants of the canonical-JSON and validator paths are worth thirty seconds. Measured after, from the repository root (the invocation the evidence records): 85 sites, 76 killed by test, 9 killed by compiler, 0 survived — identical to the counts on main. `--verify-structure` prints "Structurally verified 85 source-bound mutation records" with no TOOLCHAIN_MISMATCH, because tools now read node v24.21.0. tsc --noEmit 0 under both tsconfig.json and tsconfig.test.json. Closes #103 by answering it: the upgrade it asks for is refused with a measurement and the security patch it was reaching for is taken.
…ESM move dropped (#114) * fix: stay on the Node LTS line, and restore the mutation defence the ESM move dropped Dependabot #103 proposes node 24.20.0 -> 26.8-alpine. Measured against nodejs.org/dist/index.json: the 24.x line is the CURRENT LTS (Krypton, v24.21.0 released 2026-09-07) and every 26.x release carries `lts: false` — 26 is Current, not LTS. Moving production off a supported line is not a dependency bump, so this takes the security patch WITHIN the LTS line instead: 24.21.0 everywhere. There is a concrete reason to take it. v24.21.0 fixes an out-of-bounds write in `mkdtemp` for long prefixes (nodejs/node#64770), and this repository calls `fs.mkdtempSync` in three places, one of them the evidence path itself (mutation-harness.js gitSupplement). Five pins move together, because regenerate-derived-artefacts.yml REFUSES a mismatch between ci.yml and .github/workflows/.node-version-for-derived: apps/api/Dockerfile (x2), ci.yml, that pin file, and the root engines field. THE DEFENCE THAT WAS LOST. Two defects, both introduced by the ESM move in #113 and both invisible until a full mutation run was attempted: 1. The harness spawns jest directly and did not pass --experimental-vm-modules, so under ESM every battery failed with `SyntaxError: Cannot use import statement outside a module` and the run stopped at `baseline: FAIL`. Measured: 20 suites / 394 tests pass with the flag, 20 suites fail with 0 tests without it. `pnpm test` sets the flag; the harness never did. It went unnoticed because the only caller of a full run is regenerate-derived-artefacts.yml, gated on author == 'dependabot[bot]' — it never ran for #113. NODE_OPTIONS is appended, not assigned, so an inherited flag survives. 2. `isolatedModules: true` on the ts-jest transform removes type-checking at transform time, and nine of the 85 mutants were KILLED_BY_TYPECHECK. Measured with the flag: 79 killed by test, 6 SURVIVED — #11, #15, #16, #50, #52, #75, each a mutant the compiler used to reject and the tests do not catch. Three of the nine (#6, #13, #21) turned out to be caught by tests after all; six were not. A mutation baseline that records 0 survivors while the tree actually has 6 is worse than no baseline. The flag is removed. Its cost, measured: the local suite goes from 26s to 58s, and the CI job from about 3 minutes to an expected 3.5-4. Six mutants of the canonical-JSON and validator paths are worth thirty seconds. Measured after, from the repository root (the invocation the evidence records): 85 sites, 76 killed by test, 9 killed by compiler, 0 survived — identical to the counts on main. `--verify-structure` prints "Structurally verified 85 source-bound mutation records" with no TOOLCHAIN_MISMATCH, because tools now read node v24.21.0. tsc --noEmit 0 under both tsconfig.json and tsconfig.test.json. Closes #103 by answering it: the upgrade it asks for is refused with a measurement and the security patch it was reaching for is taken. * receipt(#103): file the ChangeAdmissionReceipt/v1 for the Node LTS pin Draft by the bundle's own verify.py in diff mode: verdict admitted, 3 entities, all three verified, zero exemptions — nothing needed exempting, which is the first receipt in this series that can say so. One finding came out of it and was fixed rather than exempted. The first draft reported v-config-schema UNDECLARED_CONFIG_KEY NODE_OPTIONS as failed, because the harness fix read process.env.NODE_OPTIONS to append to an inherited value. The verifier was right: reading it makes it a configuration key this file consumes, and declaring a test-harness runtime flag in .env.example — the application's own configuration — would have been the worse answer. The flag is now set outright, and regenerate-derived-artefacts.yml (the only caller of a full run) sets no NODE_OPTIONS, so nothing is lost. Filed in the tree with the receipt included in its own change_set, and the evidence's trackedTreeWithoutEvidence rebound after staging it — the MUN-0049 precedent. schema_check.py: CONFORMANT.
Summary
ARCA-0209 task-owned baseline dependency repair. No product source, workflow, deploy, or infrastructure changes.
^1.8.4→^1.18.015.5.19→15.5.21Verification
pnpm audit --audit-level high: 27 high advisories on main → 0 high advisories