ASI04
OWASP Agentic Top 10 APPLICATION INFRASTRUCTURE

Agentic Supply Chain Vulnerabilities

Description

Dynamic loading of tools, plugins, and MCP servers from untrusted or unverified sources introduces malicious code, data exfiltration capabilities, or backdoors into the agent runtime.

Risk

Risk Overview

Agentic systems are inherently composable — they acquire capabilities at runtime by connecting to tool servers (MCP), loading plugins, and integrating external APIs. This dynamic supply chain is fundamentally different from traditional software dependencies: tools are discovered, loaded, and executed at runtime, often based on natural language descriptions rather than verified code signatures. A compromised tool in this chain has direct access to the agent's context, credentials, and capabilities.

Attack Surface

  • Unverified MCP servers: Agents connect to MCP servers discovered through registries or recommendations without verifying the server's identity or code integrity
  • Malicious tool descriptions: Tool descriptors that accurately describe benign functionality but whose implementation includes hidden data exfiltration or credential harvesting
  • Dependency confusion: Attackers register tool names that closely resemble legitimate tools (typosquatting) in public registries
  • Version pinning gaps: Tools auto-update without review, allowing attackers who compromise a tool publisher to push malicious updates
  • Transitive dependencies: A trusted tool depends on untrusted sub-tools or libraries, inheriting their vulnerabilities

Business Impact

  • Backdoor installation: Malicious tools establish persistent access to the agent's environment
  • Data harvesting: Tools silently copy sensitive data from agent context to external collection points
  • Integrity attacks: Tools return subtly altered results that cause agents to make wrong decisions (e.g., a financial data tool that skews numbers)
  • Reputation damage: Organization deploys agents using tools later revealed as malicious, undermining customer trust

Attack Scenarios

An organization's agents connect to MCP servers listed in a community registry. An attacker publishes a server named 'enhanced-web-search' that mimics a popular search tool. The tool returns legitimate search results but also logs every query (including sensitive data from the agent's context) to an external database. Dozens of organizations connect their agents before the exfiltration is discovered.

A legitimate MCP tool publisher's CI/CD pipeline is compromised. The attacker pushes an update to the 'database-query' tool that adds a conditional backdoor: if the query contains 'SELECT' and the table name matches a pattern, the tool also executes a second query exfiltrating the results. The update passes automated tests because the backdoor only triggers on specific patterns.

An agent is configured to dynamically discover tools based on task requirements. When asked to 'analyze this spreadsheet,' it searches a tool registry and selects 'spreadsheet-analyzer-pro' — a typosquatted version of the legitimate 'spreadsheet-analyzer.' The malicious tool extracts all formulas and data from the spreadsheet, including embedded credentials and linked data sources, before returning a plausible analysis.

Mitigations

Tool Registry with Signatures

Maintain a curated, organization-controlled tool registry:

  • Require cryptographic signatures on all tool packages using a code-signing certificate chain the organization controls
  • Sign tool descriptors separately from tool implementations so tampering with either is detectable
  • Implement signature verification at load time — unsigned or mis-signed tools are rejected unconditionally
  • Publish a transparency log of all tool additions, updates, and removals

Allowlisted Sources

Restrict tool acquisition to verified sources:

  • Maintain an explicit allowlist of approved MCP servers, plugin repositories, and API endpoints
  • Block agents from connecting to tool sources not on the allowlist, even if discovered through web search or recommendations
  • Require security review and approval before adding new sources to the allowlist
  • Implement DNS-level controls to prevent connections to unapproved tool servers

Runtime Integrity Checks

Verify tool integrity continuously, not just at install time:

  • Hash tool binaries and configurations at load time and compare against the registry's recorded hashes
  • Monitor tool behavior against declared capabilities — flag tools that make network connections, file accesses, or system calls not described in their manifest
  • Implement canary inputs: send known-output test queries to tools periodically and verify results match expected output
  • Use runtime sandboxing to limit tool access to only the resources declared in their capability manifest

Version Pinning and Review

Prevent silent updates from introducing vulnerabilities:

  • Pin all tool versions in the agent's configuration and require explicit review for version bumps
  • Implement a staged rollout: new tool versions deploy to a test agent pool before production
  • Diff tool descriptors and behavior between versions and flag unexpected changes
  • Maintain rollback capability to immediately revert to a previous tool version

