Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
53c0d8c
ci: pin install to lockfile in build.sh and studio/setup.sh
danielhanchen May 16, 2026
883f26a
ci: pin install to lockfile in remaining install paths
danielhanchen May 16, 2026
2f1d2de
ci: commit oxc-validator lockfile so npm ci has something to install
danielhanchen May 16, 2026
75a8129
ci: tighten install-path comments
danielhanchen May 16, 2026
d1124c5
ci: address bot review on
danielhanchen May 16, 2026
fd5f9be
ci: run OXC tarball scan even when frontend scan exits nonzero
danielhanchen May 16, 2026
cf39fb6
ci: address 4x Opus reviewer findings on frozen-lockfile installs
danielhanchen May 16, 2026
d43b0e3
Merge remote-tracking branch 'origin/main' into
danielhanchen May 16, 2026
b743e1b
ci: extend lockfile-strict surface to Tauri smoke + audit jobs
danielhanchen May 16, 2026
032eb88
ci(lockfile_supply_chain_audit): scope defaults to the no-args case
danielhanchen May 16, 2026
7238504
ci: trim verbose rationale comments
danielhanchen May 16, 2026
c244a9d
Merge branch 'main' into ci/frozen-lockfile-installs
danielhanchen May 17, 2026
888347d
Merge branch 'main' into ci/frozen-lockfile-installs
danielhanchen May 18, 2026
220ae11
pyproject: restore intel-gpu extras dropped in main merge
shimmyshimmer May 18, 2026
1f397f4
audit: wrap lockfile read in try/except OSError
danielhanchen May 19, 2026
35a30f1
Merge remote-tracking branch 'origin/main' into pr5479-fix-audit-unre…
danielhanchen May 19, 2026
dc2292a
bun: commit bun.lock for the three Studio install surfaces
danielhanchen May 19, 2026
9c0372d
bun: restore bun-first / npm-fallback install (both frozen-lockfile)
danielhanchen May 19, 2026
819ed20
audit: scan bun.lock alongside package-lock.json + Cargo.lock
danielhanchen May 19, 2026
ca0d2fd
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] May 19, 2026
2f7cc2d
bun: pin version + auto-install + harden cache-corruption recovery
danielhanchen May 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 17 additions & 14 deletions .github/workflows/release-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -361,14 +361,19 @@ jobs:
with:
node-version: 24

# Pre-install lockfile structural audit. Has to fire BEFORE
# `npm ci` so any tarball's `prepare` / `postinstall` cannot run
# ahead of the scan. Pure-Python read-only; safe everywhere.
- name: Lockfile supply-chain audit (pre-install scan)
shell: bash
run: python3 scripts/lockfile_supply_chain_audit.py

- name: Install pinned Tauri CLI
# 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.
run: npm install --save-dev --prefix studio @tauri-apps/cli@2.10.1 --no-fund --no-audit
# `npm ci` resolves @tauri-apps/cli and its 11 platform-specific
# optional native binaries from studio/package-lock.json --
# transitive versions are fully pinned, integrity hashes are
# verified, and the install is reproducible across re-runs.
run: npm ci --prefix studio --no-fund --no-audit

- name: Verify pinned Tauri CLI
shell: bash
Expand Down Expand Up @@ -443,13 +448,11 @@ jobs:

- name: Install frontend dependencies
working-directory: studio/frontend
# 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.
run: npm install --no-fund --no-audit
# 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 ──
- name: Install Rust stable
Expand Down
157 changes: 147 additions & 10 deletions .github/workflows/security-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ 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'
Comment on lines +56 to +57

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Add the Tauri CLI lockfile to npm audit triggers

This paths block now watches the new OXC npm project but still omits studio/package.json and studio/package-lock.json, even though the desktop release workflow now installs that project with npm ci --prefix studio. A PR that changes only the Tauri CLI holder or its lockfile will not trigger security-audit.yml, so the lockfile structural audit, OSV scan, npm tarball scan, and install-script diff can all be skipped for the newly pinned release-time install surface.

Useful? React with 👍 / 👎.

- 'studio/package.json'
- 'studio/package-lock.json'
- 'studio/src-tauri/Cargo.toml'
- 'studio/src-tauri/Cargo.lock'
- 'pyproject.toml'
Expand Down Expand Up @@ -278,7 +282,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/package-lock.json + studio/src-tauri/Cargo.lock"
echo
echo "No structural anomalies or known IOC strings."
} >> "$GITHUB_STEP_SUMMARY"
Expand Down Expand Up @@ -307,6 +311,39 @@ 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"

