From 75a8129e3b1a80e643a728855e387190ec98b95c Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sat, 16 May 2026 06:51:22 +0000 Subject: [PATCH] ci: tighten install-path comments Collapse the worked-example narrative to one-line WHYs. Code is unchanged. --- .github/workflows/release-desktop.yml | 14 ++++---------- .gitignore | 5 +---- build.sh | 11 +++-------- studio/setup.ps1 | 12 +++--------- studio/setup.sh | 14 +++----------- 5 files changed, 14 insertions(+), 42 deletions(-) diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 7eb3da4095..a378a2c245 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -443,16 +443,10 @@ jobs: - name: Install frontend dependencies working-directory: studio/frontend - # `npm ci` so a release build can never pull a fresh minor/patch - # of a transitive dep from the registry via caret-range resolution; - # the tree is exactly what the committed lockfile pins. - # - # Lifecycle scripts (esbuild native-binary postinstall, etc.) are - # required for `vite build`. The pre-install lockfile structural - # audit (lockfile_supply_chain_audit.py) is the practical defence - # against the npm postinstall-dropper class -- it fires BEFORE any - # tarball runs, on the injection pattern itself rather than an - # advisory-DB lookup. + # npm ci: release build installs exactly what the lockfile pins. + # Lifecycle scripts (esbuild native-binary postinstall) are required + # for vite build; lockfile_supply_chain_audit.py runs pre-install as + # the defence against the postinstall-dropper class. run: npm ci --no-fund --no-audit # ── Rust ── diff --git a/.gitignore b/.gitignore index 659c4bc0e3..505fb9562b 100644 --- a/.gitignore +++ b/.gitignore @@ -229,10 +229,7 @@ log.txt setup_leo.sh server.pid *.log -# Default-ignore stray package-lock.json files that npm creates in random -# Python subtrees during development. Lockfiles for the real npm projects -# inside this repo are committed explicitly via the negative patterns below -# (npm ci in setup.sh / setup.ps1 / release-desktop.yml depends on them). +# Ignore stray lockfiles; real npm projects opt back in below (npm ci needs them). package-lock.json !studio/frontend/package-lock.json !studio/backend/core/data_recipe/oxc-validator/package-lock.json diff --git a/build.sh b/build.sh index 0f8b605d4d..43c06bf3b8 100644 --- a/build.sh +++ b/build.sh @@ -33,14 +33,9 @@ _restore_gitignores() { } trap _restore_gitignores EXIT -# Use bun for install if available (faster), fall back to npm. -# Both paths use lockfile-strict mode so a release build cannot silently -# pull a newer minor/patch of any transitive dep from the registry. Naked -# `bun install` / `npm install` honour caret ranges in package.json and -# will fetch new compatible versions if available, which is the standard -# vector for supply-chain attacks that compromise a sub-dep at a patch -# release. `--frozen-lockfile` / `npm ci` install only what the lockfile -# pins and abort on any drift. +# Use bun if available (faster), fall back to npm. Lockfile-strict on +# both paths so a release build can't pull a fresh caret-range patch +# of any transitive from the registry. _install_ok=false if command -v bun &>/dev/null; then if bun install --frozen-lockfile; then diff --git a/studio/setup.ps1 b/studio/setup.ps1 index 095c767c4d..dc7aa0e1c2 100644 --- a/studio/setup.ps1 +++ b/studio/setup.ps1 @@ -1333,10 +1333,7 @@ if ($NeedFrontendBuild -and -not $IsPipInstall) { # metadata but no actual content (bin/, lib/). When this happens bun install # exits 0 but leaves binaries missing. We validate after install and clear # the cache + retry once before falling back to npm. - # - # --frozen-lockfile so a Windows end-user install can never pull a fresh - # minor/patch of a transitive dep from the registry via caret-range - # resolution; the tree is exactly what the committed lockfile pins. + # --frozen-lockfile so a fresh caret-range patch can't land via npm registry. if ($UseBun) { Write-Host " Using bun for package install (faster)" -ForegroundColor DarkGray $bunExit = Invoke-SetupCommand { bun install --frozen-lockfile } @@ -1372,9 +1369,7 @@ if ($NeedFrontendBuild -and -not $IsPipInstall) { } } if (-not $UseBun) { - # npm ci (not npm install) so the install is pinned to the committed - # lockfile -- a hijacked transitive cannot land via caret-range - # resolution. Fails fast if package.json and the lockfile have drifted. + # npm ci: install exactly what the lockfile pins, fail on drift. $npmExit = Invoke-SetupCommand { npm ci } if ($npmExit -ne 0) { Pop-Location @@ -1418,8 +1413,7 @@ if (Test-Path $OxcValidatorDir) { $prevEAP_oxc = $ErrorActionPreference $ErrorActionPreference = "Continue" Push-Location $OxcValidatorDir - # npm ci pins the oxc validator install to its committed lockfile so a - # hijacked transitive cannot land via caret-range resolution. + # npm ci: lockfile-strict (see frontend install above). $oxcInstallExit = Invoke-SetupCommand { npm ci } if ($oxcInstallExit -ne 0) { Pop-Location diff --git a/studio/setup.sh b/studio/setup.sh index 6d71b68319..0c4a040bb5 100755 --- a/studio/setup.sh +++ b/studio/setup.sh @@ -343,11 +343,7 @@ trap _restore_gitignores EXIT _try_bun_install() { local _log _exit_code=0 _log=$(mktemp) - # --frozen-lockfile prevents the installer from auto-pulling newer - # transitive versions when a caret range in package.json would allow - # it. Combined with the npm-ci fallback below, this keeps the - # release-build dep set identical to what the committed lockfile - # captures. + # --frozen-lockfile so a fresh caret-range patch can't land via npm registry. bun install --frozen-lockfile >"$_log" 2>&1 || _exit_code=$? # bun may create .exe shims on Windows (Git Bash / MSYS2) instead of plain scripts @@ -386,10 +382,7 @@ if command -v bun &>/dev/null; then fi fi if [ "$_bun_install_ok" = false ]; then - # npm ci (not npm install) -- strictly install what package-lock.json - # pins, so an attacker who hijacks a minor/patch of a transitive dep - # cannot have it pulled into a release build via caret-range - # resolution. Fails fast if package.json and the lockfile have drifted. + # npm ci: install exactly what the lockfile pins, fail on drift. run_quiet_no_exit "npm ci" npm ci --no-fund --no-audit --loglevel=error _npm_install_rc=$? if [ "$_npm_install_rc" -ne 0 ]; then @@ -417,8 +410,7 @@ fi # end frontend build check # ── oxc-validator runtime ── if [ -d "$SCRIPT_DIR/backend/core/data_recipe/oxc-validator" ] && command -v npm &>/dev/null; then cd "$SCRIPT_DIR/backend/core/data_recipe/oxc-validator" - # npm ci pins the oxc validator install to its committed lockfile so a - # hijacked transitive cannot land via caret-range resolution. + # npm ci: lockfile-strict (see frontend install above). run_quiet_no_exit "npm ci (oxc validator runtime)" npm ci --no-fund --no-audit --loglevel=error _oxc_install_rc=$? if [ "$_oxc_install_rc" -ne 0 ]; then