CVE-2026-46519: when an MCP server filters tools at display but not at execution
mcp-server-kubernetes enforced its read-only and allow-list controls only in tools/list, never in tools/call. Any client that knew a tool name could run it. A clean lesson in presentation-layer vs execution-layer authorization.
What is this?
CVE-2026-46519 is an access-control bypass in mcp-server-kubernetes, a widely used Model Context Protocol (MCP) server that lets AI agents drive a Kubernetes cluster. The bug was published through GitHub Security Advisory GHSA-cr22-wjx7-2w6m and tracked in the NVD with a CVSS 3.1 score of 8.8 (High) and a weakness class of CWE-863 (Incorrect Authorization). NeuralTrust published a technical breakdown on May 19, 2026, the GitLab Advisory Database recorded it on May 21, 2026, and it was patched in version 3.6.0.
The package is not niche: it ships roughly 20,000 npm downloads a week and is the standard bridge organisations reach for when they want an agent to read or manage cluster state. That reach is what turns a small logic gap into a cluster-wide problem.
How it works
The server exposes three environment variables documented as access controls: ALLOW_ONLY_READONLY_TOOLS, ALLOW_ONLY_NON_DESTRUCTIVE_TOOLS, and ALLOWED_TOOLS (an explicit allow-list of tool names). An operator who sets ALLOW_ONLY_READONLY_TOOLS=true expects a connected agent to be able to look but never touch.
MCP splits tool use into two operations: tools/list (discovery — “what can I do here?”) and tools/call (execution — “do this now”). The flaw is that the restriction logic lived only in tools/list.
Layer Request Restriction checked?
--------------------- ------------- --------------------
Discovery (display) tools/list YES -> filtered list returned
Execution (action) tools/call NO -> any named tool runs
When a read-only agent asked for its tool list, the server correctly returned a filtered set — kubectl_get, kubectl_describe and similar — with destructive tools such as kubectl_delete, exec_in_pod and kubectl_generic absent. But the tools/call handler never re-checked the policy. Any client that already knew a restricted tool name — and those names are published in the project’s own README — could call it directly. No injection, no exploit chain, no privilege escalation primitive: just a request for a tool the UI pretended did not exist.
agent view: [ kubectl_get, kubectl_describe ] # what tools/list shows
actual reach: tools/call -> kubectl_delete([REDACTED]) # what the server will do
In the language of the advisory, the controls were “effectively cosmetic”: they hid the buttons but left the wiring connected.
Why it matters
The real-world blast radius depends on one thing the CVE cannot fix for you: the privileges of the Kubernetes Service Account the MCP server runs under. The bypass never grants the server more rights than it already holds — but in development and staging clusters it is common to run such a server with cluster-admin “for convenience.” In that configuration, anyone who can reach the HTTP endpoint inherits the full power of that account: delete namespaces, rewrite deployments, read every Secret.
Two themes make this worth your attention beyond a single npm package. First, it is a textbook case of presentation-layer vs execution-layer authorization — the same mistake as a web app that hides an admin button but leaves the /admin route unauthenticated. Agent tooling, being young, is reinventing these failures. Second, the threat model for MCP is not only “malicious prompt.” A misconfigured-but-honest agent, or any client on the same network, trips this with a normal request. As of disclosure there was no public proof-of-concept, but the bug requires no special skill to reproduce.
Defenses
-
Upgrade now. Move to
mcp-server-kubernetes3.6.0 or later, which enforces the same restrictions intools/callthat it always enforced intools/list. This is the only fix that closes the actual gap. -
Treat RBAC as the real boundary. Application-level “read-only mode” is a convenience, not a control. Give the server’s Service Account the least privilege it genuinely needs. If an agent only watches pods, its SA must not be able to delete them — regardless of any in-app toggle.
-
Don’t expose the endpoint. Keep the MCP server off the public internet, reachable only from trusted networks or via a secure tunnel, and require strong authentication (the project supports
MCP_AUTH_TOKEN). -
Audit your own tool servers for the same shape. If you build or run MCP servers, verify that every authorization decision is enforced at
tools/call, not just attools/list. Discovery filtering is UX; execution filtering is security. A quick test: call a tool that should be hidden and confirm the server refuses it. -
Log at the execution layer. Record
tools/callinvocations with tool name and outcome, so a request for a “filtered-out” tool is visible after the fact even if a control is missing.
Status
| Item | Reference | Date | Notes |
|---|---|---|---|
| GitHub Security Advisory | GHSA-cr22-wjx7-2w6m | 2026-05 | Source advisory, fix in v3.6.0 |
| CVE record | NVD CVE-2026-46519 | 2026-05 | CVSS 8.8 High, CWE-863 |
| Technical analysis | NeuralTrust | 2026-05-19 | Discovery vs execution write-up |
| GitLab Advisory DB | GLAD | 2026-05-21 | Affected: all versions < 3.6.0 |
| News coverage | TheHackerWire | 2026-06-11 | No public PoC at time of writing |
| Fixed version | mcp-server-kubernetes | v3.6.0 | Execution-layer checks added |
The headline is not “Kubernetes MCP is dangerous.” It is that authorization has to be enforced where the action happens, not where the menu is drawn — a rule the web learned twenty years ago and that agent tooling is now relearning, one CVE at a time.
Sources
- → https://github.com/Flux159/mcp-server-kubernetes/security/advisories/GHSA-cr22-wjx7-2w6m
- → https://nvd.nist.gov/vuln/detail/CVE-2026-46519
- → https://advisories.gitlab.com/npm/mcp-server-kubernetes/CVE-2026-46519/
- → https://neuraltrust.ai/blog/kubernetes-mcp
- → https://www.thehackerwire.com/mcp-server-kubernetes-access-control-bypass/