supply chain

Supply chain integrity

Attestd monitors over 27,459 PyPI and 237,601 npm packages for malicious publishes alongside CVE-based risk. The supply_chain object in a /v1/check response is independent from risk_state (which reflects NVD-derived vulnerability data only).

Supply chain compromise is deterministic: a package version either has a known malicious publish or it does not. Signals come from four sources: the Attestd registry (human-verified), OSV malicious-package advisories, PyPI yanks with security annotations, and npm deprecation messages with targeted attack language. This means you can block deployment before running code from a compromised dependency.

Package name integrity

Attestd also checks whether the package name itself is the risk. The typosquat field covers classic misspellings and AI-hallucinated or conflated names (slopsquatting). When an agent or developer requests a name that is not on the watchlist, Attestd can still return what they likely meant. Example: react-codeshift resolves toward jscodeshift with kind: "hallucination".

Detection is split by kind. Compound and AI-conflated names (for example watchlist tokens joined into a new string) are matched deterministically at query time with no LLM call. Classic typosquats are evaluated by a background sweep that uses a small model and writes results to cache; the API serves those cached hits. Do not assume every misspelling is instantaneous on first sighting.

See response fields for the full signal shape.

Quick start

Check a PyPI package for supply chain compromise. LiteLLM 1.82.7 is a confirmed malicious publish. Use it to test your integration end-to-end:

bash
# Check a PyPI package for supply chain compromise
curl "https://api.attestd.io/v1/check?product=litellm&version=1.82.7" \
  -H "Authorization: Bearer YOUR_API_KEY"

npm packages use the same endpoint. URL-encode scoped names (@scope/name becomes %40scope%2Fname):

bash
# Check an npm package for supply chain compromise
# Scoped package names must be URL-encoded (@scope/name -> %40scope%2Fname)
curl "https://api.attestd.io/v1/check?product=%40bitwarden%2Fcli&version=2026.4.0" \
  -H "Authorization: Bearer YOUR_API_KEY"

A safe package returns compromised: false with an empty sources array.

Example responses

Safe version

json
{
  "product": "langchain",
  "version": "0.3.0",
  "supported": true,
  "risk_state": "none",
  "risk_factors": [],
  "actively_exploited": false,
  "remote_exploitable": false,
  "authentication_required": false,
  "patch_available": false,
  "fixed_version": null,
  "confidence": 0.9,
  "cve_ids": [],
  "cves": null,
  "max_epss": null,
  "typosquat": null,
  "supply_chain": {
    "compromised": false,
    "sources": [],
    "malware_type": null,
    "advisory_url": null,
    "compromised_at": null,
    "removed_at": null
  },
  "supply_chain_monitored": true,
  "last_updated": "2026-02-23T18:21:30Z"
}

Compromised version

When a malicious publish is detected:

json
{
  "product": "litellm",
  "version": "1.82.7",
  "supported": true,
  "risk_state": "none",
  "risk_factors": [],
  "actively_exploited": false,
  "remote_exploitable": false,
  "authentication_required": false,
  "patch_available": false,
  "fixed_version": null,
  "confidence": 1,
  "cve_ids": [],
  "cves": null,
  "max_epss": null,
  "supply_chain": {
    "compromised": true,
    "sources": [
      "osv",
      "registry"
    ],
    "malware_type": "backdoor",
    "description": "TeamPCP supply chain attack: a malicious version contained a credential stealer in proxy_server.py targeting LLM provider API keys. Published at 10:39 UTC and removed within six hours after community detection.",
    "advisory_url": "https://docs.litellm.ai/blog/security-update-march-2026",
    "compromised_at": "2026-03-24T10:39:00Z",
    "removed_at": "2026-03-24T16:00:00Z"
  },
  "supply_chain_monitored": true,
  "typosquat": null,
  "last_updated": "2026-04-27T16:07:47.644177Z"
}

Using the SDKs

Python SDK: check supply chain status in your application or deployment pipeline:

python
import attestd

client = attestd.Client(api_key="YOUR_API_KEY")

# Check a PyPI package (safe version)
result = client.check("langchain", "0.3.0")

# Check supply chain signal
if result.supply_chain and result.supply_chain.compromised:
    print(f"[ALERT] Malicious version detected: {result.product} {result.version}")
    print(f"Detected by: {', '.join(result.supply_chain.sources)}")
    print(f"Description: {result.supply_chain.description}")
    raise SystemExit("Do not deploy - compromised dependency")

if result.supply_chain is None:
    print(f"Note: {result.product} is not in supply chain monitoring")
    # Still check CVE risk
    if result.risk_state in ("critical", "high"):
        raise SystemExit(f"CVE risk detected: {result.risk_state}")

print(f"Safe to proceed: {result.product} {result.version}")

JavaScript / TypeScript SDK: same check for npm packages:

check_supply_chain.ts
import { Client } from '@attestd/sdk';

const client = new Client({ apiKey: process.env.ATTESTD_API_KEY });

// Check an npm package
const result = await client.check('@bitwarden/cli', '2026.4.0');

if (result.supplyChain?.compromised) {
  console.error('SUPPLY CHAIN ALERT:', result.supplyChain.description);
  process.exit(1);
}

console.log(`Safe to proceed: ${result.product} ${result.version}`);

