body{font-family:-apple-system,BlinkMacSystemFont,’Segoe UI‘,Roboto,sans-serif;max-width:900px;margin:0 auto;padding:2rem;line-height:1.7;color:#1a1a1a}
h1{color:#1a1a1a;border-bottom:3px solid #6366f1;padding-bottom:.5rem}
h2{color:#334155;margin-top:2rem}
code{background:#f1f5f9;padding:.2rem .5rem;border-radius:4px;font-size:.9em}
pre{background:#1e293b;color:#e2e8f0;padding:1.5rem;border-radius:8px;overflow-x:auto;font-size:.9em}
blockquote{border-left:4px solid #6366f1;padding-left:1rem;color:#64748b;font-style:italic}
table{border-collapse:collapse;width:100%;margin:1rem 0}
th,td{border:1px solid #e2e8f0;padding:.75rem;text-align:left}
th{background:#f8fafc}
.tag{display:inline-block;background:#e0e7ff;color:#4338ca;padding:.2rem .6rem;border-radius:999px;font-size:.85em;margin-right:.5rem}
AI Agent Supply Chain Security
Reviewed: June 4, 2026
Published: May 26, 2026 | Reading time: 10 min | AI Security Supply Chain Model Provenance
An AI agent is only as trustworthy as its least trusted dependency. Models, prompts, tools, and data sources form a supply chain â and every link is a potential attack surface.
The AI Agent Supply Chain
| Component | Risk | Attack Vector |
|---|---|---|
| Foundation Models | Backdoors, data poisoning | Compromised model registry, fine-tuned malicious variants |
| System Prompts | Prompt poisoning | Modified prompts in prompt libraries, injected instructions |
| Tools/Plugins | Malicious tool behavior | Typosquatting, compromised tool marketplaces |
| Knowledge Bases | Data poisoning | RAG corpus manipulation, poisoned embeddings |
| Dependencies | Traditional supply chain | Compromised PyPI/npm packages, dependency confusion |
Model Provenance Verification
import hashlib
import json
class ModelProvenance:
"""Track and verify model origins and integrity."""
def __init__(self, trusted_registry: str):
self.registry = trusted_registry
self.known_models = {} # model_id -> expected_hash
def register_model(self, model_id: str, source: str,
sha256_hash: str, metadata: dict):
"""Register a trusted model with its provenance."""
self.known_models[model_id] = {
"source": source,
"sha256": sha256_hash,
"registered_at": datetime.utcnow().isoformat(),
"metadata": metadata
}
def verify_model(self, model_id: str, model_path: str) -> dict:
"""Verify a model file matches its registered hash."""
if model_id not in self.known_models:
return {"verified": False, "reason": "Model not in trusted registry"}
expected = self.known_models[model_id]["sha256"]
actual = self._hash_file(model_path)
return {
"verified": expected == actual,
"model_id": model_id,
"expected_hash": expected[:16] + "...",
"actual_hash": actual[:16] + "...",
"source": self.known_models[model_id]["source"]
}
def _hash_file(self, path: str) -> str:
sha256 = hashlib.sha256()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(8192), b''):
sha256.update(chunk)
return sha256.hexdigest()
def generate_sbom(self) -> dict:
"""Generate Software Bill of Materials for all registered models."""
return {
"spec_version": "1.0",
"generated_at": datetime.utcnow().isoformat(),
"components": [
{
"name": mid,
"version": info["metadata"].get("version", "unknown"),
"source": info["source"],
"hash_sha256": info["sha256"],
}
for mid, info in self.known_models.items()
]
}
Prompt Supply Chain Security
class PromptIntegrityChecker:
"""Verify prompt templates haven't been tampered with."""
def __init__(self):
self.approved_prompts = {} # prompt_name -> approved_hash
def approve_prompt(self, name: str, content: str, approved_by: str):
prompt_hash = hashlib.sha256(content.encode()).hexdigest()
self.approved_prompts[name] = {
"hash": prompt_hash,
"approved_by": approved_by,
"approved_at": datetime.utcnow().isoformat()
}
def verify_before_use(self, name: str, content: str) -> dict:
"""Check prompt against approved version before sending to LLM."""
if name not in self.approved_prompts:
return {"safe": False, "reason": f"Prompt '{name}' not in approved registry"}
current_hash = hashlib.sha256(content.encode()).hexdigest()
expected = self.approved_prompts[name]["hash"]
if current_hash != expected:
return {
"safe": False,
"reason": "PROMPT MODIFICATION DETECTED â possible injection",
"action": "Block and alert security team"
}
return {"safe": True}
def scan_for_injection(self, prompt_content: str) -> list[str]:
"""Scan prompt content for potential injection markers."""
suspicious = []
# Check for hidden instructions
if any(marker in prompt_content.lower() for marker in
['ignore previous', 'you are now', 'system:', '### system']):
suspicious.append("Contains potential injection markers")
# Check for unusual Unicode (zero-width characters, RTL overrides)
import unicodedata
for char in prompt_content:
if unicodedata.category(char) in ('Cf', 'Co', 'Cn'):
suspicious.append(f"Suspicious Unicode character: U+{ord(char):04X}")
return suspicious
Tool and Plugin Verification
class ToolVerification:
"""Verify tool/plugin integrity before loading."""
TRUSTED_SOURCES = [
"https://api.openai.com",
"https://api.anthropic.com",
"https://api.datagate.ch",
]
def verify_tool(self, tool_config: dict) -> dict:
issues = []
# Check source URL
url = tool_config.get("url", "")
if not any(url.startswith(trusted) for trusted in self.TRUSTED_SOURCES):
issues.append(f"Tool from untrusted source: {url}")
# Check for overly broad permissions
scopes = tool_config.get("scopes", [])
dangerous_scopes = {"admin", "delete_all", "write_all", "*"}
if dangerous_scopes.intersection(set(scopes)):
issues.append(f"Overly broad scopes requested: {dangerous_scopes.intersection(set(scopes))}")
# Verify tool signature if available
if "signature" in tool_config:
if not self._verify_signature(tool_config):
issues.append("Tool signature verification failed")
return {
"trusted": len(issues) == 0,
"issues": issues,
"tool_name": tool_config.get("name", "unknown")
}
def _verify_signature(self, tool_config: dict) -> bool:
# In production: verify cryptographic signature
return True
RAG Corpus Integrity
class RAGCorpusMonitor:
"""Monitor RAG knowledge base for poisoning attempts."""
def __init__(self):
self.baseline_stats = {}
def check_corpus_health(self, documents: list[dict]) -> dict:
"""Analyze document corpus for anomalies."""
issues = []
# Check for duplicate content (possible poisoning via flooding)
content_hashes = [hashlib.sha256(d["content"].encode()).hexdigest()
for d in documents]
unique_ratio = len(set(content_hashes)) / len(content_hashes)
if unique_ratio 0.5:
issues.append(f"Source '{src}' dominates corpus ({count}/{total} docs)")
return {
"healthy": len(issues) == 0,
"total_documents": total,
"unique_ratio": unique_ratio,
"issues": issues
}
Supply Chain Security Checklist
| Check | Priority |
|---|---|
| All models registered with hash verification | Critical |
| Prompt templates integrity-checked before use | Critical |
| Tool whitelist enforced, signatures verified | High |
| RAG corpus monitored for poisoning | High |
| Dependencies pinned and hash-locked | High |
| SBOM generated for all AI components | Medium |
| Regular supply chain audit scheduled | Medium |
| Incident response plan for compromised dependencies | High |
Part of the AI Agent Security series on DataGate.ch.
