Catch hallucinated dependencies before your agent installs them
AI agents invent package names and cannot reason about real-world exploitation risk from CVSS numbers alone. They need deterministic signals for CVE risk, supply chain compromise, and package name integrity, including when the model hallucinated the dependency name itself.
An agent that installs whatever the model suggests is already past the point where advisory text helps. The failure modes are concrete.
- →Models invent package names that look plausible. A typosquat or hallucinated name can land malware before any CVE exists for it.
- →CVSS scores require interpretation. An agent branching on "severity: HIGH" still has to decide whether that means block, warn, or proceed.
- →CVE databases list vulnerabilities. They do not return a single field that means "do not install this version."
- →Supply chain compromises often have no CVE at publish time. A CVE-only tool returns clean while the package is already compromised.
Map these fields directly into tool return values. No scoring step between the API and the agent.
| Field | Meaning for this use case |
|---|---|
risk_state | Categorical verdict: none, low, medium, high, or critical. Branch on critical and high without reading CVSS. |
actively_exploited | True when Attestd has confirmed active exploitation for this product@version. Prefer block over warn when true. |
supply_chain.compromised | True for confirmed malicious publishes, including packages with no CVE. Independent of risk_state. |
typosquat | Returned when the product name is unsupported and resembles a known package. kind hallucination covers model-invented names. resembles is the package to prefer instead. |
fixed_version | Upgrade target when a patch exists. Give the agent a concrete next version, not just a block. |
curl "https://api.attestd.io/v1/check?product=redis&version=6.0.9" \
-H "Authorization: Bearer $ATTESTD_API_KEY"import os
import attestd
client = attestd.Client(api_key=os.environ["ATTESTD_API_KEY"])
def check_software_safety(product: str, version: str) -> dict:
"""
Tool available to the agent for checking software risk state.
Returns structured, deterministic data. No interpretation needed.
"""
try:
risk = client.check(product, version)
except attestd.AttestdUnsupportedProductError as e:
if e.typosquat and e.typosquat.detected:
return {
"safe_to_deploy": False,
"action": "block_deployment",
"typosquat_kind": e.typosquat.kind,
"prefer": e.typosquat.resembles,
}
return {"safe_to_deploy": False, "action": "unknown_risk"}
if risk.supply_chain and risk.supply_chain.compromised:
return {
"safe_to_deploy": False,
"action": "block_deployment",
"reason": "supply_chain_compromised",
}
return {
"safe_to_deploy": risk.risk_state == "none",
"risk_state": risk.risk_state,
"actively_exploited": risk.actively_exploited,
"recommended_version": risk.fixed_version or version,
"action": (
"block_deployment"
if risk.risk_state == "critical"
else "warn"
if risk.risk_state == "high"
else "proceed"
),
}
# Agent receives structured output. No ambiguity in the signal
result = check_software_safety("redis", "6.0.9")
# result["action"] → "block_deployment" | "warn" | "proceed"- 01
Expose /v1/check as an agent tool
Wrap Attestd in a function the agent can call before install or deploy. Keep the tool schema narrow: product, version, and a small action enum.
- 02
Require a check before package install
In the agent loop, refuse to run pip install, npm install, or equivalent until check_software_safety returns proceed. Treat unknown_risk and typosquat as block.
- 03
Pass the corrected name back to the model
When typosquat.kind is hallucination, return resembles to the agent so it can retry with the real package instead of inventing another name.
Agents make deployment decisions with ground truth, not guesses.
Attestd returns the same answer every time for the same inputs. There is no probability and no scoring interpretation. When an agent invents a package name, typosquat.kind hallucination tells it what to install instead. An agent calling Attestd either gets proceed, block, or a corrected package name.
The first documented fully autonomous AI ransomware attack used a known Langflow CVE. An agent exploited CVE-2025-3248, pivoted through the Docker socket to the host, and encrypted production data with no human in the loop.
The same operator returned with a compiled Go payload targeting AI model files. 19 days later, ENCFORGE targeted roughly 180 file types including model weights, vector indexes, and training datasets.
NVD published CVE-2025-3248 more than a year before either attack. The signal existed as raw CVE data. What was missing was a synthesized condition an agent could act on. That is the machine-speed argument: autonomous attackers and autonomous installers operate in the same time domain. A human-readable advisory digest does not.