Skip to content

chore(deps): refresh dependencies and base image, drop dead deps - #33

Merged
BatLeDev merged 8 commits into
masterfrom
chore-deps-refresh
Sep 8, 2026
Merged

BatLeDev merged 8 commits into
masterfrom
chore-deps-refresh

Conversation

@BatLeDev

@BatLeDev BatLeDev commented Sep 8, 2026 •

Copy link
Copy Markdown
Member

Refresh every dependency in the monorepo: clear the open advisories, modernise the base image, drop dead declarations, and take the majors that can be proven behaviour-neutral.
Why: routine dependency maintenance — the tree had drifted, npm audit reported 11 advisories (7 high) and the released image carried 60 HIGH/CRITICAL findings.

Security

1.2.1 after
npm audit (full tree) 11 0
npm audit --omit=dev 10 0
Trivy HIGH/CRITICAL on image 60 (2 CRITICAL) 6 (0 CRITICAL)

Every npm advisory was fixable within the existing semver ranges — no manifest range needed widening. On the image, the alpine layer goes 21 → 2 (openssl only, no CRITICAL) and shipped app dependencies 11 → 0.

What changed

  • In-range update clears all 11 advisories. tar-stream@3.2.1 now ships its own types, which shadow @types/tar-stream and rename Headers → Header; the upgrade script derives the type from Pack['entry'] instead. Those types also pull in streamx, whose Writable.end is declared end(data: unknown) although the runtime treats a missing argument as "just finish" (streamx/index.js:170) and tar-stream itself calls sink.end() with none — passing explicit undefined is equivalent.
  • Base image node:24.11.1-alpine3.22 → 24.20.0-alpine3.24, a one-line change. It clears the alpine layer on its own: musl 1.2.5-r10 → 1.2.6-r2, zlib 1.3.1-r2 → 1.3.2-r0, openssl 3.5.4-r0 → 3.5.7-r0 (the two remaining HIGH; 3.5.8-r0 will arrive with the next node image refresh). The bundled npm under /usr/local/lib/node_modules accounts for the other 4 findings and would be the obvious thing to delete, but it must stay: lib-node-registry spawns npm rebuild (index.js:195, reached from the plugin download at index.js:125).
  • Dead and phantom deps — dropped @types/tar-stream and ui's semver/@types/semver; declared ofetch, imported by ui/src/context.ts but resolving only through hoisting; removed easymde from optimizeDeps (a vjsf 3 leftover, absent from the tree entirely).
  • Majors — config 5, nanoid 6, types-catalogs 0.7 (api/worker); vueuse 14, unplugin-auto-import 21, unplugin-vue-components 32, types-catalogs 0.7 (ui).

How the majors were verified

  • config 4 → 5: both consumers mutate the config object (rawConfig.tmpDir = ...), so resolved values aren't enough. Compared both workspaces × dev/prod × with and without env overrides: identical trees, Object.keys, JSON round-trip, isFrozen/isSealed, and mutation stickiness. Then boot-tested inside the alpine image.
  • nanoid 3 → 6: 200k ids per version — same 21-char default length, same alphabet, no collisions.
  • unplugin majors: only emitted code matters. route-map.d.ts byte-identical, components identical (17), auto-imports.d.ts gains exactly two vue-preset symbols. Nothing removed or remapped.
  • vueuse 14 dedupes ui/lib-vue/lib-vuetify onto one copy; lib-vuetify 2.4.3 already required ^14.

Declined, with the blocker

  • mongodb 6 → 7 — @data-fair/lib-node declares peerDependencies.mongodb: "6", and 2.13.3 still does. Blocked upstream.
  • croner 9 → 10 — changes DST fall-back resolution: it picks the first occurrence of an ambiguous repeated local hour where croner 9 picked the second (11/144 in a dedicated DST probe, spring-forward untouched). An affected import fires an hour earlier in UTC, once a year. No CVE or feature driver, so not worth shifting users' schedules. Note croner 10 also rejects numeric-prefix stepping (0/5, 2/3), but the scheduling schema only defines hourStep alongside hour: { const: '*' }, so the only form emitted is */N, which it accepts — the N/step branches in toCRON() are dead code.

Unrelated fix carried in this branch

fix(tests): select the login inputs by name attribute is not a dependency change. simple-directory's login page gained a password visibility toggle whose aria-label also matches "Mot de passe", so getByLabel('Mot de passe') resolves to two elements and every e2e login fails in strict mode. Nothing here caused it — CI pulls simple-directory:master fresh. The fixture now selects on the name attribute, which is what events and agents already do. The same breakage is latent in data-fair and processings, which still use getByLabel there.

Clears every open advisory reported by `npm audit`: 11 in the full tree
(7 high, 2 moderate, 2 low) and 10 in the production tree, down to 0 in
both. No manifest range needed widening — all fixes were already in
range, notably nanoid 3.3.15 -> 3.3.18, postcss, axios, fast-uri,
js-yaml, immutable, qs, brace-expansion and body-parser.

tar-stream 3.2.1 started shipping its own type definitions, which take
precedence over @types/tar-stream and renamed the entry header
interface (`Headers` -> `Header`, with the partial form kept internal as
`HeaderArgument`). The upgrade script now derives the header type from
`Pack['entry']` so it tracks whichever name the package uses.

The same bundled types pull in streamx, whose `Writable.end` is declared
as `end(data: unknown)` although the runtime treats a missing argument as
"just finish" (streamx index.js:170) and tar-stream itself calls
`sink.end()` with no argument. Passing an explicit `undefined` keeps the
behaviour identical and satisfies the declaration.
node:24.11.1-alpine3.22 -> node:24.20.0-alpine3.24, and a runtime stage
that runs `apk upgrade --no-cache` so alpine releases published after the
node image are picked up when the image is built.

Trivy on ghcr.io/data-fair/catalogs:1.2.1 reported 60 HIGH/CRITICAL: 21
from the alpine layer (libcrypto3/libssl3 3.5.4-r0 with CVE-2026-31789
CRITICAL, musl, zlib) and 39 from node packages. The new base cuts the
alpine layer to libcrypto3/libssl3 3.5.7-r0, and `apk upgrade` takes
those to 3.5.8-r0.

Only the two final runtime stages derive from the patched stage; the
build stages keep using `base` so the extra apk round-trip does not run
four more times per build.

The bundled npm under /usr/local/lib/node_modules accounts for most of
the remaining node-package findings and is the obvious thing to delete,
but it has to stay: @data-fair/lib-node-registry spawns `npm rebuild`
(index.js:195, reached from the plugin download path at index.js:125) to
build native addons of plugins installed at runtime.
… entry

- worker: @types/tar-stream is inert since tar-stream 3.2.1 ships its own
  definitions, which win over the @types fallback under NodeNext.
- ui: semver and @types/semver had no reference anywhere in the workspace.
- ui: ofetch was imported by src/context.ts but never declared — it only
  resolved through the hoisted copy that lib-vue and lib-vuetify pull in
  as a peer. Declared explicitly so it stops depending on hoisting.
- ui: 'easymde' in optimizeDeps.include is a leftover from vjsf 3. vjsf 4
  has no reference to it and the package is absent from the tree, so vite
  was being asked to prebundle something that does not exist.
…d worker

config 4.4.2 -> 5.0.1. Both consumers import the default export, hand it
to assertValid and then mutate it (`rawConfig.tmpDir = ...`), so the
resolved values are not the only thing that matters. Compared 4.4.2 and
5.0.1 side by side over api/config and worker/config, in development and
production, with and without the environment overrides from
custom-environment-variables.mjs: the resolved trees are identical, and
so are Object.keys, the JSON round-trip, isFrozen/isSealed and whether
the tmpDir assignment sticks.

nanoid 3.3.18 -> 6.0.1. Over 200k ids per version: same 21-character
default length, same URL-safe alphabet, no collisions, and an explicit
size argument still honoured. nanoid has been ESM-only since 4 and both
workspaces are already "type": "module".

@data-fair/types-catalogs ^0.6.0 -> ^0.7.1 in api, which aligns it with
worker and with the local ./types-catalogs the root package.json
resolves through relativeDependencies — that checkout is already 0.7.1,
so api and ui were the odd ones out.
….7 in ui

@vueuse/core ^13.0.0 -> ^14.4.0. lib-vuetify 2.4.3 already requires
^14.0.0, so the workspace was pulling a second copy purely to satisfy
its own older range; ui, lib-vue, lib-vuetify and unplugin-auto-import
now all dedupe onto 14.4.0. vjsf keeps its own 12.8.2. lib-vue's peer
range is ">=10", so it is satisfied either way.

unplugin-auto-import 19.3.0 -> 21.1.0 and unplugin-vue-components
28.8.0 -> 32.1.0. Both only generate code at build time, so the check
that matters is what they emit. Diffing dts/ before and after: the
component list is identical (17), route-map.d.ts is byte-identical, and
auto-imports.d.ts gains exactly two entries — getCurrentWatcher and
isShallow, both from vue's own preset. Nothing was removed or remapped;
the rest of the file diff is a formatting change from bracket to dot
access plus new lint-disable headers.

@data-fair/types-catalogs ^0.6.0 -> ^0.7.1, matching api and worker.
It landed between @mdi/js and @vitejs/plugin-vue when it was declared,
which breaks the scoped-then-unscoped ordering the rest of the file
follows. No effect on resolution — the lockfile is unchanged.
simple-directory's login page gained a password visibility toggle whose
aria-label also matches "Mot de passe", so `getByLabel('Mot de passe')`
now resolves to two elements and the e2e login fails in strict mode.
Nothing in this repo changed — CI pulls simple-directory:master fresh,
so it broke on its own.

Selecting on the `name` attribute is what events and agents already do,
and it is indifferent to labels, aria-labels and added controls. The
same breakage is latent in data-fair and processings, which still use
getByLabel here.

The submit button is left on getByRole: it is unambiguous and this repo
runs the directory in French, unlike the English-locale fixtures in
events and agents.
Patching alpine at build time makes an image depend on the day it was
built rather than on its commit, which no other project here does. Not
worth being the odd one out for it.

Only the base image bump remains, and it carries the security result on
its own: 6 HIGH and 0 CRITICAL on the built image, against 60 HIGH and
2 CRITICAL on the released 1.2.1. The difference with the stage removed
is two findings — libcrypto3 and libssl3 stay at 3.5.7-r0 instead of
3.5.8-r0, which resolves itself whenever the node base image is
refreshed again.
@BatLeDev
BatLeDev merged commit 0c40d73 into master Sep 8, 2026
3 checks passed
@BatLeDev
BatLeDev deleted the chore-deps-refresh branch September 8, 2026 10:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant