From d1124c52a9040f9af28a5ae3e35302c398f85e03 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sat, 16 May 2026 09:21:33 +0000 Subject: [PATCH] ci: address bot review on Two issues codex flagged: 1. bun.lock gate (studio/frontend/.gitignore line 14 ignores bun.lock, so it is never committed). bun install --frozen-lockfile cannot migrate from package-lock.json, so without a bun.lock the bun path always fails. setup.sh then misclassifies that as a corrupt cache, clears the user's bun cache, and re-runs the same guaranteed-failing command before falling back to npm. build.sh, studio/setup.sh, and studio/setup.ps1 now only enter the bun path when bun.lock is present; otherwise we go straight to npm ci. 2. OXC validator lockfile was outside the npm supply-chain scan surface. lockfile_supply_chain_audit.py default, npm audit, OSV, scan_npm_packages.py invocation, and the diff-for-new-install-scripts step all now cover both lockfiles. security-audit.yml pull_request paths filter triggers on changes to either. wheel-smoke checks the built wheel ships the OXC lockfile too. Verified: python3 scripts/lockfile_supply_chain_audit.py > OK: 0 findings across 2 npm + 1 cargo lockfile(s) python3 scripts/scan_npm_packages.py --lockfile oxc-validator/... > OK --- .github/workflows/security-audit.yml | 46 ++++++++++++++++++++++++-- .github/workflows/wheel-smoke.yml | 1 + build.sh | 8 ++--- scripts/lockfile_supply_chain_audit.py | 8 +++-- studio/setup.ps1 | 6 ++-- studio/setup.sh | 6 +++- 6 files changed, 63 insertions(+), 12 deletions(-) diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml index a1e7b2efa6..3f9a3610bb 100644 --- a/.github/workflows/security-audit.yml +++ b/.github/workflows/security-audit.yml @@ -53,6 +53,8 @@ on: - 'studio/backend/requirements/**' - 'studio/frontend/package.json' - 'studio/frontend/package-lock.json' + - 'studio/backend/core/data_recipe/oxc-validator/package.json' + - 'studio/backend/core/data_recipe/oxc-validator/package-lock.json' - 'studio/src-tauri/Cargo.toml' - 'studio/src-tauri/Cargo.lock' - 'pyproject.toml' @@ -278,7 +280,7 @@ jobs: { echo "## Lockfile supply-chain audit" echo - echo "Scanned: studio/frontend/package-lock.json + studio/src-tauri/Cargo.lock" + echo "Scanned: studio/frontend/package-lock.json + studio/backend/core/data_recipe/oxc-validator/package-lock.json + studio/src-tauri/Cargo.lock" echo echo "No structural anomalies or known IOC strings." } >> "$GITHUB_STEP_SUMMARY" @@ -307,6 +309,22 @@ jobs: echo '```' } >> "$GITHUB_STEP_SUMMARY" + - name: npm audit (oxc-validator runtime) + # Same audit surface, separate npm project (oxc-parser, oxlint). + continue-on-error: true + working-directory: studio/backend/core/data_recipe/oxc-validator + run: | + set +e + npm audit --audit-level=high | tee "$GITHUB_WORKSPACE/logs-npm-audit-oxc.txt" + npm audit --json > "$GITHUB_WORKSPACE/logs-npm-audit-oxc.json" || true + { + echo "## npm audit (oxc-validator)" + echo + echo '```' + tail -200 "$GITHUB_WORKSPACE/logs-npm-audit-oxc.txt" + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + # ───────────────────────────────────────────────────────────── # cargo: Studio Tauri shell # ───────────────────────────────────────────────────────────── @@ -348,6 +366,7 @@ jobs: /tmp/osv-scanner --version /tmp/osv-scanner scan source \ --lockfile=studio/frontend/package-lock.json \ + --lockfile=studio/backend/core/data_recipe/oxc-validator/package-lock.json \ --lockfile=studio/src-tauri/Cargo.lock \ --lockfile=requirements.txt:audit-reqs/unsloth-deps.txt \ --lockfile=requirements.txt:audit-reqs/studio.txt \ @@ -913,24 +932,39 @@ jobs: # downloaded tarball, and only fetches from registry.npmjs.org. # Initially non-blocking so the baseline can settle; drop # continue-on-error once the baseline is clean for a week. + # + # Two separate npm projects share this scan surface; scan each. run: | set -o pipefail LOG=logs-scan-npm.txt python3 scripts/scan_npm_packages.py 2>&1 | tee "$LOG" + LOG2=logs-scan-npm-oxc.txt + python3 scripts/scan_npm_packages.py \ + --lockfile studio/backend/core/data_recipe/oxc-validator/package-lock.json \ + 2>&1 | tee "$LOG2" { - echo "## scan_npm_packages" + echo "## scan_npm_packages (Studio frontend)" echo echo '### Findings (tail)' echo '```' tail -300 "$LOG" echo '```' + echo + echo "## scan_npm_packages (oxc-validator)" + echo + echo '### Findings (tail)' + echo '```' + tail -300 "$LOG2" + echo '```' } >> "$GITHUB_STEP_SUMMARY" - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: always() with: name: scan-npm-packages-log - path: logs-scan-npm.txt + path: | + logs-scan-npm.txt + logs-scan-npm-oxc.txt retention-days: 30 # ───────────────────────────────────────────────────────────────────── @@ -1103,6 +1137,9 @@ jobs: BASE_SHA="${{ github.event.pull_request.base.sha }}" git show "$BASE_SHA:studio/frontend/package-lock.json" \ > /tmp/base-package-lock.json + # OXC lockfile may not exist on the base ref (newly added). + git show "$BASE_SHA:studio/backend/core/data_recipe/oxc-validator/package-lock.json" \ + > /tmp/base-oxc-package-lock.json 2>/dev/null || echo '{}' > /tmp/base-oxc-package-lock.json - name: Diff for newly-added install-script deps if: github.event_name == 'pull_request' @@ -1110,6 +1147,9 @@ jobs: python3 scripts/check_new_install_scripts.py \ --base /tmp/base-package-lock.json \ --head studio/frontend/package-lock.json + python3 scripts/check_new_install_scripts.py \ + --base /tmp/base-oxc-package-lock.json \ + --head studio/backend/core/data_recipe/oxc-validator/package-lock.json - name: Skip install-script diff (non-PR trigger) if: github.event_name != 'pull_request' diff --git a/.github/workflows/wheel-smoke.yml b/.github/workflows/wheel-smoke.yml index 3de3c33ca2..1ebd09da72 100644 --- a/.github/workflows/wheel-smoke.yml +++ b/.github/workflows/wheel-smoke.yml @@ -87,6 +87,7 @@ jobs: n = z.namelist() checks = { "lockfile shipped": any(s.endswith("studio/frontend/package-lock.json") for s in n), + "oxc lockfile shipped": any(s.endswith("oxc-validator/package-lock.json") for s in n), "frontend dist shipped": any(s.endswith("studio/frontend/dist/index.html") for s in n), "no node_modules": not any("studio/frontend/node_modules/" in s for s in n), "no bun.lock": not any(s.endswith("studio/frontend/bun.lock") for s in n), diff --git a/build.sh b/build.sh index 43c06bf3b8..de69f13d7c 100644 --- a/build.sh +++ b/build.sh @@ -33,11 +33,11 @@ _restore_gitignores() { } trap _restore_gitignores EXIT -# 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. +# Use bun if a bun.lock is committed (lockfile-strict, fast), else npm ci. +# bun install --frozen-lockfile cannot migrate from package-lock.json, so if +# only the npm lockfile is present we skip bun and go straight to npm ci. _install_ok=false -if command -v bun &>/dev/null; then +if [ -f bun.lock ] && command -v bun &>/dev/null; then if bun install --frozen-lockfile; then _install_ok=true else diff --git a/scripts/lockfile_supply_chain_audit.py b/scripts/lockfile_supply_chain_audit.py index ae215bf344..478be4a27e 100644 --- a/scripts/lockfile_supply_chain_audit.py +++ b/scripts/lockfile_supply_chain_audit.py @@ -652,7 +652,10 @@ def audit_cargo_lockfile(path: Path) -> list[Finding]: # ───────────────────────────────────────────────────────────────────── -DEFAULT_NPM_LOCKFILES = ("studio/frontend/package-lock.json",) +DEFAULT_NPM_LOCKFILES = ( + "studio/frontend/package-lock.json", + "studio/backend/core/data_recipe/oxc-validator/package-lock.json", +) DEFAULT_CARGO_LOCKFILES = ("studio/src-tauri/Cargo.lock",) @@ -671,7 +674,8 @@ def main(argv: list[str] | None = None) -> int: default = None, help = ( "Path to a package-lock.json (repeatable). " - "Default: studio/frontend/package-lock.json." + "Default: studio/frontend/package-lock.json plus " + "studio/backend/core/data_recipe/oxc-validator/package-lock.json." ), ) parser.add_argument( diff --git a/studio/setup.ps1 b/studio/setup.ps1 index dc7aa0e1c2..5aefcea216 100644 --- a/studio/setup.ps1 +++ b/studio/setup.ps1 @@ -1327,13 +1327,15 @@ if ($NeedFrontendBuild -and -not $IsPipInstall) { $ErrorActionPreference = "Continue" Push-Location $FrontendDir - $UseBun = $null -ne (Get-Command bun -ErrorAction SilentlyContinue) + # Only use bun when a committed bun.lock is present. bun install + # --frozen-lockfile cannot migrate from package-lock.json, so without + # bun.lock the bun path would always fail. + $UseBun = ($null -ne (Get-Command bun -ErrorAction SilentlyContinue)) -and (Test-Path "bun.lock") # bun's package cache can become corrupt -- packages get stored with only # 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 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 } diff --git a/studio/setup.sh b/studio/setup.sh index 0c4a040bb5..f4b5bea076 100755 --- a/studio/setup.sh +++ b/studio/setup.sh @@ -367,7 +367,11 @@ _try_bun_install() { } _bun_install_ok=false -if command -v bun &>/dev/null; then +# bun install --frozen-lockfile cannot migrate from package-lock.json, so we +# only enter the bun path when a committed bun.lock exists. Without it, +# bun would fail every time and the corrupt-cache retry would clear the +# user's bun cache for nothing. +if [ -f bun.lock ] && command -v bun &>/dev/null; then substep "using bun for package install (faster)" if _try_bun_install; then _bun_install_ok=true