unsloth/.github/workflows/security-audit.yml
Daniel Han de7fd062ab CI: add codespell + shellcheck to Lint CI; add Security audit workflow
Three Priority-1 follow-ups from the lint review.

Lint CI gains two non-blocking gates that surface drift without
blocking merges (the same shape as the existing format-drift step):

  - codespell: typo catcher across source / comments / docs. Skips
    lockfiles, generated assets, binary artefacts, LICENSE files.
    ignore-words-list pulls out short identifiers and PyTorch
    idioms (parm/parms, ans, hist, etc.) the default dictionary
    would flag. Local run finds 16 real typos to fix in a follow-up.

  - shellcheck: catches subtle shell bugs `bash -n` doesn't see --
    unquoted expansions, useless cat, `[[ ]]` command substitution,
    etc. SC1090 + SC2034 muted because install/setup scripts
    legitimately source runtime paths and use export-only
    assignments. Critical-path coverage: install.sh, setup.sh,
    tests/sh/.

Both pinned for reproducibility (codespell>=2.3,<3 in pip,
shellcheck via apt-get). Both surface findings in PR annotations
without failing the run; drop continue-on-error after the cleanup
PRs land.

New workflow: Security audit. Runs `pip-audit` against the same
dep set Studio's backend pytest matrix installs, so we audit what
the runtime actually loads (not what pyproject.toml's transitive
resolution might pull in differently). Triggers:
  - PRs touching requirements / pyproject.toml,
  - push to main / pip,
  - nightly @ 04:13 UTC (off-the-hour to dodge cron rush),
  - workflow_dispatch.

The default branch already carries 17 known vulnerabilities per
the dependabot banner, so a hard gate today would block every PR
on a baseline we have not triaged. Non-blocking; full table goes
to GITHUB_STEP_SUMMARY for grep-ability and a 30-day artefact for
historical comparison.

The custom AST anti-pattern scan I prototyped was dropped: every
class of CPU-import-time bug we hit in this PR (bitsandbytes,
torchvision, _cuda_getCurrentRawStream, DEVICE_COUNT==0 stream
init) is already caught by the Repo tests (CPU) job exercising
the actual import on a CPU torch wheel. Restating the rule
in AST form would only add noise.
2026-05-06 21:21:59 +00:00

96 lines
3.2 KiB
YAML

# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved.
# pip-audit on the Studio backend's resolved dep tree. Runs:
# - on PRs touching any requirements file or pyproject.toml,
# - nightly on a schedule against main, so newly-published CVEs
# surface even when no PR opens,
# - on workflow_dispatch for ad-hoc invocations.
#
# pip-audit is non-blocking initially. The default branch already
# carries 17 known vulnerabilities (per the dependabot banner on
# every push), so a hard gate today would block every PR on a
# baseline we haven't triaged. As the baseline closes, drop
# continue-on-error and the next PR that introduces a NEW vuln
# will be blocked instead.
name: Security audit
on:
pull_request:
paths:
- 'studio/backend/requirements/**'
- 'pyproject.toml'
- '.github/workflows/security-audit.yml'
push:
branches: [main, pip]
schedule:
# 04:13 UTC every day. Off-the-hour so we don't pile onto the
# GitHub-Actions cron rush at :00.
- cron: '13 4 * * *'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
pip-audit:
name: pip-audit (Studio backend deps)
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install pip-audit + the dep set under test
# We resolve the same dep set Studio's backend pytest matrix
# installs, then pip-audit it. This audits what Studio
# actually loads at runtime, not what pyproject.toml's
# transitive resolution might pull in differently on a
# different host.
run: |
python -m pip install --upgrade pip 'pip-audit>=2.7'
pip install -r studio/backend/requirements/studio.txt
pip install \
python-multipart aiofiles sqlalchemy cryptography \
pyyaml jinja2 mammoth unpdf requests typer \
'numpy<3' pytest pytest-asyncio httpx
pip install --index-url https://download.pytorch.org/whl/cpu \
'torch>=2.4,<2.11' 'torchvision<0.26'
pip install 'transformers>=4.51,<5.5'
pip install 'bitsandbytes>=0.45'
pip install 'unsloth_zoo>=2026.5.1'
pip install -e . --no-deps
- name: pip-audit (informational, drives baseline triage)
# `--strict`: fail on any finding (we'll use it once the
# baseline is clean).
# For now, run without --strict and let the runner surface
# the count + which packages are affected. Output goes to
# the workflow summary so it's grep-able.
continue-on-error: true
run: |
pip-audit --format=columns | tee logs-audit.txt
{
echo "## pip-audit results"
echo
echo "\`\`\`"
cat logs-audit.txt
echo "\`\`\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload audit log
if: always()
uses: actions/upload-artifact@v4
with:
name: pip-audit-log
path: logs-audit.txt
retention-days: 30