- name: npm audit (Studio Tauri CLI holder)
# Same audit surface, third npm project (@tauri-apps/cli for the
# signed desktop release; lockfile lives at studio/package-lock.json).
continue-on-error: true
working-directory: studio
run: |
set +e
npm audit --audit-level=high | tee "$GITHUB_WORKSPACE/logs-npm-audit-studio.txt"
npm audit --json > "$GITHUB_WORKSPACE/logs-npm-audit-studio.json" || true
{
echo "## npm audit (Studio Tauri CLI holder)"
echo
echo '```'
tail -200 "$GITHUB_WORKSPACE/logs-npm-audit-studio.txt"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"

# ─────────────────────────────────────────────────────────────
# cargo: Studio Tauri shell
# ─────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -348,6 +385,8 @@ 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/package-lock.json \
--lockfile=studio/src-tauri/Cargo.lock \
--lockfile=requirements.txt:audit-reqs/unsloth-deps.txt \
--lockfile=requirements.txt:audit-reqs/studio.txt \
Expand Down Expand Up @@ -911,26 +950,71 @@ jobs:
# full log and surface it in the step summary either way. It
# never runs `npm install`, never executes anything from a
# 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.
# This step is blocking: the final exit code is the worst rc
# across all three npm projects (no continue-on-error).
#
# Three separate npm projects share this scan surface; scan each.
# Capture exit codes via PIPESTATUS so a HIGH/CRITICAL on one
# lockfile does not skip the next scan (all reports are most
# useful exactly when one already failed).
run: |
set -o pipefail
set +e

LOG=logs-scan-npm.txt
python3 scripts/scan_npm_packages.py 2>&1 | tee "$LOG"
frontend_rc=${PIPESTATUS[0]}

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"
Comment on lines +967 to +970

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Continue to the OXC scan after frontend findings

GitHub documents default bash run steps as fail-fast (set -e, with -o pipefail for bash), and this step also enables pipefail explicitly. If the frontend tarball scan exits 1/2 for a HIGH finding or scanner error, the step exits at the previous pipeline before reaching this newly added OXC scan, so the OXC lockfile is not scanned in exactly those runs. Capture the first status, run the OXC scan, then fail after both results are collected.

Useful? React with 👍 / 👎.

oxc_rc=${PIPESTATUS[0]}

LOG3=logs-scan-npm-studio.txt
python3 scripts/scan_npm_packages.py \
--lockfile studio/package-lock.json \
2>&1 | tee "$LOG3"
studio_rc=${PIPESTATUS[0]}

{
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 '```'
echo
echo "## scan_npm_packages (Studio Tauri CLI holder)"
echo
echo '### Findings (tail)'
echo '```'
tail -300 "$LOG3"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"

if [ "$frontend_rc" -ne 0 ]; then
exit "$frontend_rc"
fi
if [ "$oxc_rc" -ne 0 ]; then
exit "$oxc_rc"
fi
exit "$studio_rc"

- 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
logs-scan-npm-studio.txt
retention-days: 30

# ─────────────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -1077,7 +1161,7 @@ jobs:
working-directory: studio/frontend
run: npm ci --ignore-scripts

- name: npm audit signatures (informational)
- name: npm audit signatures (Studio frontend, informational)
# Surfaces unsigned / mis-signed packages from the npm
# transparency log. continue-on-error during baseline-build
# phase; promote to hard gate once the lockfile is fully
Expand All @@ -1086,10 +1170,48 @@ jobs:
continue-on-error: true
run: |
set -o pipefail
LOG=logs-audit-signatures.txt
LOG="$GITHUB_WORKSPACE/logs-audit-signatures.txt"
npm audit signatures 2>&1 | tee "$LOG"
{
echo "## npm audit signatures (Studio frontend)"
echo
echo '```'
tail -200 "$LOG"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"

- name: Install oxc-validator deps (--ignore-scripts)
working-directory: studio/backend/core/data_recipe/oxc-validator
run: npm ci --ignore-scripts

- name: npm audit signatures (oxc-validator, informational)
working-directory: studio/backend/core/data_recipe/oxc-validator
continue-on-error: true
run: |
set -o pipefail
LOG="$GITHUB_WORKSPACE/logs-audit-signatures-oxc.txt"
npm audit signatures 2>&1 | tee "$LOG"
{
echo "## npm audit signatures"
echo "## npm audit signatures (oxc-validator)"
echo
echo '```'
tail -200 "$LOG"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"

- name: Install Studio Tauri CLI holder deps (--ignore-scripts)
working-directory: studio
run: npm ci --ignore-scripts

- name: npm audit signatures (Studio Tauri CLI holder, informational)
working-directory: studio
continue-on-error: true
run: |
set -o pipefail
LOG="$GITHUB_WORKSPACE/logs-audit-signatures-studio.txt"
npm audit signatures 2>&1 | tee "$LOG"
{
echo "## npm audit signatures (Studio Tauri CLI holder)"
echo
echo '```'
tail -200 "$LOG"
Expand All @@ -1103,13 +1225,25 @@ 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
# Tauri CLI holder lockfile may not exist on the base ref (newly added).
git show "$BASE_SHA:studio/package-lock.json" \
> /tmp/base-studio-package-lock.json 2>/dev/null || echo '{}' > /tmp/base-studio-package-lock.json

- name: Diff for newly-added install-script deps
if: github.event_name == 'pull_request'
run: |
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
python3 scripts/check_new_install_scripts.py \
--base /tmp/base-studio-package-lock.json \
--head studio/package-lock.json

- name: Skip install-script diff (non-PR trigger)
if: github.event_name != 'pull_request'
Expand All @@ -1121,6 +1255,9 @@ jobs:
if: always()
with:
name: npm-audit-signatures-log
path: studio/frontend/logs-audit-signatures.txt
path: |
logs-audit-signatures.txt
logs-audit-signatures-oxc.txt
logs-audit-signatures-studio.txt
if-no-files-found: ignore
retention-days: 30
27 changes: 11 additions & 16 deletions .github/workflows/studio-tauri-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,32 +60,27 @@ jobs:
with:
workspaces: studio/src-tauri -> target

# Must run BEFORE any `npm ci` so a tarball's prepare/postinstall
# cannot execute ahead of the structural scan.
- name: Lockfile supply-chain audit (pre-install scan)
run: python3 scripts/lockfile_supply_chain_audit.py

- name: Install pinned Tauri CLI (matches release-desktop.yml)
# 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.
run: npm install --save-dev --prefix studio @tauri-apps/cli@2.10.1 --no-fund --no-audit
# `npm ci` resolves @tauri-apps/cli from studio/package-lock.json
# (transitives fully pinned, integrity hashes verified).
run: npm ci --prefix studio --no-fund --no-audit

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include Tauri lockfile changes in smoke trigger

This workflow now installs and verifies the Tauri CLI from studio/package-lock.json, but the pull_request.paths block above still only watches studio/frontend/**, studio/src-tauri/**, and unsloth_cli/**. A PR that changes only studio/package.json or studio/package-lock.json can therefore alter or break the CLI used by this smoke build without running the workflow that would catch it; add those two files to this workflow's path filter.

Useful? React with 👍 / 👎.


- name: Verify pinned Tauri CLI version
run: |
out="$(npx --prefix studio tauri --version)"
echo "$out"
[ "$out" = "tauri-cli 2.10.1" ] || { echo "::error::expected tauri-cli 2.10.1, got $out"; exit 1; }

- name: Lockfile supply-chain audit (pre-install scan)
run: python3 scripts/lockfile_supply_chain_audit.py

- name: Frontend build (npm ci, vite)
working-directory: studio/frontend
# 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.
# Vite build needs esbuild's native-binary postinstall; the
# pre-install lockfile audit above is what gates that path
# against the npm postinstall-dropper class.
run: |
npm ci --no-fund --no-audit
npm run build
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/wheel-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -229,5 +229,15 @@ log.txt
setup_leo.sh
server.pid
*.log
# 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
!studio/package-lock.json
# Bun lockfiles: same opt-in policy as package-lock.json above.
# bun install --frozen-lockfile needs these committed for the same surfaces.
bun.lock
!studio/frontend/bun.lock
!studio/backend/core/data_recipe/oxc-validator/bun.lock
!studio/bun.lock
llama.cpp/
Loading
Loading