mcp-server-kubernetes 3.6.2 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Validate user-supplied kubectl flags and args. Throws an McpError if any
3
+ * dangerous flag is present and the unsafe-flags escape hatch is not set.
4
+ *
5
+ * The check covers:
6
+ * - keys of the `flags` object (e.g. { server: "..." })
7
+ * - tokens in the `args` array, in both joined ("--server=x") and split
8
+ * ("--server", "x") forms, plus short aliases ("-s").
9
+ */
10
+ export declare function assertNoDangerousFlags(flags?: Record<string, unknown>, args?: string[]): void;
@@ -0,0 +1,97 @@
1
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
2
+ // Flags that would let a caller redirect kubectl to a different API server,
3
+ // substitute credentials, or impersonate another identity. Allowing any of
4
+ // these to flow in from tool inputs lets an attacker who can influence the
5
+ // LLM's tool arguments (e.g. via indirect prompt injection in pod logs)
6
+ // exfiltrate the operator's bearer token to an attacker-controlled host.
7
+ //
8
+ // Names are stored in canonical (long-form) kebab-case, without the leading
9
+ // "--". Short aliases that have the same effect are listed in SHORT_ALIASES.
10
+ const DANGEROUS_FLAGS = new Set([
11
+ // Target / endpoint overrides
12
+ "server",
13
+ "kubeconfig",
14
+ "cluster",
15
+ "context",
16
+ "user",
17
+ "tls-server-name",
18
+ // TLS bypass
19
+ "insecure-skip-tls-verify",
20
+ "certificate-authority",
21
+ "client-certificate",
22
+ "client-key",
23
+ // Credential overrides
24
+ "token",
25
+ "username",
26
+ "password",
27
+ "auth-provider",
28
+ "auth-provider-arg",
29
+ "exec-command",
30
+ "exec-arg",
31
+ "exec-api-version",
32
+ "exec-env",
33
+ // Identity impersonation
34
+ "as",
35
+ "as-group",
36
+ "as-uid",
37
+ ]);
38
+ const SHORT_ALIASES = new Set([
39
+ "s", // -s is an alias for --server
40
+ ]);
41
+ function isUnsafeFlagsAllowed() {
42
+ return process.env.ALLOW_KUBECTL_UNSAFE_FLAGS === "true";
43
+ }
44
+ function normalizeFlagName(raw) {
45
+ // Strip leading dashes; drop "=value" suffix; lowercase.
46
+ let name = raw.replace(/^-+/, "");
47
+ const eq = name.indexOf("=");
48
+ if (eq !== -1)
49
+ name = name.slice(0, eq);
50
+ return name.toLowerCase();
51
+ }
52
+ function isDangerousFlagName(rawName, fromArgs) {
53
+ const name = normalizeFlagName(rawName);
54
+ if (DANGEROUS_FLAGS.has(name))
55
+ return true;
56
+ // Short aliases (-s) are only meaningful when they appear as a CLI token,
57
+ // not as a key in the `flags` object.
58
+ if (fromArgs && SHORT_ALIASES.has(name))
59
+ return true;
60
+ return false;
61
+ }
62
+ function reject(flag) {
63
+ throw new McpError(ErrorCode.InvalidParams, `Refusing to run kubectl with flag "${flag}": this flag can redirect ` +
64
+ `kubectl to a different API server or substitute credentials, which ` +
65
+ `would allow exfiltration of the operator's bearer token. If you ` +
66
+ `genuinely need this flag, set ALLOW_KUBECTL_UNSAFE_FLAGS=true in the ` +
67
+ `server environment.`);
68
+ }
69
+ /**
70
+ * Validate user-supplied kubectl flags and args. Throws an McpError if any
71
+ * dangerous flag is present and the unsafe-flags escape hatch is not set.
72
+ *
73
+ * The check covers:
74
+ * - keys of the `flags` object (e.g. { server: "..." })
75
+ * - tokens in the `args` array, in both joined ("--server=x") and split
76
+ * ("--server", "x") forms, plus short aliases ("-s").
77
+ */
78
+ export function assertNoDangerousFlags(flags, args) {
79
+ if (isUnsafeFlagsAllowed())
80
+ return;
81
+ if (flags) {
82
+ for (const key of Object.keys(flags)) {
83
+ if (isDangerousFlagName(key, false))
84
+ reject(`--${normalizeFlagName(key)}`);
85
+ }
86
+ }
87
+ if (args) {
88
+ for (const tok of args) {
89
+ if (typeof tok !== "string")
90
+ continue;
91
+ if (!tok.startsWith("-"))
92
+ continue;
93
+ if (isDangerousFlagName(tok, true))
94
+ reject(tok);
95
+ }
96
+ }
97
+ }
@@ -2,6 +2,7 @@ import { execFileSync } from "child_process";
2
2
  import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
3
3
  import { getSpawnMaxBuffer } from "../config/max-buffer.js";
4
4
  import { contextParameter, namespaceParameter, } from "../models/common-parameters.js";
5
+ import { assertNoDangerousFlags } from "../security/kubectl-flags.js";
5
6
  export const kubectlGenericSchema = {
6
7
  name: "kubectl_generic",
7
8
  description: "Execute any kubectl command with the provided arguments and flags",
@@ -50,6 +51,9 @@ export const kubectlGenericSchema = {
50
51
  };
51
52
  export async function kubectlGeneric(k8sManager, input) {
52
53
  try {
54
+ // Reject credential/target-redirecting flags before constructing the
55
+ // command. See src/security/kubectl-flags.ts for the rationale.
56
+ assertNoDangerousFlags(input.flags, input.args);
53
57
  // Start building the kubectl command
54
58
  const command = "kubectl";
55
59
  const cmdArgs = [input.command];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-server-kubernetes",
3
- "version": "3.6.2",
3
+ "version": "3.7.0",
4
4
  "description": "MCP server for interacting with Kubernetes clusters via kubectl",
5
5
  "license": "MIT",
6
6
  "type": "module",