Most of the MCP K8s tools carefully wrap resource names in quote(name, safe='') and validate them with _valid_k8s_name. patch_workload does neither — it drops workload_name straight into the request path.
mcp_servers/k8s_mcp_server.py:1288
try:
result = _k8s_patch(
f"/apis/apps/v1/namespaces/{namespace}/{resource}/{workload_name}",
patch,
)
check_namespace() only validates the namespace segment against ALLOWED_NAMESPACES. workload_name is attacker-influenced (it flows in from alert payloads / API callers) and is neither validated nor URL-encoded. restart_deployment and scale_deployment share the same raw-interpolation pattern.
Failure scenario
With ALLOWED_NAMESPACES=default, a caller invokes:
patch_workload(
namespace="default",
workload_type="deployment",
workload_name="../../../../apis/apps/v1/namespaces/kube-system/deployments/coredns",
patch={...},
)
check_namespace("default") passes. The resulting path collapses (via .. traversal) to a kube-system/deployments/coredns PATCH — a namespace the guard was explicitly meant to exclude. A name containing ?/& can likewise inject query parameters. The very control the platform relies on to bound blast radius is defeated by an unencoded path segment.
Suggested fix
Match the hardened peers (recreate_pod, evict_pod, cordon_node): validate with _valid_k8s_name(workload_name) and reject on failure, then wrap every dynamic segment in quote(..., safe=''):
if not _valid_k8s_name(workload_name):
return {"status": "error", "message": "invalid workload name"}
result = _k8s_patch(
f"/apis/apps/v1/namespaces/{quote(namespace, safe='')}/{resource}/{quote(workload_name, safe='')}",
patch,
)
Apply the same treatment to restart_deployment (line ~1097) and scale_deployment (line ~1136).
Most of the MCP K8s tools carefully wrap resource names in
quote(name, safe='')and validate them with_valid_k8s_name.patch_workloaddoes neither — it dropsworkload_namestraight into the request path.mcp_servers/k8s_mcp_server.py:1288check_namespace()only validates thenamespacesegment againstALLOWED_NAMESPACES.workload_nameis attacker-influenced (it flows in from alert payloads / API callers) and is neither validated nor URL-encoded.restart_deploymentandscale_deploymentshare the same raw-interpolation pattern.Failure scenario
With
ALLOWED_NAMESPACES=default, a caller invokes:check_namespace("default")passes. The resulting path collapses (via..traversal) to akube-system/deployments/corednsPATCH — a namespace the guard was explicitly meant to exclude. A name containing?/&can likewise inject query parameters. The very control the platform relies on to bound blast radius is defeated by an unencoded path segment.Suggested fix
Match the hardened peers (
recreate_pod,evict_pod,cordon_node): validate with_valid_k8s_name(workload_name)and reject on failure, then wrap every dynamic segment inquote(..., safe=''):Apply the same treatment to
restart_deployment(line ~1097) andscale_deployment(line ~1136).