From 8e098ae93e5af59dd6251561972c26ef4c10c90a Mon Sep 17 00:00:00 2001 From: Christopher Bartz Date: Fri, 9 Oct 2026 11:31:28 +0000 Subject: [PATCH 1/5] fix(release): strip unused npm and corepack from bundled node The runner only executes externals/node*/bin/node. The npm and corepack shipped with Node 20 and 24 are never used, but they pin vulnerable dependencies (tar, brace-expansion, minimatch, undici, ...) that account for all High/Critical secscan findings in the release tarballs (ISD-6336). Strip them after layout in the fork-only release workflow, so no upstream source file changes and upstream merges stay conflict-free. --- .github/workflows/release.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4f4fedb007..2088e893af7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -108,6 +108,35 @@ jobs: ./dev.sh layout Release ${{ matrix.runtime }} working-directory: src + # Fork-only: the runner only executes externals/node*/bin/node. The npm and corepack bundled + # with Node are unused and pin vulnerable dependencies (tar, minimatch, undici, ...) that would + # otherwise ship in every release. Kept here rather than in src/ so upstream merges stay clean. + - name: Strip bundled npm and corepack + env: + RUNTIME: ${{ matrix.runtime }} + run: | + shopt -s nullglob + nodes=(_layout/externals/node*/) + if [ ${#nodes[@]} -eq 0 ]; then + echo "::error::No bundled Node found in _layout/externals" + exit 1 + fi + for d in "${nodes[@]}"; do + rm -rf "${d}lib/node_modules/npm" "${d}lib/node_modules/corepack" \ + "${d}bin/npm" "${d}bin/npx" "${d}bin/corepack" + done + if find _layout/externals \( -path '*/node_modules/npm' -o -path '*/node_modules/corepack' \) | grep -q .; then + echo "::error::npm or corepack is still bundled in _layout/externals" + exit 1 + fi + # linux-arm (armv7l) binaries cannot run on the arm64 build host; the container test covers it. + if [ "$RUNTIME" != linux-arm ]; then + for node in _layout/externals/node[0-9]*/bin/node; do + case "$node" in *_alpine/*) continue ;; esac + echo "$node $("$node" --version)" + done + fi + # Create runner package tar.gz/zip - name: Package Release if: github.event_name != 'pull_request' From fd4c0be58869f18fe6c93b76e25001f98414aa03 Mon Sep 17 00:00:00 2001 From: Christopher Bartz Date: Fri, 9 Oct 2026 11:38:36 +0000 Subject: [PATCH 2/5] ci(build): strip bundled npm in PR builds via a shared script release.yml only runs on release branches, so the strip and its checks never ran for pull requests. Move the logic to .github/scripts/strip-bundled-npm.sh and call it from both build.yml (PR CI, all 5 architectures) and release.yml. --- .github/scripts/strip-bundled-npm.sh | 32 ++++++++++++++++++++++++++++ .github/workflows/build.yml | 4 ++++ .github/workflows/release.yml | 29 ++----------------------- 3 files changed, 38 insertions(+), 27 deletions(-) create mode 100755 .github/scripts/strip-bundled-npm.sh diff --git a/.github/scripts/strip-bundled-npm.sh b/.github/scripts/strip-bundled-npm.sh new file mode 100755 index 00000000000..c9420a541aa --- /dev/null +++ b/.github/scripts/strip-bundled-npm.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# Fork-only: remove the npm and corepack bundled with Node from a runner layout. +# The runner only executes externals/node*/bin/node; npm and corepack are unused and pin +# vulnerable dependencies (tar, minimatch, undici, ...) that would otherwise ship in every +# package. Lives outside src/ so upstream merges stay clean. +# +# Usage: strip-bundled-npm.sh +set -euo pipefail +layout="$1" +runtime="$2" + +shopt -s nullglob +nodes=("$layout"/externals/node*/) +if [ ${#nodes[@]} -eq 0 ]; then + echo "::error::No bundled Node found in $layout/externals" + exit 1 +fi +for d in "${nodes[@]}"; do + rm -rf "${d}lib/node_modules/npm" "${d}lib/node_modules/corepack" \ + "${d}bin/npm" "${d}bin/npx" "${d}bin/corepack" +done +if find "$layout/externals" \( -path '*/node_modules/npm' -o -path '*/node_modules/corepack' \) | grep -q .; then + echo "::error::npm or corepack is still bundled in $layout/externals" + exit 1 +fi +# linux-arm (armv7l) binaries cannot run on the arm64 build host; the container test covers it. +if [ "$runtime" != linux-arm ]; then + for node in "$layout"/externals/node[0-9]*/bin/node; do + case "$node" in *_alpine/*) continue ;; esac + echo "$node $("$node" --version)" + done +fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5c76e0c89f9..41a357425a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,6 +70,10 @@ jobs: ./dev.sh layout Release ${{ matrix.runtime }} working-directory: src + # Fork-only: see .github/scripts/strip-bundled-npm.sh. + - name: Strip bundled npm and corepack + run: .github/scripts/strip-bundled-npm.sh _layout ${{ matrix.runtime }} + # Run tests - name: L0 run: | diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2088e893af7..6f8293d56fb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -108,34 +108,9 @@ jobs: ./dev.sh layout Release ${{ matrix.runtime }} working-directory: src - # Fork-only: the runner only executes externals/node*/bin/node. The npm and corepack bundled - # with Node are unused and pin vulnerable dependencies (tar, minimatch, undici, ...) that would - # otherwise ship in every release. Kept here rather than in src/ so upstream merges stay clean. + # Fork-only: see .github/scripts/strip-bundled-npm.sh. - name: Strip bundled npm and corepack - env: - RUNTIME: ${{ matrix.runtime }} - run: | - shopt -s nullglob - nodes=(_layout/externals/node*/) - if [ ${#nodes[@]} -eq 0 ]; then - echo "::error::No bundled Node found in _layout/externals" - exit 1 - fi - for d in "${nodes[@]}"; do - rm -rf "${d}lib/node_modules/npm" "${d}lib/node_modules/corepack" \ - "${d}bin/npm" "${d}bin/npx" "${d}bin/corepack" - done - if find _layout/externals \( -path '*/node_modules/npm' -o -path '*/node_modules/corepack' \) | grep -q .; then - echo "::error::npm or corepack is still bundled in _layout/externals" - exit 1 - fi - # linux-arm (armv7l) binaries cannot run on the arm64 build host; the container test covers it. - if [ "$RUNTIME" != linux-arm ]; then - for node in _layout/externals/node[0-9]*/bin/node; do - case "$node" in *_alpine/*) continue ;; esac - echo "$node $("$node" --version)" - done - fi + run: .github/scripts/strip-bundled-npm.sh _layout ${{ matrix.runtime }} # Create runner package tar.gz/zip - name: Package Release From 9c33adea4cf2fffbdec5d7083ea53e438f2ae3c5 Mon Sep 17 00:00:00 2001 From: Christopher Bartz Date: Fri, 9 Oct 2026 11:45:15 +0000 Subject: [PATCH 3/5] fix(ci): avoid pipefail false negative in bundled npm check find | grep -q under pipefail can fail with SIGPIPE after a match and skip the error. Use find -print -quit instead, and also check the npm/npx/corepack launchers in bin/. --- .github/scripts/strip-bundled-npm.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/scripts/strip-bundled-npm.sh b/.github/scripts/strip-bundled-npm.sh index c9420a541aa..ca500f8b7b6 100755 --- a/.github/scripts/strip-bundled-npm.sh +++ b/.github/scripts/strip-bundled-npm.sh @@ -19,8 +19,10 @@ for d in "${nodes[@]}"; do rm -rf "${d}lib/node_modules/npm" "${d}lib/node_modules/corepack" \ "${d}bin/npm" "${d}bin/npx" "${d}bin/corepack" done -if find "$layout/externals" \( -path '*/node_modules/npm' -o -path '*/node_modules/corepack' \) | grep -q .; then - echo "::error::npm or corepack is still bundled in $layout/externals" +leftover="$(find "$layout/externals" \( -path '*/node_modules/npm' -o -path '*/node_modules/corepack' \ + -o -path '*/bin/npm' -o -path '*/bin/npx' -o -path '*/bin/corepack' \) -print -quit)" +if [ -n "$leftover" ]; then + echo "::error::npm or corepack is still bundled in $layout/externals: $leftover" exit 1 fi # linux-arm (armv7l) binaries cannot run on the arm64 build host; the container test covers it. From 6c0c2dec84df7b203f72af8475fa8aa0bcdd83b5 Mon Sep 17 00:00:00 2001 From: Christopher Bartz Date: Fri, 9 Oct 2026 11:55:53 +0000 Subject: [PATCH 4/5] fix(ci): fail when a bundled node binary is missing The version check globbed bin/node and silently skipped Node directories without one. Require bin/node in every bundled Node, including alpine and linux-arm where it cannot be executed on the build host. --- .github/scripts/strip-bundled-npm.sh | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/scripts/strip-bundled-npm.sh b/.github/scripts/strip-bundled-npm.sh index ca500f8b7b6..b4d5f0d903b 100755 --- a/.github/scripts/strip-bundled-npm.sh +++ b/.github/scripts/strip-bundled-npm.sh @@ -25,10 +25,15 @@ if [ -n "$leftover" ]; then echo "::error::npm or corepack is still bundled in $layout/externals: $leftover" exit 1 fi -# linux-arm (armv7l) binaries cannot run on the arm64 build host; the container test covers it. -if [ "$runtime" != linux-arm ]; then - for node in "$layout"/externals/node[0-9]*/bin/node; do - case "$node" in *_alpine/*) continue ;; esac - echo "$node $("$node" --version)" - done -fi +for d in "${nodes[@]}"; do + if [ ! -x "${d}bin/node" ]; then + echo "::error::${d}bin/node is missing" + exit 1 + fi + # Alpine builds need musl, and linux-arm (armv7l) cannot run on the arm64 build host; + # the package tests cover those. + case "$d" in *_alpine/) continue ;; esac + if [ "$runtime" != linux-arm ]; then + echo "${d}bin/node $("${d}bin/node" --version)" + fi +done From 17ac084e2cd805363fb630eac43f7e3f2c21b41a Mon Sep 17 00:00:00 2001 From: Christopher Bartz Date: Fri, 9 Oct 2026 12:41:59 +0000 Subject: [PATCH 5/5] fix(ci): fail when bundled node cannot report its version --- .github/scripts/strip-bundled-npm.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/scripts/strip-bundled-npm.sh b/.github/scripts/strip-bundled-npm.sh index b4d5f0d903b..cd3981b9bc9 100755 --- a/.github/scripts/strip-bundled-npm.sh +++ b/.github/scripts/strip-bundled-npm.sh @@ -34,6 +34,7 @@ for d in "${nodes[@]}"; do # the package tests cover those. case "$d" in *_alpine/) continue ;; esac if [ "$runtime" != linux-arm ]; then - echo "${d}bin/node $("${d}bin/node" --version)" + version="$("${d}bin/node" --version)" + echo "${d}bin/node $version" fi done