From d239ed33c3aa76ceb9dead9c059fe4b8019baacd Mon Sep 17 00:00:00 2001 From: codex Date: Sat, 22 Aug 2026 21:25:20 +0200 Subject: [PATCH] Harden remote kubectl argument quoting Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a02991-be07-7bb3-8b6d-e9701b5621de --- .../audit-core-whitehat-e2-credentials-02.py | 3 ++- ...t_audit_core_whitehat_e2_credentials_02.py | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/scripts/audit-core-whitehat-e2-credentials-02.py b/scripts/audit-core-whitehat-e2-credentials-02.py index a921228..ccacb05 100755 --- a/scripts/audit-core-whitehat-e2-credentials-02.py +++ b/scripts/audit-core-whitehat-e2-credentials-02.py @@ -7,6 +7,7 @@ import argparse import json import os import secrets +import shlex import stat import subprocess import sys @@ -109,7 +110,7 @@ class Operator: def kubectl(self, args: list[str], *, label: str, input_text: str | None = None, allow_missing: bool = False) -> subprocess.CompletedProcess[str]: return safe_run( - ["ssh", "-o", "BatchMode=yes", self.remote, "kubectl", *args], + ["ssh", "-o", "BatchMode=yes", self.remote, shlex.join(["kubectl", *args])], label=label, input_text=input_text, allow_missing=allow_missing, diff --git a/tests/test_audit_core_whitehat_e2_credentials_02.py b/tests/test_audit_core_whitehat_e2_credentials_02.py index 081ba76..f1b42a8 100644 --- a/tests/test_audit_core_whitehat_e2_credentials_02.py +++ b/tests/test_audit_core_whitehat_e2_credentials_02.py @@ -1,7 +1,9 @@ from __future__ import annotations import importlib.util +import subprocess import unittest +from unittest import mock from datetime import UTC, datetime from pathlib import Path @@ -17,6 +19,25 @@ SPEC.loader.exec_module(module) class CredentialProcedureTests(unittest.TestCase): + def test_remote_kubectl_shell_quotes_template_arguments(self) -> None: + operator = object.__new__(module.Operator) + operator.remote = "railiance01" + template = 'go-template={{range $k, $_ := .data}}{{$k}}{{"\\n"}}{{end}}' + completed = subprocess.CompletedProcess([], 0, stdout="token-a\ntoken-b\n", stderr="") + with mock.patch.object(module, "safe_run", return_value=completed) as safe_run: + operator.kubectl( + ["-n", "whitehat", "get", "secret", "example", "-o", template], + label="verify keys", + ) + + command = safe_run.call_args.args[0] + self.assertEqual(["ssh", "-o", "BatchMode=yes", "railiance01"], command[:4]) + self.assertEqual( + "kubectl -n whitehat get secret example -o " + "'go-template={{range $k, $_ := .data}}{{$k}}{{\"\\n\"}}{{end}}'", + command[4], + ) + def test_add_and_remove_only_named_temporary_identities(self) -> None: original = [{"name": "user-engine", "tokens": ["existing"]}] updated = module.add_temporary_identities(original, {"token-a": "a", "token-b": "b"})