system: OPERATIONAL
← back to all hacks
AGENTS CRITICAL NEW

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.

2026-06-15 // 6 min affects: mcp-server-kubernetes<3.6.0

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

  1. Upgrade now. Move to mcp-server-kubernetes 3.6.0 or later, which enforces the same restrictions in tools/call that it always enforced in tools/list. This is the only fix that closes the actual gap.

  2. 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.

  3. 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).

  4. 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 at tools/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.

  5. Log at the execution layer. Record tools/call invocations 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

ItemReferenceDateNotes
GitHub Security AdvisoryGHSA-cr22-wjx7-2w6m2026-05Source advisory, fix in v3.6.0
CVE recordNVD CVE-2026-465192026-05CVSS 8.8 High, CWE-863
Technical analysisNeuralTrust2026-05-19Discovery vs execution write-up
GitLab Advisory DBGLAD2026-05-21Affected: all versions < 3.6.0
News coverageTheHackerWire2026-06-11No public PoC at time of writing
Fixed versionmcp-server-kubernetesv3.6.0Execution-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