Running pip-audit in CI/CD: Failing Builds on Critical and High Severity Vulnerabilities
Unpinned dependencies can introduce vulnerabilities that reach production through requirements.txt. We’ll use pip-audit in CI/CD to scan locked files like requirements.txt or pyproject.toml, failing builds only for critical and high severity issues (CVSS 7.0+). Filter noise from low/medium vulns with JSON + jq or ignores—no manual scans needed. Includes GitHub Actions, GitLab CI, and Jenkins examples. Builds on article 16: Pinning Transitives.
Why pip-audit in CI/CD?
- Automatic scanning: Checks locked dependencies (pinned via pip-compile or uv, as in article 16), exits non-zero on detected vulnerabilities.
- Severity focus: Defaults to failing on any vulnerability; customize scripts parse JSON for critical/high only via description keywords or CVSS scores—though keyword matching can have limitations.
- Performance: Typically fast (<1s for 100+ deps) by reusing pip cache; scales well for most projects.
- Compliance: Audit logs support SOC2/PCI requirements, though consult your compliance team for full integration.
Of course, this requires pinned dependencies first (see article 16).
Test Locally First
# Install pip-audit (isolated, no deps conflicts)\n$ pipx install pip-audit\n\n# Test requirements.txt (assumes pinned from article 16)\n$ pip-audit -r requirements.txt # Exits 1 on vulns\n\n# JSON output for severity filtering\n$ pip-audit -r requirements.txt --format=json
It exits non-zero on vulnerabilities, integrating seamlessly with CI && chains.
GitHub Actions Example
Full workflow—fails build if critical/high detected.
name: Python Security Audit (pip-audit)\non:\n push:\n paths: ['requirements.in', 'pyproject.toml', '**/*.txt']\n pull_request:\n\njobs:\n audit:\n runs-on: ubuntu-latest\n steps:\n - name: Checkout code\n uses: actions/checkout@v4
- name: Set up Python 3.12\n uses: actions/setup-python@v5\n with:\n python-version: '3.12'\n - name: Install tools\n run: pipx install pip-audit uv\n - name: Compile pinned requirements (from article 16)\n run: uv pip compile requirements.in -o requirements.txt --hashes
- run: |
pip-audit -r requirements.txt --format=json > audit.json
# Fail only critical/high (grep CVSS keywords in desc; customize)
# Fail only critical/high via keywords in description (approximate; tune regex for your vulns)\n # Matches: "critical", "high", "CVSS [7-9]", "CVSS 10", case-insensitive\n if jq -e '.[] | .vulns[] | select(.description | ascii_downcase | test("critical|high|cvss [7-9]|cvss 10|severity: high"; "i"))' audit.json > /dev/null; then\n echo "Critical or high severity vulnerabilities found. Review audit.json."\n exit 1
fi
name: pip-audit (critical/high only)
- name: Fallback: Official pip-audit action (fails all vulns)\n if: failure()\n uses: pypa/gh-action-pip-audit@v1
Official gh-action-pip-audit: fails any vuln.
GitLab CI: .gitlab-ci.yml
audit:\n stage: test\n image: python:3.12-slim\n script:\n - name: Install pip-tools and pip-audit\n pip install pip-tools pip-audit
- name: Compile pinned requirements\n pip-compile requirements.in --generate-hashes\n - name: Run pip-audit JSON\n pip-audit -r requirements.txt --format=json > audit.json || true\n - name: Check for critical/high vulns (keyword approx)\n |\n if grep -qiE "critical|high severity|CVSS [7-9]|CVSS 10" audit.json; then
echo "Critical/High vulns! Failing build."
exit 1
fi
rules:
- changes:
- requirements.in
- pyproject.toml
Jenkins Pipeline
pipeline {\n agent any\n stages {\n stage('Python Security Audit') {\n steps {\n sh '''\n # Install tools (pipx preferred, but pipx may need setup in agent)
pipx install pip-audit\n pip-audit -r requirements.txt --format=json > audit.json\n # Fail on critical/high keywords (tune as needed)\n if grep -qiE "critical|high severity|CVSS [7-9]|CVSS 10" audit.json; then
echo "Failing: critical/high vulns"
exit 1
fi
'''
}
}
}
}
Ignore Low/Medium Vulns
> Tip: pip-audit draws from PyPA advisory database. For acceptable risks or false positives (e.g., resolved pytest issues), use --ignore-vuln ID.\n\nbash\n$ pip-audit -r requirements.txt --ignore-vuln GHSA-w596-4wvx-j9j6\n\n\nGenerate ignore file (review candidates):\n\nbash\n$ pip-audit -r requirements.txt --format=json | jq -r '.[]?.vulns?[]?.id // empty' | sort -u > candidate-ignores.txt\n# Edit/review, rename to .pip-audit-ignores.txt, commit with comments\n\n\nLoad in CI: --ignore-vuln-file .pip-audit-ignores.txt
Commit .pip-audit-ignores.txt:
GHSA-w596-4wvx-j9j6 # pytest noise
PYSEC-2023-123 # Low impact
Load: pip-audit -r requirements.txt --ignore-vuln-file .pip-audit-ignores.txt
Troubleshooting
| Issue | Fix |
|---|---|
| No vulns but fails | Unpinned deps—pin via 16 |
| jq/grep false pos | Customize regex; use full OSV API for CVSS |
| Slow audits | --no-deps + pinned/hashed reqs |
| Third-party index | --index-url / --extra-index-url |
| Ignore not working | Use aliases CVE/GHSA |
Alternative: Safety CLI\n\nSafety provides native severity-based failing:\n\nbash\n$ safety check --full-report -r requirements.txt --fail-on=CRITICAL,HIGH\n\n\nPros: Built-in CVSS parsing (no scripts), enterprise dashboards (Safety Pro).\nCons: Core free, advanced features paid; pip-audit is FOSS with PyPA backing.\n\nUse Safety if you need precise severity without custom logic; pip-audit for open-source simplicity.
Conclusion
With pip-audit and severity filtering, you can effectively gate critical and high vulnerabilities in your Python CI/CD pipelines while managing noise through ignores. Of course, regularly review and update your ignore list. For reproducible environments, see article 15 on uv/pip-sync.
Related:
Questions? Drop your CI yaml below.
Sponsored by Durable Programming
Need help maintaining or upgrading your Python application? Durable Programming specializes in keeping Python apps secure, performant, and up-to-date.
Hire Durable Programming