Imported from msaad00/cloud-ai-security-skills (
skills/detection/detect-mcp-plugin-supply-chain/SKILL.md). Install upstream withnpx skills add msaad00/cloud-ai-security-skills --skill detect-mcp-plugin-supply-chain. Copyright stays with the author (Apache-2.0).
detect-mcp-plugin-supply-chain
Attack pattern
An MCP server's tools/list response can advertise a tool whose JSON
inputSchema itself reaches outside the trust boundary — a $ref to a
remote schema URL, a default value containing a remote URL, or a
description that promotes a remote endpoint. When the agent or its
infrastructure resolves that schema (for input validation, for documentation
rendering, for code generation, or for human review), it fetches code or
content from a host the operator never authorized.
This is the plugin / tool supply-chain flavour of OWASP LLM Top 10 LLM05 Supply Chain. The poisoned link is not the tool's implementation — it is the tool's declaration.
Detection logic
Walk MCP Application Activity events from ingest-mcp-proxy-ocsf. For each
tools/list response, recursively walk every inputSchema node and harvest
URL-shaped strings from $ref, default, description, and string values
nested inside oneOf / anyOf / allOf / properties / items.
Compare each URL's hostname against MCP_PLUGIN_ALLOWED_HOSTS (a comma-
separated list, default empty). When the env var is empty, the detector
fails open and prints a single stderr warning so operators notice they shipped
without an allowlist. Each (session, host) pair fires once per session.
allowlist = MCP_PLUGIN_ALLOWED_HOSTS (default empty → warn + fail-open)
walk(input_schema) → collected_urls
for url in collected_urls:
host = urlparse(url).hostname
if host and host not in allowlist:
emit finding once per (session, host)
Use when the user mentions LLM05, plugin supply chain, untrusted schema, or
remote $ref resolution. Do NOT use as a content-security policy enforcer.
Output contract
One OCSF 1.8 Detection Finding (class 2004) per disallowed (session, host)
pair. With --output-format native the skill emits the repo-owned native
projection.
OCSF output populates:
finding_info.types[] = ["mcp-plugin-supply-chain", "llm-supply-chain"]finding_info.attacks[]— MITRE ATT&CKT1195.001Compromise Software Supply Chain (the tool's declaration is the software the agent ingests) and MITRE ATLASAML.T0010AI Supply Chain Compromise (Initial Access).- deterministic
finding_info.uid. observables[]— session uid, tool name, host, source field ($ref/default/description).
Usage
MCP_PLUGIN_ALLOWED_HOSTS="schema.openai.com,registry.modelcontextprotocol.io" \
python ../../ingestion/ingest-mcp-proxy-ocsf/src/ingest.py mcp-proxy.jsonl \
| python src/detect.py \
> plugin-supply-chain-findings.ocsf.jsonl