name: Vulnerability Scan on: pull_request: push: branches: [main] schedule: # Run weekly on Monday at 9:00 UTC - cron: '0 9 * * 1' permissions: contents: read jobs: pip-audit: name: pip-audit runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Restore global uv cache id: cache-restore uses: actions/cache/restore@v4 with: path: | ~/.cache/uv ~/.local/share/uv .venv key: uv-main-py3.11-${{ hashFiles('uv.lock') }} restore-keys: | uv-main-py3.11- - name: Install uv uses: astral-sh/setup-uv@v6 with: version: "0.11.3" python-version: "3.11" enable-cache: false - name: Install dependencies run: uv sync --all-groups --all-extras --no-install-project - name: Install pip-audit run: uv pip install pip-audit - name: Run pip-audit run: | uv run pip-audit --desc --aliases --skip-editable --format json --output pip-audit-report.json \ --ignore-vuln CVE-2026-3219 # Ignored CVEs: # CVE-2026-3219 - pip 26.0.1 (GHSA-58qw-9mgm-455v): no fix available, archive handling issue continue-on-error: true - name: Display results if: always() run: | if [ -f pip-audit-report.json ]; then echo "## pip-audit Results" >> $GITHUB_STEP_SUMMARY echo '```json' >> $GITHUB_STEP_SUMMARY cat pip-audit-report.json | python3 -m json.tool >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY # Fail if vulnerabilities found python3 -c " import json, sys with open('pip-audit-report.json') as f: data = json.load(f) vulns = [d for d in data.get('dependencies', []) if d.get('vulns')] if vulns: print(f'::error::Found vulnerabilities in {len(vulns)} package(s)') for v in vulns: for vuln in v['vulns']: print(f' - {v[\"name\"]}=={v[\"version\"]}: {vuln[\"id\"]}') sys.exit(1) print('No known vulnerabilities found') " else echo "::error::pip-audit failed to produce a report. Check the pip-audit step logs." exit 1 fi - name: Upload pip-audit report if: always() uses: actions/upload-artifact@v4 with: name: pip-audit-report path: pip-audit-report.json - name: Save uv caches if: steps.cache-restore.outputs.cache-hit != 'true' uses: actions/cache/save@v4 with: path: | ~/.cache/uv ~/.local/share/uv .venv key: uv-main-py3.11-${{ hashFiles('uv.lock') }}