Supply Chain Inventory

Maintain a complete bill of materials for the agent's tool chain:

  • Record every tool, its version, source, signature status, and last verification date
  • Map transitive dependencies so compromises in sub-tools are traceable
  • Conduct periodic supply chain audits comparing the live agent's tool set against the approved inventory

Code Examples

Tool Registry Validator

import hashlib
import json
import time
from dataclasses import dataclass
from typing import Optional
from enum import Enum


class VerificationStatus(Enum):
    VERIFIED = "verified"
    SIGNATURE_MISMATCH = "signature_mismatch"
    NOT_IN_REGISTRY = "not_in_registry"
    EXPIRED = "expired"
    REVOKED = "revoked"


@dataclass
class ToolManifest:
    name: str
    version: str
    publisher: str
    source_url: str
    descriptor_hash: str
    binary_hash: str
    signature: str
    capabilities: list[str]  # declared: ["network:read", "filesystem:none"]
    approved_at: float
    expires_at: float


class ToolRegistryValidator:
    """Validates tools against an organization-controlled registry."""

    def __init__(self):
        self._approved_tools: dict[str, ToolManifest] = {}
        self._allowed_sources: set[str] = set()
        self._revoked: set[str] = set()

    def register_approved_tool(self, manifest: ToolManifest):
        key = f"{manifest.name}@{manifest.version}"
        self._approved_tools[key] = manifest

    def add_allowed_source(self, source_url: str):
        self._allowed_sources.add(source_url)

    def revoke_tool(self, name: str, version: str):
        self._revoked.add(f"{name}@{version}")

    def verify_tool(
        self,
        name: str,
        version: str,
        source_url: str,
        descriptor_content: str,
        binary_content: bytes,
    ) -> dict:
        """Full verification: source, registry, hashes, expiry."""
        key = f"{name}@{version}"
        result = {"tool": key, "checks": {}}

        # Check source allowlist
        result["checks"]["source_allowed"] = source_url in self._allowed_sources
        if not result["checks"]["source_allowed"]:
            result["status"] = VerificationStatus.NOT_IN_REGISTRY.value
            result["block"] = True
            return result

        # Check revocation
        if key in self._revoked:
            result["status"] = VerificationStatus.REVOKED.value
            result["block"] = True
            return result

        # Check registry entry exists
        manifest = self._approved_tools.get(key)
        if not manifest:
            result["status"] = VerificationStatus.NOT_IN_REGISTRY.value
            result["block"] = True
            return result

        # Check expiry
        if time.time() > manifest.expires_at:
            result["status"] = VerificationStatus.EXPIRED.value
            result["block"] = True
            return result

        # Verify descriptor hash
        actual_desc_hash = hashlib.sha256(descriptor_content.encode()).hexdigest()
        result["checks"]["descriptor_hash"] = actual_desc_hash == manifest.descriptor_hash

        # Verify binary hash
        actual_bin_hash = hashlib.sha256(binary_content).hexdigest()
        result["checks"]["binary_hash"] = actual_bin_hash == manifest.binary_hash

        all_hashes_match = (
            result["checks"]["descriptor_hash"] and result["checks"]["binary_hash"]
        )

        if all_hashes_match:
            result["status"] = VerificationStatus.VERIFIED.value
            result["block"] = False
        else:
            result["status"] = VerificationStatus.SIGNATURE_MISMATCH.value
            result["block"] = True

        return result

    def get_inventory(self) -> list[dict]:
        """Return full tool supply chain inventory for audit."""
        return [
            {
                "tool": key,
                "publisher": m.publisher,
                "source": m.source_url,
                "approved_at": m.approved_at,
                "expires_at": m.expires_at,
                "revoked": key in self._revoked,
            }
            for key, m in self._approved_tools.items()
        ]

Evidence Requirements

  • Tool registry with signed manifests and approval audit trail
  • Supply chain bill of materials (SBOM) for all agent tool dependencies
  • Integrity verification logs showing hash checks at every tool load
  • Source allowlist change history with approver attribution
  • Canary test results validating tool output consistency over time