#!/usr/bin/env bash # Pre-deploy supply-chain review: CycloneDX SBOM + known-CVE scan (pip-audit). # # Run before promoting a build to a prod HF Space. Does NOT modify the repo or # install anything globally. Outputs go to security/ as committable artifacts. # # Usage: scripts/security_scan.sh [requirements.txt] # Requires: uv (for the SBOM) and a Python >=3.11 with a working venv (for # pip-audit). The audit venv is cached in security/.audit-venv. set -euo pipefail cd "$(dirname "$0")/.." REQ="${1:-requirements.txt}" OUT="security" mkdir -p "$OUT" STAMP="$(date -u +%Y%m%dT%H%M%SZ)" [[ -f "$REQ" ]] || { echo "error: '$REQ' not found" >&2; exit 1; } # ---- 1. CycloneDX SBOM (from the pinned requirements; no install needed) ---- if command -v uv >/dev/null 2>&1; then echo "==> CycloneDX SBOM -> $OUT/sbom.json" uvx --from cyclonedx-bom cyclonedx-py requirements "$REQ" --of JSON -o "$OUT/sbom.json" else echo "WARN: uv not found; skipping SBOM (install from https://docs.astral.sh/uv/)" >&2 fi # ---- 2. pip-audit (known CVEs) ---- # The first-party biodata-registry git+ line is not a PyPI CVE target and HF's # git server rejects pip's partial clone, so strip it before auditing. REQ_NOGIT="$(mktemp)"; trap 'rm -f "$REQ_NOGIT"' EXIT grep -v "git+" "$REQ" > "$REQ_NOGIT" # pip-audit resolves the pinned versions in a venv matching its interpreter, so # we need a Python >=3.11 whose venv/ensurepip actually works (uv's standalone # builds can have a broken ensurepip on macOS). Discover one. PYBIN="" for cand in python3.13 python3.12 python3.11 python3; do command -v "$cand" >/dev/null 2>&1 || continue td="$(mktemp -d)"; if "$cand" -m venv "$td/probe" >/dev/null 2>&1; then PYBIN="$cand"; rm -rf "$td"; break; fi rm -rf "$td" done if [[ -z "$PYBIN" ]]; then echo "WARN: no Python >=3.11 with a working venv found; skipping pip-audit." >&2 echo " SBOM was still generated at $OUT/sbom.json." >&2 exit 0 fi VENV="$OUT/.audit-venv" if [[ ! -x "$VENV/bin/pip-audit" ]]; then echo "==> setting up audit venv ($PYBIN)" "$PYBIN" -m venv "$VENV" "$VENV/bin/python" -m pip install -q --upgrade pip pip-audit fi echo "==> pip-audit (known CVEs in $REQ, git dep excluded)" set +e "$VENV/bin/python" -m pip_audit --requirement "$REQ_NOGIT" --no-deps \ --format columns | tee "$OUT/pip-audit-$STAMP.txt" AUDIT_RC=${PIPESTATUS[0]} "$VENV/bin/python" -m pip_audit --requirement "$REQ_NOGIT" --no-deps \ --format json --output "$OUT/pip-audit-$STAMP.json" >/dev/null 2>&1 || true set -e echo if [[ "$AUDIT_RC" -eq 0 ]]; then echo "OK: no known vulnerabilities. SBOM at $OUT/sbom.json" else echo "FINDINGS: pip-audit reported vulnerabilities (rc=$AUDIT_RC)." echo "Review $OUT/pip-audit-$STAMP.txt. Note: some pins (gradio, mcp) are" echo "constrained by the HF Space sdk_version and can't be freely bumped." fi exit "$AUDIT_RC"