Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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'
- '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"
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

- 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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -229,5 +229,9 @@ 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
llama.cpp/
23 changes: 8 additions & 15 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,14 @@ _restore_gitignores() {
}
trap _restore_gitignores EXIT

# Use bun for install if available (faster), fall back to npm.
_install_ok=false
if command -v bun &>/dev/null; then
if bun install; then
_install_ok=true
else
echo "⚠ bun install failed, falling back to npm"
rm -rf node_modules
fi
fi
if [ "$_install_ok" != "true" ]; then
if ! npm install; then
echo "❌ ERROR: package install failed" >&2
exit 1
fi
# Frontend installs always use npm ci against the committed lockfile.
# There is no bun.lock anywhere in the repo, so a bun-first branch
# would always miss and silently regenerate (or fail under
# --frozen-lockfile). Keep this single path until/unless a real
# bun.lock lands.
if ! npm ci --no-fund --no-audit; then
echo "❌ ERROR: npm ci failed" >&2
exit 1
fi
npm run build # outputs to studio/frontend/dist/

Expand Down
Loading
Loading