fix(kubernetes): block kubeconfig command auth bypass

- Reject legacy auth-provider cmd-path command authentication

- Keep backend validation as the Cloud security boundary

- Cover API and UI validation with regression tests
This commit is contained in:
Hugo P.Brito
2026-07-22 11:50:23 +01:00
parent 3bd13d173d
commit c4c188f6fb
8 changed files with 103 additions and 20 deletions
@@ -0,0 +1 @@
Kubernetes kubeconfig validation now rejects legacy `auth-provider.config.cmd-path` command authentication in Prowler Cloud/API
@@ -309,6 +309,36 @@ current-context: test-context
assert not serializer.is_valid()
assert "kubeconfig_content" in serializer.errors
def test_kubeconfig_with_auth_provider_cmd_path_is_rejected(self):
kubeconfig_content = """
apiVersion: v1
kind: Config
clusters:
- name: test-cluster
cluster:
server: https://kubernetes.example.test
users:
- name: test-user
user:
auth-provider:
name: gcp
config:
cmd-path: /bin/sh
contexts:
- name: test-context
context:
cluster: test-cluster
user: test-user
current-context: test-context
"""
serializer = KubernetesProviderSecret(
data={"kubeconfig_content": kubeconfig_content}
)
assert not serializer.is_valid()
assert "kubeconfig_content" in serializer.errors
def test_malformed_kubeconfig_is_rejected(self):
serializer = KubernetesProviderSecret(
data={"kubeconfig_content": "apiVersion: ["}
@@ -214,7 +214,7 @@ from rest_framework_json_api import serializers
"kubeconfig_content": {
"type": "string",
"description": "The content of the Kubernetes kubeconfig file, encoded as a string. "
"Kubeconfig exec authentication is not supported in Prowler Cloud for security reasons.",
"Kubeconfig command-based authentication is not supported in Prowler Cloud for security reasons.",
}
},
"required": ["kubeconfig_content"],
+19 -6
View File
@@ -1569,14 +1569,14 @@ class FindingMetadataSerializer(BaseSerializerV1):
# Provider secrets
KUBERNETES_KUBECONFIG_EXEC_ERROR = (
"Kubernetes kubeconfig exec authentication is not supported in Prowler Cloud "
"for security reasons."
KUBERNETES_KUBECONFIG_UNSUPPORTED_COMMAND_AUTH_ERROR = (
"Kubernetes kubeconfig command-based authentication is not supported in "
"Prowler Cloud for security reasons."
)
KUBERNETES_KUBECONFIG_INVALID_ERROR = "Invalid Kubernetes kubeconfig content."
def kubeconfig_contains_exec_auth(kubeconfig: dict) -> bool:
def kubeconfig_contains_unsupported_command_auth(kubeconfig: dict) -> bool:
users = kubeconfig.get("users", [])
if not isinstance(users, list):
raise ValidationError(KUBERNETES_KUBECONFIG_INVALID_ERROR)
@@ -1592,6 +1592,17 @@ def kubeconfig_contains_exec_auth(kubeconfig: dict) -> bool:
if "exec" in user:
return True
auth_provider = user.get("auth-provider", {})
if not isinstance(auth_provider, dict):
continue
auth_provider_config = auth_provider.get("config", {})
if not isinstance(auth_provider_config, dict):
continue
if "cmd-path" in auth_provider_config:
return True
return False
@@ -1788,8 +1799,10 @@ class KubernetesProviderSecret(serializers.Serializer):
if not isinstance(kubeconfig, dict):
raise serializers.ValidationError(KUBERNETES_KUBECONFIG_INVALID_ERROR)
if kubeconfig_contains_exec_auth(kubeconfig):
raise serializers.ValidationError(KUBERNETES_KUBECONFIG_EXEC_ERROR)
if kubeconfig_contains_unsupported_command_auth(kubeconfig):
raise serializers.ValidationError(
KUBERNETES_KUBECONFIG_UNSUPPORTED_COMMAND_AUTH_ERROR
)
return kubeconfig_content
@@ -0,0 +1 @@
Kubernetes credential forms now reject kubeconfig files using legacy `auth-provider.config.cmd-path` command authentication
@@ -5,8 +5,8 @@ import { Control, useWatch } from "react-hook-form";
import { WizardTextareaField } from "@/components/providers/workflow/forms/fields";
import { KubernetesCredentials } from "@/types";
import {
KUBECONFIG_EXEC_AUTHENTICATION_ERROR,
kubeconfigContainsExecAuthentication,
KUBECONFIG_UNSUPPORTED_COMMAND_AUTHENTICATION_ERROR,
kubeconfigContainsUnsupportedCommandAuthentication,
} from "@/types/formSchemas";
export const KubernetesCredentialsForm = ({
@@ -18,9 +18,8 @@ export const KubernetesCredentialsForm = ({
control,
name: "kubeconfig_content",
});
const hasExecAuthentication = kubeconfigContainsExecAuthentication(
kubeconfigContent ?? "",
);
const hasUnsupportedCommandAuthentication =
kubeconfigContainsUnsupportedCommandAuthentication(kubeconfigContent ?? "");
return (
<>
@@ -42,9 +41,9 @@ export const KubernetesCredentialsForm = ({
minRows={10}
isRequired
/>
{hasExecAuthentication && (
{hasUnsupportedCommandAuthentication && (
<p className="text-text-error-primary text-xs">
{KUBECONFIG_EXEC_AUTHENTICATION_ERROR}
{KUBECONFIG_UNSUPPORTED_COMMAND_AUTHENTICATION_ERROR}
</p>
)}
</>
+26
View File
@@ -198,6 +198,32 @@ users:
);
});
it("reports kubeconfig auth-provider cmd-path on kubeconfig_content field", () => {
const schema = addCredentialsFormSchema("kubernetes");
const result = schema.safeParse({
...BASE_KUBERNETES_VALUES,
[ProviderCredentialFields.KUBECONFIG_CONTENT]: `apiVersion: v1
kind: Config
users:
- name: test-user
user:
auth-provider:
name: gcp
config:
cmd-path: /bin/sh`,
});
expect(result.success).toBe(false);
if (result.success) return;
expect(result.error.issues).toContainEqual(
expect.objectContaining({
path: [ProviderCredentialFields.KUBECONFIG_CONTENT],
}),
);
});
it("accepts malformed kubeconfig content for backend validation", () => {
const schema = addCredentialsFormSchema("kubernetes");
+19 -6
View File
@@ -6,14 +6,14 @@ import { validateMutelistYaml, validateYaml } from "@/lib/yaml";
import { PROVIDER_TYPES, ProviderType } from "./providers";
export const KUBECONFIG_EXEC_AUTHENTICATION_ERROR =
"Kubernetes kubeconfig exec authentication is not supported in Prowler Cloud for security reasons.";
export const KUBECONFIG_UNSUPPORTED_COMMAND_AUTHENTICATION_ERROR =
"Kubernetes kubeconfig command-based authentication is not supported in Prowler Cloud for security reasons.";
const isRecord = (value: unknown): value is Record<string, unknown> => {
return typeof value === "object" && value !== null && !Array.isArray(value);
};
export const kubeconfigContainsExecAuthentication = (
export const kubeconfigContainsUnsupportedCommandAuthentication = (
value: string,
): boolean => {
try {
@@ -28,7 +28,16 @@ export const kubeconfigContainsExecAuthentication = (
return false;
}
return "exec" in userEntry.user;
if ("exec" in userEntry.user) {
return true;
}
const authProvider = userEntry.user["auth-provider"];
if (!isRecord(authProvider) || !isRecord(authProvider.config)) {
return false;
}
return "cmd-path" in authProvider.config;
});
} catch {
return false;
@@ -229,9 +238,13 @@ export const addCredentialsFormSchema = (
.string()
.min(1, "Kubeconfig Content is required")
.refine(
(value) => !kubeconfigContainsExecAuthentication(value),
(value) =>
!kubeconfigContainsUnsupportedCommandAuthentication(
value,
),
{
error: KUBECONFIG_EXEC_AUTHENTICATION_ERROR,
error:
KUBECONFIG_UNSUPPORTED_COMMAND_AUTHENTICATION_ERROR,
},
),
}