Skip to content

Security: saagpatel/MCPAudit

SECURITY.md

Security Policy

Experimental fixture enforcement

The enforcement-fixture commands are a program-owned synthetic compatibility harness, not a production enforcement gateway. They are exact-pinned to agent-governance-toolkit-core==4.1.0, reject unsupported translation, and may write only to explicitly named fixture-state directories whose basename starts with mcpaudit-enforcement-fixture- and whose ownership marker is valid. Managed state and temporary paths may not be symlinks. Apply and rollback use a persistent program-owned fixture lock; processes that ignore that lock remain outside the harness guarantee. Do not point these commands at normal MCP configuration or treat their proof as authority for a real server. See docs/EVIDENCE-ENFORCEMENT-THREAT-MODEL.md.

Experimental Agent UI contract audit

The agent-ui commands accept only program-owned synthetic MCP Apps/OpenAI metadata fixtures and A2UI v0.9 JSONL fixtures. They do not read widget HTML or JavaScript, contact an MCP server, call tools, fetch resources or catalogs, authenticate, install plugins, or process real user data. Symlink inputs are rejected; fixture bytes are read from one identity-checked descriptor with a post-read size bound; report outputs cannot alias each other or the input; and no-force output commit is descriptor-bound and atomic create-if-absent. Generic JSON nesting and A2UI traversal are bounded; duplicate JSON keys and same-update component IDs are invalid; missing or unknown approval provenance, out-of-domain evidence states, contradictory standard/OpenAI metadata, malformed HTTPS authorities, and unsupported protocol or host constructs produce UNKNOWN. Supported contradictions already established in an A2UI lifecycle cannot be erased by replacement or surface deletion.

A passing fixture report proves only internal consistency for the supported static contract. It does not prove host sandboxing, CSP enforcement, transport integrity, authorization, renderer behavior, interoperability, or real-workflow safety. See docs/AGENT-UI-CONTRACT-AUDITOR.md.

MCPAudit is itself a security tool. This document covers two distinct concerns:

  1. How to report vulnerabilities in MCPAudit (the auditor tool itself)
  2. MCPAudit's threat model — what it does and does not protect against

Reporting a vulnerability in MCPAudit

Please do not open a public GitHub issue for security vulnerabilities.

Use GitHub Security Advisories to report privately. This allows coordinated disclosure before a fix is published.

Include in your report:

  • A description of the vulnerability and its impact
  • Steps to reproduce (config snippet, command invoked, observed vs. expected behavior)
  • The MCPAudit version (mcp-audit --version) and Python version
  • Whether the vulnerability requires a malicious MCP config to trigger, or if it can be triggered by a benign config

We aim to acknowledge reports within 72 hours and provide an initial assessment within 7 days.


Threat model

Understanding what MCPAudit does and does not do is important for evaluating its security properties.

What MCPAudit does

  • Reads local MCP server configuration files (e.g., claude_desktop_config.json, Cursor/VS Code equivalents)
  • During a standard mcp-audit scan, connects to configured MCP servers to enumerate declared tools and schemas
  • For stdio servers, starts the configured server command with timeout guards; for HTTP/SSE servers, opens a client connection to the configured URL
  • Scores permission risk and flags potential prompt injection patterns in tool, prompt, and resource text
  • Detects schema drift across capability declarations with mcp-audit pin and scan --pin-check
  • Supports --skip-connect for config-only review when you do not want MCPAudit to spawn or connect to audited servers

What MCPAudit does not do

  • MCPAudit does not call audited MCP tools; it only enumerates tool metadata
  • MCPAudit does not exfiltrate config data
  • MCPAudit does not modify MCP configs during scans
  • mcp-audit serve --install is an explicit setup command that writes an MCPAudit server entry to detected Claude config files
  • MCPAudit does not transmit data to a third-party API unless optional LLM analysis is explicitly requested
  • The watch mode monitors local config file changes using watchfiles; each re-scan follows the selected scan mode, so use watch --skip-connect for config-only watching

Trust boundary

MCPAudit reads config files from your local filesystem. It trusts that the config files it reads are your own. A scenario where an attacker can write to your MCP config directory is outside MCPAudit's threat model — at that point, the attacker already has local file write access.

Prompt injection in MCPAudit's own output

MCPAudit parses and displays content from MCP server configs and MCP server metadata, including tool descriptions that may be attacker-controlled. If a malicious MCP server is installed, its tool descriptions could contain content designed to manipulate AI assistants that consume MCPAudit's output. MCPAudit does not sanitize or escape tool description content before rendering it. Users should treat MCPAudit's output as untrusted data when piping it to AI systems.

LLM mode (--llm-analysis)

When the optional anthropic dependency is installed and --llm-analysis is used, MCPAudit sends selected tool names, descriptions, and parameter names to the Anthropic API for permission classification. In this mode, content from audited MCP server configs is transmitted over the network to a third-party service. Do not use --llm-analysis if your MCP configs contain sensitive information (API keys in args, internal hostnames, etc.).

Proof Before Action

proof-before-action compares a declared boundary with observations from a disposable Docker execution and emits a versioned offline evidence capsule. Verification binds the staged subject, producer revision, deterministic report projection, and an independently supplied root hash.

Its result is evidence about the declared and observed surfaces, not a general sandbox or safety guarantee. Unknown, stale, masked, unmatched, incomplete, unobservable, dirty, or authority-unverified evidence remains non-authoritative and cannot become a passing safety claim.

The observation-v2 observer emits stable machine-readable unknown receipts for transient create-delete/write-restore attempts, no-delta SQLite queries or transactions, requested network destinations or hostnames, and filesystem or abstract Unix-domain sockets. It does not prove those surfaces observed, blocked, or safe. Missing or unresolved attempt receipts are non-passing, and v2 does not accept a producer's unsupported observed/blocked claim as proof.

Historical observation/capsule/index v1 bundles remain verifiable under their original comparison and offline-report semantics. They are not reinterpreted as v2 attempt evidence, and new observations cannot emit the v1 contract.

The observer also does not prove host-kernel isolation or safety outside the declared surfaces. A valid capsule does not prove that an operator independently obtained the root hash; callers must supply and protect that trust input themselves. See docs/SECURITY-REVIEW.md for the detailed boundary.


Supported versions

Version Supported
2.x Yes
1.x Security fixes only
0.x and earlier No
Pre-release builds (alpha/beta/RC) No

Acknowledgements

We appreciate responsible disclosure. Reporters of valid vulnerabilities will be credited in the release notes unless they prefer to remain anonymous.

There aren't any published security advisories