From 0ef2ae515e6fa674118c9ace2de93ea967d211a7 Mon Sep 17 00:00:00 2001 From: tegwick Date: Tue, 28 Jul 2026 15:22:43 +0200 Subject: [PATCH] Normalize runtime secret transport whitespace --- src/user_engine/runtime.py | 8 +++++++- tests/test_runtime_config.py | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/test_runtime_config.py diff --git a/src/user_engine/runtime.py b/src/user_engine/runtime.py index a60f99a..b1ffe6b 100644 --- a/src/user_engine/runtime.py +++ b/src/user_engine/runtime.py @@ -69,7 +69,13 @@ def main() -> None: def _required(name: str) -> str: - value = os.environ.get(name) + # Values sourced from files or `kubectl create secret --from-file` commonly + # retain one trailing newline. Such a value is unusable in HTTP headers + # (notably USER_ENGINE_PROXY_SECRET and provisioning bearer tokens), and + # comparing it byte-for-byte makes the trusted boundary impossible to + # exercise. Normalize transport whitespace at the runtime boundary; the + # domain and adapters still receive an opaque non-empty value. + value = os.environ.get(name, "").strip() if not value: raise RuntimeError(f"{name} is required") return value diff --git a/tests/test_runtime_config.py b/tests/test_runtime_config.py new file mode 100644 index 0000000..cb9435a --- /dev/null +++ b/tests/test_runtime_config.py @@ -0,0 +1,20 @@ +import os +import unittest +from unittest.mock import patch + +from user_engine.runtime import _required + + +class RequiredRuntimeValueTests(unittest.TestCase): + def test_normalizes_secret_file_newline(self): + with patch.dict(os.environ, {"USER_ENGINE_TEST_SECRET": "secret-value\n"}): + self.assertEqual(_required("USER_ENGINE_TEST_SECRET"), "secret-value") + + def test_rejects_whitespace_only_value(self): + with patch.dict(os.environ, {"USER_ENGINE_TEST_SECRET": " \n\t"}): + with self.assertRaisesRegex(RuntimeError, "USER_ENGINE_TEST_SECRET is required"): + _required("USER_ENGINE_TEST_SECRET") + + +if __name__ == "__main__": + unittest.main()