Threat Model¶
KiCad MCP Pro runs local automation against KiCad projects on behalf of an MCP client (an AI agent). This document maps the data flows, the trust boundaries, and — for each attack surface — the control and the test that verifies it. Controls that are only recommended (not yet implemented) are called out as residual risks.
Data flow and trust boundaries¶
MCP client (agent) --tool calls--> KiCad MCP Pro
| | |
argv list (no shell) ---------+ | +--- file I/O (workspace-confined)
kicad-cli subprocess |
+--- KiCad IPC (kipy) to a running KiCad
- The MCP client is not automatically trusted. Tool arguments (paths, names, net specs) are attacker-controllable. Use the smallest practical profile and the least-privileged operating mode.
- The active project directory is the trusted I/O scope. Reads/writes are confined to it (or the configured workspace root).
kicad-cliand the KiCad IPC endpoint are trusted local components, configured by the operator (KICAD_MCP_KICAD_CLI, socket env). Whoever sets those env vars is in the trust base.- HTTP/bridge transports are off by default and local-only unless explicitly exposed.
Surfaces, controls, and verification¶
1. kicad-cli subprocess (command/argument injection)¶
- Threat: a tool argument (an output path, layer, variant, or net name) is interpreted as a shell command or an injected CLI flag.
- Control: the server never invokes a shell. Every
kicad-clicall issubprocess.run([binary, *args], shell=False)with the operator-configured binary, so arguments are passed as discrete argv elements and are never shell-interpreted. There is noos.system,os.popen,subprocess.getoutput, orshell=Trueanywhere insrc/. - Verification:
tests/unit/test_security_controls.py::test_no_shell_execution_anywhere_in_srcscans the whole source tree, and::test_kicad_cli_is_invoked_as_argv_listasserts an injection-looking argument ("; rm -rf /") reaches the CLI as a single literal argv element withshell=False.
2. Path traversal¶
- Threat: a client supplies
../../etc/passwdor an absolute/UNC path to read or write outside the project. - Control:
path_safety.resolve_under/config.resolve_within_projectresolve every path under the project root and raiseUnsafePathErrorif it escapes;relative_subpathrejects absolute paths and..for output subdirectories;reject_foreign_windows_pathblocks Windows drive/UNC paths on POSIX hosts. - Verification:
tests/unit/test_security_controls.py(traversal/absolute rejection) and the broadertests/unit/test_path_safety.pycorpus (UNC, symlinks, spaces/unicode, long paths).
3. HTTP transport (auth, CORS, origin)¶
- Threat: a local web page or a leaked token drives the tool surface over HTTP.
- Control: Streamable HTTP is opt-in. When enabled,
_DashboardAuthMiddlewareenforces the optional bearer token (_StaticTokenVerifier,required_scopes=["mcp"]),_OriginValidationMiddlewarechecks theOrigin, andCORSMiddlewarerestricts origins to the configured allow-list. The legacy HTTP+SSE routes are disabled by default. - Verification: web-route and server-startup tests under
tests/unit/exercise the auth/CORS/origin paths; transport contract is checked bytest_mcp_protocol_contract.py.
4. Bridge daemon (remote pairing)¶
- Threat: an attacker brute-forces the bridge pairing code or floods the daemon.
- Control: the bridge is opt-in, binds locally, and requires a per-session pairing
code (
secrets.token_hex). Inbound messages are now rate-limited by a token bucket (TokenBucketinbridge.py): a general per-daemon budget blunts floods, and a much stricter pairing budget (small burst, then one attempt every five seconds) makes brute-forcing the 24-bit code infeasible. Exceeding either budget returns a JSON-RPC rate-limit error (-32004) without doing any work. Covered bytests/unit/test_bridge_rate_limit.py. - Residual risk: the pairing code is still short (24-bit). Rate-limiting makes
online brute force impractical, but use a longer explicit
--codeand do not expose the bridge port to untrusted networks until a longer default code ships.
Supply chain and release integrity¶
- Dependabot/Renovate, CodeQL, Gitleaks, Trivy, Hadolint, Bandit, and pip-audit cover the main automated scan layers.
- Release artifacts are built by GitHub Actions and accompanied by an SBOM, Sigstore signatures, checksums, and GitHub artifact attestations, so an artifact is traceable to its source and workflow.
Reporting¶
Report vulnerabilities privately through GitHub Security Advisories.