A focused prototype that shows one core concept: when a developer requests a vulnerable Python package, Echo detects the CVE, shows a detailed remediation block, and either blocks (High) or warns and proceeds (Medium). A separate step demonstrates applying Echo-patched wheels and proving the registry check passes.
Two distinct severity flows using urllib3 and requests:
| Package | CVE | Severity | CVSS | Behaviour |
|---|---|---|---|---|
urllib3==1.26.0 |
CVE-2021-33503 | High | 7.5 | CVE info block printed, build blocked (exit 1) |
requests==2.28.0 |
CVE-2023-32681 | Medium | 6.1 | CVE info block printed, proceeds with vulnerable version |
Both severity levels show the same informational block:
[ECHO] ⚠ CVE DETECTED: {pkg}=={ver} — {cve_id} ({severity}, CVSS {cvss})
Affected range: {range}
Safe release: {pkg}>={first_patched}
Echo patched: {pkg}=={pivot}+echo1 ← same version, backport fix applied
Fix options:
pip install '{pkg}>={first_patched}' # upgrade to safe release
pip install '{pkg}=={pivot}+echo1' \
--find-links factory/artifacts/ # use Echo patched build
Each patched wheel embeds a CycloneDX 1.4 SBOM so security scanners can verify the fix.
client/install.py # pip wrapper: checks registry → shows CVE block → warns/blocks
│
▼
registry/server.py # FastAPI: PEP 503 simple index + /check endpoint
│
├── GET /check/{package}/{version} → CVE lookup (early-return for +echo1 versions)
├── GET /simple/{package}/ → PEP 503 wheel index w/ CVE metadata
└── GET /files/{filename} → serve patched wheel
factory/builder.py # demand-driven backport builder (30-day filter)
factory/sbom.py # CycloneDX SBOM generator + wheel injector
db/schema.py # SQLite schema (cves, version_groups, request_log)
db/discover.py # GHSA discovery: fetches live CVE data → populates DB
db/seed.py # seeds demo request_log rows (30-day window illustration)
The builder only builds a wheel if a matching package version was requested within the last 30 days (tracked in request_log). Seeded demo data illustrates both sides:
| Package | Version | Last Requested | Outcome |
|---|---|---|---|
| requests | 2.28.0 | 7 days ago | BUILD (within window, in CVE range) |
| requests | 2.26.0 | 50 days ago | SKIP (outside 30-day window) |
| urllib3 | 1.26.0 | 15 days ago | pre-built (skip) |
/check/{package}/{version} immediately returns {"vulnerable": false} when the version string contains +echo1. This ensures that once you install an Echo-patched wheel and re-run with the patched spec, the check passes without hitting the CVE database.
Echo uses the GitHub Security Advisory (GHSA) GraphQL API to fetch live CVE data. The API requires a GitHub personal access token — no special scopes or permissions are needed, just a basic token that proves you have a GitHub account.
Classic token (simplest):
- Go to github.com → Settings → Developer settings → Personal access tokens → Tokens (classic)
- Click Generate new token (classic)
- Give it any name (e.g.
echo-demo) - Leave all scopes unchecked — the GHSA API only needs public read access
- Click Generate token and copy it
Fine-grained token (alternative):
- Go to github.com → Settings → Developer settings → Personal access tokens → Fine-grained tokens
- Click Generate new token
- Set expiration, leave repository access as Public Repositories (read-only)
- No additional permissions needed — click Generate token and copy it
Once you have a token:
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxTokens starting with
ghp_are classic tokens;github_pat_are fine-grained. Both work.
# 1. Export your GitHub token (see above)
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx
# 2. Reset to clean state (creates venvs, plants vulnerable versions)
./reset.sh
# 3. Run the full 8-step demo
./run.shRequirements: Python 3.9+, internet access,
GITHUB_TOKENenv var. Both scripts auto-create.venv(tool environment) and.demo_env(simulated customer environment).
# Discover CVEs from GHSA and populate the database
export GITHUB_TOKEN=ghp_xxx
python3 db/discover.py urllib3 requests
# Seed demo request_log rows (30-day demand window illustration)
python3 db/seed.py
# Start registry on :8000
uvicorn registry.server:app --port 8000
# Run builder (builds requests wheel, skips pre-built urllib3 wheels)
python3 factory/builder.py
# Medium severity: CVE info block + proceeds with vulnerable version
python3 client/install.py requests==2.28.0
# High severity: CVE info block + build blocked (exit 1)
python3 client/install.py urllib3==1.26.0
# Full requirements (blocked due to urllib3 High CVE)
python3 client/install.py -r client/requirements.txt
# Install Echo-patched wheels
pip install "urllib3==1.26.4+echo1" "requests==2.28.2+echo1" \
--find-links factory/artifacts/
# Re-run with patched specs — registry returns vulnerable=false → succeeds
python3 client/install.py "urllib3==1.26.4+echo1" "requests==2.28.2+echo1"Checks Python, dependencies, and verifies the three urllib3 pre-built wheels exist in factory/artifacts/. Shows the demo environment's "before" state (urllib3==1.26.0, requests==2.28.0).
Runs db/discover.py to query the GitHub Security Advisory database (GHSA) for urllib3 and requests advisories; maps each advisory to a cves + version_groups row and resolves the pivot version from PyPI. Then runs db/seed.py to insert 5 synthetic request_log rows. Starts the FastAPI registry on :8000.
Runs factory/builder.py. urllib3 groups are already built (skipped). The requests group (2.28.0 was requested 7 days ago) is eligible and built. A CycloneDX SBOM is injected into the wheel.
[ECHO] ⚠ CVE DETECTED: requests==2.28.0 — CVE-2023-32681 (Medium, CVSS 6.1)
Affected range: >=2.1.0,<2.31.0
Safe release: requests>=2.31.0
Echo patched: requests==2.28.2+echo1 ← same version, backport fix applied
Fix options:
pip install 'requests>=2.31.0'
pip install 'requests==2.28.2+echo1' --find-links factory/artifacts/
[ECHO] ⚠ Proceeding with vulnerable version (Medium severity — explicit fix recommended)
requests==2.28.0 is installed in the demo environment (not the patched version — the developer must apply the fix explicitly).
[ECHO] ⚠ CVE DETECTED: urllib3==1.26.0 — CVE-2021-33503 (High, CVSS 7.5)
...
[ECHO] ⚠ CVE DETECTED: requests==2.28.0 — CVE-2023-32681 (Medium, CVSS 6.1)
...
[ECHO] ✗ BUILD BLOCKED — one or more High severity CVEs require explicit upgrade.
client/install.py exits with code 1. The build is aborted due to the urllib3 High CVE.
Installs urllib3==1.26.4+echo1 and requests==2.28.2+echo1 directly into the demo environment using --find-links factory/artifacts/. Shows the installed versions to confirm the patched wheels are active.
Runs install.py with the +echo1 version specs directly. The registry /check endpoint sees +echo1 in the version string and returns {"vulnerable": false} immediately — no CVE block, no blocking, clean exit 0.
Registry check: GET /check/urllib3/1.26.4+echo1 → {"vulnerable": false}
Shows the PEP 503 index for urllib3 (with embedded CVE metadata in HTML comments) and the updated request_log counts.
CVE-2021-33503 — urllib3, High, CVSS 7.5
ReDoS via crafted HTTP response header in urllib3.util.url. Fixed in 1.26.5.
| Version range | Pivot | Artifact |
|---|---|---|
>=1.25.4,<1.25.8 |
1.25.7 | urllib3-1.25.7+echo1-py2.py3-none-any.whl |
>=1.25.8,<1.26.5 |
1.26.4 | urllib3-1.26.4+echo1-py2.py3-none-any.whl |
>=2.0.0,<2.0.6 |
2.0.5 | urllib3-2.0.5+echo1-py3-none-any.whl |
CVE-2023-32681 — requests, Medium, CVSS 6.1 SSRF via trusted Host header forwarded through redirects to untrusted origins. Fixed in 2.31.0.
| Version range | Pivot | Artifact |
|---|---|---|
>=2.1.0,<2.31.0 |
2.28.2 | requests-2.28.2+echo1-py3-none-any.whl (built during demo) |
python3 -c "
import zipfile, json
with zipfile.ZipFile('factory/artifacts/requests-2.28.2+echo1-py3-none-any.whl') as zf:
sbom_path = next(n for n in zf.namelist() if 'sbom.cdx.json' in n)
print(json.dumps(json.loads(zf.read(sbom_path)), indent=2))
"
# Expected: CycloneDX SBOM with CVE-2023-32681, analysis.state = "resolved"echo/
├── db/
│ ├── schema.py # SQLite schema + get_connection() + init_db()
│ ├── discover.py # GHSA discovery service → populates cves + version_groups
│ └── seed.py # seeds demo request_log rows
├── factory/
│ ├── builder.py # demand-driven backport wheel builder
│ ├── sbom.py # CycloneDX SBOM generator + wheel injector
│ └── artifacts/ # pre-built urllib3 wheels + CVE patches
├── registry/
│ └── server.py # FastAPI PEP 503 registry + /check endpoint
├── client/
│ ├── install.py # pip wrapper with CVE check + remediation output
│ └── requirements.txt # urllib3==1.26.0 + requests==2.28.0 (demo "before")
├── run.sh # 8-step demo script
└── reset.sh # reset to clean state