chore(docker): clear most CVEs in published image (npm/pnpm/uuid + drop curl)#7674
chore(docker): clear most CVEs in published image (npm/pnpm/uuid + drop curl)#7674SamTV12345 merged 12 commits intodevelopfrom
Conversation
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one. |
Review Summary by Qodo(Agentic_describe updated until commit 5a8561f)Reduce published Docker image CVEs via pnpm/corepack and curl removal
WalkthroughsDescription• Reduce Docker image CVEs from 18 to 12 by removing npm and curl • Provision pnpm via corepack instead of npm to eliminate bundled vulnerabilities • Bump pnpm version 10.28.2 → 10.33.2 and add uuid override • Fix HEALTHCHECK by switching from curl to wget and localhost to 127.0.0.1 Diagramflowchart LR
A["Node 22 Alpine<br/>npm@10.9.7"] -->|"Remove bundled npm"| B["Corepack<br/>pnpm@10.33.2"]
C["npm transitives<br/>picomatch/brace-expansion<br/>CVE-2026-33671/33672/33750"] -->|"Eliminated"| D["4 CVEs cleared"]
E["curl runtime dep<br/>nghttp2 CVE-2026-27135"] -->|"Replace with wget"| F["1 CVE cleared"]
G["uuid@8.3.2<br/>GHSA-w5hq-g745-h8pq"] -->|"Override to 14.0.0"| H["1 CVE cleared"]
I["HEALTHCHECK localhost<br/>IPv6 resolution issue"] -->|"Switch to 127.0.0.1"| J["Connection refused fixed"]
D --> K["18 CVEs → 12 CVEs<br/>8 packages → 3 packages"]
F --> K
H --> K
File Changes1. Dockerfile
|
Code Review by Qodo
1.
|
|
Qodo review responses: 1. Corepack pnpm prepare failure (Bug): fixed in 5a8561f — 2. 3. |
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one. |
|
Persistent review updated to latest commit 5a8561f |
|
Re-checking the rule-violation findings after pushing 5a8561f — Qodo's reply was paused at the free-tier limit, but I went back through them and added the missing mitigation: #2 (curl) and #3 (npm/npx): documented in |
…rop curl Cuts published-image vulnerabilities from 18 (4H/13M/1L) across 8 packages to 12 (2H/9M/1L) across 3 packages. The remaining three (curl/libcurl, git, busybox) are all upstream Alpine 3.23 packages with "not fixed" status — libcurl is pulled in transitively by git and cannot be removed independently. Changes: - Provision pnpm via corepack instead of `npm install -g pnpm`, then remove the bundled npm. The base image's npm@10.9.7 ships old transitives (picomatch 4.0.3 → CVE-2026-33671/33672, brace-expansion 2.0.2 → CVE-2026-33750) that we don't otherwise need at runtime; corepack handles pnpm directly without npm. Fixes 1H + 1M. - Bump PnpmVersion 10.28.2 → 10.33.2 to align with the rest of the workflow and pull in pnpm's patched bundled brace-expansion (5.0.5 vs 5.0.4). Fixes 1M. - Add `uuid@<14.0.0` → `>=14.0.0` to pnpm.overrides (GHSA-w5hq-g745-h8pq). Fixes 1M. - Drop `curl` from the runtime apk add list and switch HEALTHCHECK to wget (busybox built-in). curl was only invoked by the healthcheck and by dev/CI scripts that don't run in the container. Removes the curl CLI binary; libcurl remains as a git transitive dep, so the `apk/alpine/curl` advisories scout reports against libcurl persist but aren't reachable from any code we ship. As a side-effect this also clears nghttp2 (CVE-2026-27135) which was a curl-CLI dep. - Switch HEALTHCHECK URL from `localhost` to `127.0.0.1` — alpine/musl resolves localhost to ::1 first and Etherpad only binds IPv4. Verified locally: docker build → docker run → healthy → docker scout cves shows 12 CVEs / 3 packages.
Node 22's bundled corepack ships a stale signing-key list and can reject newer pnpm releases (nodejs/corepack#612), which would fail the image build at `corepack prepare`. Mirror the snap/snapcraft.yaml workaround: `npm install -g corepack@latest` before activating pnpm, in both adminbuild and build stages. npm is still removed afterwards.
Address Qodo's "backwards-incompatible change without mitigation" rule violations by documenting the removal in the 2.7.3 breaking-changes section. Operators who exec into the container can apk add curl on demand or use the busybox wget / pnpm already present.
7de2986 to
6794cc5
Compare
…7689) * fix(docker): share corepack cache so etherpad user can resolve pnpm (#7687) PR #7674 switched the Dockerfile from `npm install -g pnpm` to corepack and `corepack prepare pnpm@${PnpmVersion} --activate`. The activate step runs as root and writes its lastKnownGood pin into `$COREPACK_HOME`, which defaults to `~/.cache/node/corepack` — i.e. a per-user path. The Dockerfile then drops to `USER etherpad` and later runs `bin/installLocalPlugins.sh`, which invokes `pnpm` as etherpad. With an empty per-user corepack cache and no shared activation file, corepack re-resolves pnpm and (for forks/configs without a `packageManager` pin matching the activated version) can fall back to "latest" from the registry — pulling `pnpm@10.33.4` instead of the requested 11.x and failing the workspace's `engines.pnpm` check. Pin `COREPACK_HOME=/opt/corepack` and chown it to etherpad after the prepare step. Both root and etherpad now share the same lastKnownGood file and tarball cache, so etherpad inherits the activated pnpm without hitting the registry again. Verified end-to-end: - `docker build --target development --build-arg ETHERPAD_LOCAL_PLUGINS=ep_test` with a stub local plugin runs `installLocalPlugins.sh` cleanly: `Done in 16.6s using pnpm v11.0.6`. - `docker run ... pnpm --version` as etherpad reports 11.0.6 from the shared cache — no "Unsupported environment" error. Note: corepack still emits a one-time "about to download" line at runtime because `corepack prepare pnpm@11.0.6` resolves to the highest matching patch (11.0.8) at build time while the project's `packageManager` field pins exactly 11.0.6. That's a follow-up — the download succeeds non-interactively and the engine check passes. Fixes #7687. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(docker): action Qodo PR review (#7687 follow-up) - Replace hard-coded /opt/corepack with ${COREPACK_HOME} in mkdir/chown so the env var stays the single source of truth (Qodo: "COREPACK_HOME path duplication"). - Add a build-test-local-plugin job to .github/workflows/docker.yml that builds the development target with a stub ETHERPAD_LOCAL_PLUGINS so the original failure mode (corepack/pnpm cache invisible across the USER switch) cannot silently regress (Qodo: "COREPACK_HOME fix lacks test"). The job is small — `docker build` only, no run — and uses the shared GHA buildx cache. Verified: same docker build + `docker run pnpm --version` flow on the variable form gives identical output (pnpm 11.0.6 from the etherpad-owned cache). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Cuts published-image vulnerabilities reported by docker scout from 18 (0C/4H/13M/1L) across 8 packages → 12 (0C/2H/9M/1L) across 3 packages, locally verified against
etherpad/etherpad:2.7.2.The three remaining flagged packages —
curl/libcurl,git,busybox— are all upstream Alpine 3.23 packages currently marked not fixed.libcurlis a transitive dep ofgitand can't be removed independently; thecurladvisories scout still attributes to it are SMB-protocol use-after-frees that are unreachable from any code we ship.Changes
/usr/local/lib/node_modules/npmplus its CLI shims. Clears 1 high + 1 medium.PnpmVersion10.28.2 → 10.33.2. Aligns with the workflow'spnpm/action-setuppinning and brings pnpm's bundled brace-expansion to 5.0.5 (was 5.0.4 / CVE-2026-33750). Clears 1 medium.uuid@<14.0.0→>=14.0.0override. Fixes GHSA-w5hq-g745-h8pq. Lockfile resolves to uuid@14.0.0. Clears 1 medium.curlfrom runtime apk add and switch HEALTHCHECK to wget. curl was only invoked by the HEALTHCHECK and a couple of dev/CI scripts that don't run in the container. wget (busybox built-in) replaces it. Side-effect: clears nghttp2 (CVE-2026-27135 high), which was a curl-CLI runtime dep.localhost→127.0.0.1. Alpine/musl resolveslocalhostto::1first and Etherpad only binds IPv4 — was producing `Connection refused` until the IPv4 fallback timed out.Test plan
🤖 Generated with Claude Code