Scanning a requirements file

Scan all dependencies in requirements.txt for supply chain compromise before deploying:

requirements-check.py
# requirements-check.py
import attestd
import re
import sys

def parse_requirements(filename):
    """Parse requirements.txt and return list of (package, version) tuples"""
    packages = []
    with open(filename) as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            # Handle == and >= operators
            if '==' in line:
                pkg, ver = line.split('==')
                packages.append((pkg.strip(), ver.strip()))
            elif '>=' in line:
                pkg, ver = line.split('>=')
                packages.append((pkg.strip(), ver.strip()))
    return packages

def check_dependencies(filename, api_key):
    """Check all dependencies for supply chain compromise"""
    client = attestd.Client(api_key=api_key)
    packages = parse_requirements(filename)
    
    compromised = []
    for package, version in packages:
        try:
            result = client.check(package, version)
            
            # Check supply chain compromise
            if result.supply_chain and result.supply_chain.compromised:
                compromised.append({
                    'package': package,
                    'version': version,
                    'reason': result.supply_chain.description,
                    'sources': result.supply_chain.sources
                })
            
            # Also check CVE risk
            if result.risk_state in ("critical", "high"):
                compromised.append({
                    'package': package,
                    'version': version,
                    'reason': f"CVE risk: {result.risk_state}",
                    'cves': result.cve_ids
                })
        except attestd.AttestdUnsupportedProductError as e:
            if e.typosquat and e.typosquat.detected:
                compromised.append({
                    'package': package,
                    'version': version,
                    'reason': (
                        f"name integrity ({e.typosquat.kind}): "
                        f"prefer {e.typosquat.resembles}"
                    ),
                    'sources': ['typosquat'],
                })
            else:
                # Not in attestd coverage - not necessarily bad, but alert
                print(f"[WARNING] {package} not in attestd coverage")
    
    if compromised:
        print("[ERROR] Compromised or risky packages found:")
        for item in compromised:
            print(f"  {item['package']}@{item['version']}: {item['reason']}")
        sys.exit(1)
    else:
        print(f"[OK] All {len(packages)} dependencies passed supply chain check")

Run this as part of your CI/CD pipeline with:

python requirements-check.py requirements.txt

Monitored packages

Attestd monitors approximately 27,459 PyPI and 237,601 npm packages, covering LLM SDKs, web frameworks, cloud SDKs, auth libraries, data tooling, and the broader long tail of packages above 500,000 weekly downloads.

Package-specific examples

Common supply chain checks for packages used in AI and data stacks:

LangChain (AI orchestration)

curl "https://api.attestd.io/v1/check?product=langchain&version=0.3.0" \
  -H "Authorization: Bearer YOUR_API_KEY"

Requests (HTTP dependency)

curl "https://api.attestd.io/v1/check?product=requests&version=2.31.0" \
  -H "Authorization: Bearer YOUR_API_KEY"

NumPy (data science)

curl "https://api.attestd.io/v1/check?product=numpy&version=1.24.0" \
  -H "Authorization: Bearer YOUR_API_KEY"

FastAPI (web framework)

curl "https://api.attestd.io/v1/check?product=fastapi&version=0.115.0" \
  -H "Authorization: Bearer YOUR_API_KEY"

Sources

  • registry: manually curated YAML in the Attestd repo. Human-verified; confidence 1.0.
  • osv: OSV.dev malicious-package advisories with IDs prefixed MAL-; confidence 0.95.
  • pypi_yank: versions yanked on PyPI with a security-related yanked_reason; confidence 0.80.
  • npm_deprecation: npm versions with deprecation messages containing targeted attack language such as malicious, backdoor, or compromised. Generic maintenance notices are filtered out; confidence 0.80.

The sources array lists which sources flagged the version (e.g. ["osv", "registry"] when both agree).

Understanding the response

supply_chain_monitored: false

Package is not on the supply chain watchlist. Absence of a supply_chain object (or supply_chain: null) means the same thing: not monitored, not clean. Do not treat this as a safety signal. Check risk_state for CVE status when present.

supply_chain: null

Product is not in supply chain monitoring. For example, infrastructure packages (nginx, postgres) or unsupported PyPI packages. Equivalent to supply_chain_monitored: false.

compromised: false, sources: []

Package is monitored (supply_chain_monitored: true) and no malicious publish was found at the last ingestion. Safe to use. The last_updated field shows when monitoring last ran; check this if you need the absolute latest data.

compromised: true

A malicious publish has been confirmed. Block deployment immediately. Do not auto-upgrade. Treat as an active incident. The compromised_at and removed_at timestamps indicate when the malicious version appeared and when it was pulled from the registry.

sources

Which detection mechanisms flagged the compromise. Multiple sources (e.g., ["registry", "osv"]) mean independent confirmations. A single source like ["pypi_yank"] means the version was yanked with a security annotation.

risk_state, supply_chain, and typosquat

These are independent signals. A package can have risk_state: "none" (no CVEs) but supply_chain.compromised: true (malicious publish). An unsupported name can still return typosquat.detected: true when the name itself is wrong. Always check all three before deploying.

See also Response Field Reference, CI/CD Integration, Account & Portal (for scoped keys), the Python SDK, and the JavaScript SDK (SupplyChainSignal on RiskResult).