attestd
Attestd checks whether a dependency version has exploitable CVEs or a confirmed supply-chain compromise. One API call returns a structured risk response.
Get a free API key · Full docs
Requires Python 3.10+.
Install
pip install attestd
Quick start
import os
import attestd
client = attestd.Client(api_key=os.environ["ATTESTD_API_KEY"])
result = client.check("nginx", "1.20.0")
print(result.risk_state) # "high"
print(result.cve_ids) # ["CVE-2021-23017", ...]
Batch check
Check up to 100 packages in one request. Each item costs one API call. A 429 rejects the entire batch before billing.
results = client.batch_check([
("litellm", "1.82.7"),
("nginx", "1.25.3"),
])
# results[i] is RiskResult | None — None means outside coverage
Async:
results = await client.batch_check([("litellm", "1.82.7"), ("nginx", "1.25.3")])
Per-CVE detail
Default check / batch_check omit include and return compact results (cves is []). Pass include=["cves"] to request CVSS and EPSS per CVE. Compact and detailed responses are cached separately.
detailed = client.check("nginx", "1.20.0", include=["cves"])
print(detailed.cves[0].cvss_score)
print(detailed.cves[0].epss_score)
batch = client.batch_check(
[("nginx", "1.20.0")],
include=["cves"],
)
Catalog and quota
Three additional endpoints for product discovery, CVE lookup, and quota monitoring. All require a valid API key.
Products catalog
catalog = client.products()
print(catalog.total)
print(catalog.cve_products[0].slug)
print(catalog.supply_chain_packages[0].package)
Returns ProductsResult with cve_products, supply_chain_packages, and total. Maps to GET /v1/products.
CVE detail
try:
detail = client.cve("CVE-2021-44228")
print(detail.cvss_score, detail.epss_score)
except attestd.AttestdAPIError as e:
if e.status_code == 404:
print("CVE not in database")
Returns CveDetail with CVSS, EPSS, KEV status, and affected products. Raises AttestdAPIError(status_code=404) when the CVE is not in Attestd's database.
Usage quota
usage = client.usage()
print(usage.tier)
print(usage.key_calls_this_month, "/", usage.included_calls)
print(usage.billing_period_end)
Returns UsageResult with calls used this month, included cap, billing period, and overage estimate. Use for quota monitoring in CI or agent loops.
Supply chain check
Attestd monitors select PyPI and npm packages for known malicious publishes.
result = client.check("litellm", "1.82.7")
if result.supply_chain:
print(result.supply_chain.compromised) # True
Error handling
AttestdUnsupportedProductError means the product is outside Attestd coverage. That is unknown risk, not a safety signal.
try:
result = client.check(product, version)
except attestd.AttestdUnsupportedProductError:
raise RuntimeError(f"{product} is outside Attestd coverage")
See Outside coverage below for policy options.
Async
import asyncio
import os
import attestd
async def main():
async with attestd.AsyncClient(api_key=os.environ["ATTESTD_API_KEY"]) as client:
result = await client.check("log4j", "2.14.1")
if result.risk_state in ("critical", "high"):
raise RuntimeError(f"Vulnerable dependency: {result.cve_ids}")
asyncio.run(main())
CI/CD deployment gate
import os
import attestd
DEPENDENCIES = [
("nginx", "1.20.0"),
("log4j", "2.17.1"),
("openssh", "9.2p1"),
]
with attestd.Client(api_key=os.environ["ATTESTD_API_KEY"]) as client:
for product, version in DEPENDENCIES:
try:
result = client.check(product, version)
except attestd.AttestdUnsupportedProductError:
continue
if result.risk_state in ("critical", "high"):
print(f"BLOCK: {product} {version} ({result.risk_state})")
print(f" CVEs: {', '.join(result.cve_ids)}")
print(f" Fix: upgrade to {result.fixed_version}")
exit(1)
AI agent tool
import os
import attestd
client = attestd.Client(api_key=os.environ["ATTESTD_API_KEY"])
def check_dependency_risk(product: str, version: str) -> dict:
"""Return risk_state, CVE IDs, and fixed version for a dependency."""
try:
result = client.check(product, version)
return {
"supported": True,
"risk_state": result.risk_state,
"actively_exploited": result.actively_exploited,
"fixed_version": result.fixed_version,
"cve_ids": result.cve_ids,
}
except attestd.AttestdUnsupportedProductError:
return {"supported": False}
Full error handling
import os
import time
import attestd
with attestd.Client(api_key=os.environ["ATTESTD_API_KEY"]) as client:
try:
result = client.check("nginx", "1.20.0")
except attestd.AttestdUnsupportedProductError:
pass
except attestd.AttestdRateLimitError as e:
time.sleep(e.retry_after or 60)
except attestd.AttestdAuthError:
raise
except attestd.AttestdAPIError as e:
print(f"API error: {e.status_code}")
except attestd.AttestdError:
pass
Outside coverage: not a safety signal
AttestdUnsupportedProductError means Attestd has no vulnerability data for this product, not that the product is free of vulnerabilities. An agent that catches the exception and treats it as safe to proceed is making a dangerous inference.
Recommended handling:
try:
result = client.check(product, version)
except attestd.AttestdUnsupportedProductError as e:
raise RuntimeError(
f"{e.product} is outside Attestd's coverage. "
"Manual security review required before deploying."
)
RiskResult fields
| Field | Type | Description |
|---|---|---|
product |
str |
Product name |
version |
str |
Version queried |
risk_state |
str |
One of critical, high, elevated, low, none |
risk_factors |
list[str] |
Machine-readable factors (see below) |
actively_exploited |
bool |
On the CISA KEV list |
remote_exploitable |
bool |
Remotely exploitable |
authentication_required |
bool |
True only if ALL CVEs require auth |
patch_available |
bool |
A fixed version is known |
fixed_version |
str | None |
Earliest version that resolves all CVEs |
confidence |
float |
Synthesis confidence (0.0-1.0) |
cve_ids |
list[str] |
CVE IDs in this assessment |
max_epss |
float | None |
Highest EPSS probability across matching CVEs |
cves |
list[CveSummary] |
Per-CVE detail when include=["cves"] was passed; otherwise [] |
last_updated |
datetime |
UTC timestamp of last synthesis run |
supply_chain |
SupplyChainSignal | None |
PyPI/npm supply chain signal when monitored; None for CVE-only products |
typosquat |
TyposquatSignal | None |
Present when the package name resembles a known product |
TyposquatSignal: detected, resembles, confidence, ecosystem, kind, likely_intended
CveSummary: cve_id, cvss_score, actively_exploited, remote_exploitable, epss_score, epss_percentile
Risk states
| State | Meaning |
|---|---|
critical |
Actively exploited in the wild (CISA KEV) |
high |
Remote unauthenticated exploitation possible |
elevated |
Remote exploitation requires authentication |
low |
Local-only or low-impact vulnerability |
none |
No known vulnerabilities affecting this version |
Risk factors
| Factor | Meaning |
|---|---|
active_exploitation |
CVE on CISA KEV list |
remote_code_execution |
Remote exploitation possible |
no_authentication_required |
Remote + no auth required |
internet_exposed_service |
Remote + no auth (surface area flag) |
patch_available |
A fix is available |
Catalog and quota types
| Type | Key fields |
|---|---|
ProductEntry |
slug, display_name |
SupplyChainEntry |
package, ecosystem, display_name |
ProductsResult |
cve_products, supply_chain_packages, total |
CveSummary |
cve_id, cvss_score, actively_exploited, remote_exploitable, epss_score, epss_percentile |
CveDetail |
cve_id, description, cvss_score, cvss_vector, actively_exploited, remote_exploitable, authentication_required, affected_products, epss_score, epss_percentile, source_published_at, last_checked_at |
UsageResult |
tier, key_calls_this_month, account_calls_this_month, included_calls, billing_period_start, billing_period_end, overage_calls, estimated_overage_usd |
Configuration
client = attestd.Client(
api_key=os.environ.get("ATTESTD_API_KEY"),
base_url="https://api.attestd.io",
timeout=10.0,
max_retries=3,
retry_delay=1.0,
cache_policy="runtime",
)
print(client.stats())
client.invalidate_cache("nginx", "1.20.0")
cache_policy is one of development, runtime, ci, or none. Default is runtime. stats() returns session counters (api_calls_made, cache_hits, batch_saves, calls_saved). invalidate_cache(product, version) drops both the compact and detailed (include=["cves"]) cached results for that product and version.
AsyncClient accepts the same options plus batch_window_ms (default 5). Concurrent check() calls within that window are coalesced into one batch request. Set batch_window_ms=0 to disable coalescing.
Set ATTESTD_API_KEY in your environment from the portal. Then omit api_key in the constructor:
with attestd.Client() as client:
result = client.check("nginx", "1.20.0")
The SDK retries on transient 5xx responses and connection failures with exponential backoff (1s, 2s, 4s between attempts). Timeouts raise immediately and are not retried. 401 and 429 are surfaced immediately without retry.
Supported products
See attestd.io/docs/products for the full list. Each product page documents the API slug, version format, and notable CVEs. Querying an unsupported product raises AttestdUnsupportedProductError.
Testing your integration
The SDK ships an attestd.testing module with httpx transports for injecting controlled API responses. No local Attestd instance required.
import attestd
from attestd.testing import (
MockTransport,
MockAsyncTransport,
SequentialMockTransport,
SequentialMockAsyncTransport,
NGINX_SAFE,
NGINX_VULNERABLE,
LOG4J_CRITICAL,
UNSUPPORTED,
LITELLM_SAFE,
LITELLM_COMPROMISED,
PRODUCTS_RESPONSE,
CVE_LOG4SHELL,
USAGE_SOLO,
STATS_EMPTY,
)
Test a deployment gate
from attestd.testing import MockTransport, LOG4J_CRITICAL
def test_deployment_blocked_on_critical():
client = attestd.Client(
api_key="test",
transport=MockTransport(200, LOG4J_CRITICAL),
)
with pytest.raises(DeploymentBlockedError):
run_deployment_gate(client, "log4j", "2.14.1")
Test retry behaviour
from attestd.testing import SequentialMockTransport, NGINX_SAFE
def test_retry_succeeds_on_second_attempt():
transport = SequentialMockTransport([
(503, {}),
(200, NGINX_SAFE),
])
client = attestd.Client(api_key="test", transport=transport, max_retries=1)
result = client.check("nginx", "1.27.4")
assert result.risk_state == "none"
assert transport.call_count == 2
Test the outside coverage policy branch
from attestd.testing import MockTransport, UNSUPPORTED
def test_outside_coverage_is_blocked_not_allowed():
client = attestd.Client(api_key="test", transport=MockTransport(200, UNSUPPORTED))
with pytest.raises(attestd.AttestdUnsupportedProductError):
run_check(client, "unknownproduct", "1.0.0")
Custom response bodies
All ready-made bodies (NGINX_SAFE, etc.) are plain dicts. Merge in overrides:
from attestd.testing import MockTransport, NGINX_VULNERABLE
body = {**NGINX_VULNERABLE, "actively_exploited": True, "risk_state": "critical"}
client = attestd.Client(api_key="test", transport=MockTransport(200, body))
License
MIT
Metadata
Release files for attestd 0.7.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| attestd-0.7.2.tar.gz | 33.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| attestd-0.7.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 61.0 kB
Release files / attestd-0.7.2.tar.gz
| Download URL | attestd-0.7.2.tar.gz |
|---|---|
| Size | 33.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cbdb2b3c3885c85f5d47b2e75dd646d00f8be783f8feea6760b83a1d0228d197
|
|
BLAKE2b-256 checksum How to use checksums |
6f58fe33157594e7f1c388928663c468c84def56577c71705b89c2b6f97d8559
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency logRelease files / attestd-0.7.2-py3-none-any.whl
| Download URL | attestd-0.7.2-py3-none-any.whl |
|---|---|
| Size | 27.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b57feec30ce4c512fd0036fe151cdbebb7870c9735d11e0e7109431c26d127de
|
|
BLAKE2b-256 checksum How to use checksums |
9a0fb4aa80c1c57b9d34228afe38669b2ad0c011c2304509d8c8104561205d50
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.
Transparency log