pip-compile wrapper with CVE awareness
Project description
safe-pip-compile
A drop-in pip-compile wrapper that avoids pinning packages with known CVEs.
pip-compile resolves dependencies without vulnerability awareness — it will happily pin a version with critical CVEs. safe-pip-compile automates the compile → audit → fix loop by querying OSV.dev and iteratively constraining vulnerable versions out of the resolution.
Installation
pip install -e .
Quick start
# Drop-in replacement for pip-compile
safe-pip-compile requirements.in -o requirements.txt
CLI flags
| Flag | Description | Default |
|---|---|---|
-o, --output-file PATH |
Output requirements.txt path | <input_basename>.txt |
--min-severity LEVEL |
Only block CVEs at this level or above (critical, high, medium, low) |
all (blocks everything) |
--allow-list PATH |
YAML file of accepted CVEs to skip | none |
--max-iterations INT |
Max compile→audit loops before giving up | 10 |
--strict / --no-strict |
Exit code 1 if unresolved CVEs remain | --strict |
--dry-run |
Show what would happen, write nothing | off |
--json-report PATH |
Write machine-readable JSON vulnerability report | none |
--cert PATH |
CA bundle for SSL verification (corporate proxies) | auto-detect from env |
--no-cache |
Disable local CVE cache, always query OSV.dev | cache enabled |
--refresh-cache |
Wipe cached data and re-fetch from OSV.dev | off |
-v, --verbose |
Increase output detail (-v, -vv) |
quiet |
All other flags pass through to pip-compile (e.g. --generate-hashes, --allow-unsafe).
Examples
# Only block high and critical CVEs
safe-pip-compile requirements.in --min-severity high
# Accept specific CVEs via allowlist
safe-pip-compile requirements.in --allow-list cve-allowlist.yaml
# Pass pip-compile flags through
safe-pip-compile requirements.in -- --generate-hashes --allow-unsafe
# CI: generate JSON report and fail on unresolved CVEs
safe-pip-compile requirements.in --json-report audit.json --strict
# Preview without writing files
safe-pip-compile requirements.in --dry-run -v
# Skip cache, always query OSV.dev fresh
safe-pip-compile requirements.in --no-cache
# Wipe cache and re-fetch everything
safe-pip-compile requirements.in --refresh-cache
Allowlist format
Create a cve-allowlist.yaml to accept specific CVEs:
allowed_cves:
- id: CVE-2024-12345
reason: "Not applicable — we don't use the affected feature"
expires: 2025-06-01 # optional, re-blocks after this date
- id: GHSA-xxxx-yyyy-zzzz
reason: "Accepted risk, tracked in JIRA-789"
Matches against vuln ID and all aliases (CVE, PYSEC, GHSA).
pyproject.toml config
[tool.safe-pip-compile]
max-iterations = 10
min-severity = "high"
allowlist = "cve-allowlist.yaml"
strict = true
CLI flags override these values.
Exit codes
| Code | Meaning |
|---|---|
0 |
Clean — no blocking CVEs |
1 |
Unresolved CVEs remain (strict mode) |
2 |
pip-compile resolution failed |
3 |
Network or configuration error |
How it works
requirements.in
│
▼
┌─ LOOP (max N iterations) ──────────────────────────┐
│ 1. pip-compile (with accumulated constraints) │
│ 2. Parse resolved packages │
│ 3. Check local cache → on miss, query OSV.dev │
│ 4. Filter by severity + allowlist │
│ 5. Generate constraints (e.g. django>=3.2.25) │
│ 6. Clean? → done. Stuck? → report. Else → loop. │
└─────────────────────────────────────────────────────┘
│
▼
requirements.txt (CVE-free)
Local CVE cache
Vulnerability data is cached in a local SQLite database to avoid repeated OSV.dev API calls.
- Location:
~/.cache/safe-pip-compile/cache.db(Linux),~/Library/Caches/safe-pip-compile/cache.db(macOS),%LOCALAPPDATA%\safe-pip-compile\Cache\cache.db(Windows) - TTL: 6 months — "django 3.2.1 has CVE-X, fix is 3.2.25" doesn't change
- No-fix vulns are never cached — they get re-checked every run in case a fix is published
- Works across virtualenvs — stored in user cache dir, not inside any venv
- Concurrent-safe — SQLite WAL mode allows parallel readers
Corporate proxy / SSL issues
If you're behind a corporate proxy that does SSL inspection, you may see:
OSV.dev error: Cannot reach OSV.dev API: [SSL: CERTIFICATE_VERIFY_FAILED]
Three ways to fix this:
Option 1: --cert flag
safe-pip-compile requirements.in --cert /path/to/corporate-ca-bundle.pem
Option 2: Environment variable (auto-detected, no flag needed)
# Set any of these — same vars that pip, requests, and httpx respect
export SSL_CERT_FILE=/path/to/corporate-ca-bundle.pem
# or
export REQUESTS_CA_BUNDLE=/path/to/corporate-ca-bundle.pem
safe-pip-compile requirements.in
Option 3: Find your org's CA cert
# Check if pip already knows where it is
python -m pip config get global.cert
# On Windows, export from certificate store:
# certmgr.msc → Trusted Root CAs → find your org's proxy cert → export as .pem
Lookup order: --cert flag > SSL_CERT_FILE > REQUESTS_CA_BUNDLE > CURL_CA_BUNDLE > system default.
Development
pip install -e ".[dev]"
python -m pytest tests/ -v
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file safe_pip_compile-0.1.2.tar.gz.
File metadata
- Download URL: safe_pip_compile-0.1.2.tar.gz
- Upload date:
- Size: 23.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
616c8e292ab20d6d9af10aca00a068b06a955fb3f0a32873e50c5e4ed103c381
|
|
| MD5 |
6c0462fd046c9ad773f429c48a56e181
|
|
| BLAKE2b-256 |
e24bc98e046cdbfbb92241e77931b9791c1fffb58cf48b754fc7bc0c098294ad
|
Provenance
The following attestation bundles were made for safe_pip_compile-0.1.2.tar.gz:
Publisher:
publish.yml on sai1027/safe-pip-compile
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
safe_pip_compile-0.1.2.tar.gz -
Subject digest:
616c8e292ab20d6d9af10aca00a068b06a955fb3f0a32873e50c5e4ed103c381 - Sigstore transparency entry: 1417809308
- Sigstore integration time:
-
Permalink:
sai1027/safe-pip-compile@5a1a1ffc83f3b87b1f69bea3bf0b8864b1132e63 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/sai1027
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5a1a1ffc83f3b87b1f69bea3bf0b8864b1132e63 -
Trigger Event:
push
-
Statement type:
File details
Details for the file safe_pip_compile-0.1.2-py3-none-any.whl.
File metadata
- Download URL: safe_pip_compile-0.1.2-py3-none-any.whl
- Upload date:
- Size: 26.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3220abc0d73f0ba5266d220eca2625fb61ed94c10aa3fa6fdfb3831e0cfe4aa2
|
|
| MD5 |
a0bdc51f824931194a5e2c2ef6a2b1ad
|
|
| BLAKE2b-256 |
c24cb1fc797130fdc26b8cff0080eebdb9e5c4ffec8ce5c3bc7455561f6d3642
|
Provenance
The following attestation bundles were made for safe_pip_compile-0.1.2-py3-none-any.whl:
Publisher:
publish.yml on sai1027/safe-pip-compile
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
safe_pip_compile-0.1.2-py3-none-any.whl -
Subject digest:
3220abc0d73f0ba5266d220eca2625fb61ed94c10aa3fa6fdfb3831e0cfe4aa2 - Sigstore transparency entry: 1417809316
- Sigstore integration time:
-
Permalink:
sai1027/safe-pip-compile@5a1a1ffc83f3b87b1f69bea3bf0b8864b1132e63 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/sai1027
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5a1a1ffc83f3b87b1f69bea3bf0b8864b1132e63 -
Trigger Event:
push
-
Statement type: