@attestd/sdk
v0.5.0
Published
Official JavaScript/TypeScript client for the Attestd security risk API
Downloads
854
Maintainers
Readme
@attestd/sdk
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
Node 18+ required (native fetch and AbortSignal.timeout).
Install
npm install @attestd/sdkQuick start
import { Client } from '@attestd/sdk';
const client = new Client({ apiKey: process.env.ATTESTD_API_KEY! });
const result = await client.check('nginx', '1.25.3');
console.log(result.riskState); // 'high'
console.log(result.cveIds); // ['CVE-2024-7347']Batch check
Check up to 100 packages in one request. Each item costs one API call. A 429 is returned before billing if the batch would exceed your quota.
const results = await client.checkBatch([
{ product: 'litellm', version: '1.82.7' },
{ product: 'nginx', version: '1.25.3' },
]);
// results[i] is RiskResult | null — null means outside coverageUnsupported items return null rather than throwing. Typosquat signals are not surfaced on batch unsupported items.
Catalog and quota
Three additional endpoints for product discovery, CVE lookup, and quota monitoring. All require a valid API key.
Products catalog
const catalog = await client.products();
console.log(catalog.total);
console.log(catalog.cveProducts[0].slug);
console.log(catalog.supplyChainPackages[0].package);Returns ProductsResult with cveProducts, supplyChainPackages, and total. Maps to GET /v1/products.
CVE detail
import { AttestdAPIError } from '@attestd/sdk';
try {
const detail = await client.cve('CVE-2021-44228');
console.log(detail.cvssScore, detail.epssScore);
} catch (err) {
if (err instanceof AttestdAPIError && err.statusCode === 404) {
console.log('CVE not in database');
}
}Returns CveDetail with CVSS, EPSS, KEV status, and affected products. Throws AttestdAPIError with statusCode === 404 when the CVE is not in Attestd's database.
Usage quota
const usage = await client.usage();
console.log(usage.tier);
console.log(usage.keyCallsThisMonth, '/', usage.includedCalls);
console.log(usage.billingPeriodEnd);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. Pass scoped npm names as-is (@scope/pkg is URL-encoded by the client).
import { Client } from '@attestd/sdk';
const client = new Client({ apiKey: process.env.ATTESTD_API_KEY! });
const pypi = await client.check('litellm', '1.82.7');
console.log(pypi.supplyChain?.compromised); // true
const npm = await client.check('@bitwarden/cli', '2026.4.0');
console.log(npm.supplyChain?.compromised); // trueError handling
AttestdUnsupportedProductError means the product is outside Attestd coverage. That is unknown risk, not a safety signal.
import { Client, AttestdUnsupportedProductError } from '@attestd/sdk';
const client = new Client({ apiKey: process.env.ATTESTD_API_KEY! });
try {
await client.check(product, version);
} catch (err) {
if (err instanceof AttestdUnsupportedProductError) {
throw new Error(`${err.product} is outside Attestd coverage`);
}
throw err;
}| Error class | When thrown |
|---|---|
| AttestdAuthError | 401, invalid or missing API key |
| AttestdRateLimitError | 429, rate limit exceeded. Check .retryAfter (seconds) |
| AttestdUnsupportedProductError | Product not in Attestd coverage (404 or 200 with supported: false). Check .product, .version, and .typosquat |
| AttestdAPIError | Unexpected HTTP status, malformed response, network failure, or timeout. .statusCode is 0 for transport errors |
All error classes extend AttestdError, which extends Error.
CI/CD gate example
Block a deployment when a dependency is at critical or high risk:
import { Client, AttestdUnsupportedProductError } from '@attestd/sdk';
const client = new Client({ apiKey: process.env.ATTESTD_API_KEY! });
async function assertSafe(product: string, version: string) {
try {
const result = await client.check(product, version);
if (result.riskState === 'critical' || result.riskState === 'high') {
console.error(`BLOCKED: ${product}@${version} risk_state=${result.riskState}`);
process.exit(1);
}
} catch (err) {
if (err instanceof AttestdUnsupportedProductError) {
console.warn(`${product} is not covered by Attestd, skipping.`);
return;
}
throw err;
}
}
await assertSafe('nginx', process.env.NGINX_VERSION!);Client options
const client = new Client({
apiKey: process.env.ATTESTD_API_KEY!,
baseUrl: process.env.ATTESTD_BASE_URL ?? 'https://api.attestd.io',
timeout: 10_000,
maxRetries: 3,
fetch: customFetch,
retryDelayMs: 1_000,
});Set ATTESTD_API_KEY and optionally ATTESTD_BASE_URL in the environment. The constructor reads both when options are omitted.
RiskResult fields
| Field | Type | Description |
|---|---|---|
| product | string | Product name |
| version | string | Version queried |
| riskState | RiskState | critical, high, elevated, low, or none |
| riskFactors | RiskFactor[] | Machine-readable factors |
| activelyExploited | boolean | On the CISA KEV list |
| remoteExploitable | boolean | Remotely exploitable |
| authenticationRequired | boolean | True only if all CVEs require auth |
| patchAvailable | boolean | A fixed version is known |
| fixedVersion | string \| null | Earliest clean version |
| confidence | number | Synthesis confidence (0.0–1.0) |
| cveIds | string[] | CVE IDs in this assessment |
| lastUpdated | Date | UTC timestamp of last synthesis |
| supplyChain | SupplyChainSignal \| null | PyPI/npm signal when monitored |
| typosquat | TyposquatSignal \| null | Present when the name resembles a known product |
SupplyChainSignal: compromised, sources, malwareType, description, advisoryUrl, compromisedAt, removedAt
TyposquatSignal: detected, resembles, confidence, ecosystem
Catalog and quota types
| Interface | Key fields |
|---|---|
| ProductEntry | slug, displayName |
| SupplyChainEntry | package, ecosystem, displayName |
| ProductsResult | cveProducts, supplyChainPackages, total |
| CveDetail | cveId, description, cvssScore, cvssVector, activelyExploited, remoteExploitable, authenticationRequired, affectedProducts, epssScore, epssPercentile, sourcePublishedAt, lastCheckedAt |
| UsageResult | tier, keyCallsThisMonth, accountCallsThisMonth, includedCalls, billingPeriodStart, billingPeriodEnd, overageCalls, estimatedOverageUsd |
Testing module
Import mock helpers from @attestd/sdk/testing. They are not included in the main bundle.
import { Client } from '@attestd/sdk';
import {
MockFetch,
SequentialMockFetch,
NGINX_VULNERABLE,
LITELLM_COMPROMISED,
PYTORCH_LIGHTNING_COMPROMISED,
BITWARDEN_CLI_SAFE,
BITWARDEN_CLI_COMPROMISED,
} from '@attestd/sdk/testing';
const mock = new MockFetch(200, NGINX_VULNERABLE);
const client = new Client({ apiKey: 'test', fetch: mock.fn });
const result = await client.check('nginx', '1.25.3');
expect(result.riskState).toBe('high');Available fixtures: NGINX_SAFE, NGINX_VULNERABLE, LOG4J_CRITICAL, UNSUPPORTED, LITELLM_SAFE, LITELLM_COMPROMISED, PYTORCH_LIGHTNING_COMPROMISED, BITWARDEN_CLI_SAFE, BITWARDEN_CLI_COMPROMISED, PRODUCTS_RESPONSE, CVE_LOG4SHELL, USAGE_SOLO.
Jest note
If you use Jest (< v29) with the @attestd/sdk/testing subpath, configure customExportConditions:
// jest.config.js
module.exports = {
testEnvironment: 'node',
testEnvironmentOptions: {
customExportConditions: ['node', 'require', 'default'],
},
};Supported products
CVE-covered infrastructure products across databases, container runtimes, web/proxy, message brokers, and AI/ML frameworks. Full product list.
Supply chain monitoring covers PyPI and npm. Monitored packages.
License
MIT
