From d774171be9b40aedc301c4cc37318e1cb35dce60 Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Tue, 21 Jul 2026 14:37:35 +0200 Subject: [PATCH] Fix tests using /dev/stdout in restricted container environments Backport: https://github.com/keylime/keylime/pull/1934 Resolves: RHEL-192222 Signed-off-by: Anderson Toshiyuki Sasaki --- ...use-temp-files-instead-of-dev-stdout.patch | 284 ++++++++++++++++++ keylime.spec | 10 +- 2 files changed, 293 insertions(+), 1 deletion(-) create mode 100644 0006-fix-test-use-temp-files-instead-of-dev-stdout.patch diff --git a/0006-fix-test-use-temp-files-instead-of-dev-stdout.patch b/0006-fix-test-use-temp-files-instead-of-dev-stdout.patch new file mode 100644 index 0000000..abf0986 --- /dev/null +++ b/0006-fix-test-use-temp-files-instead-of-dev-stdout.patch @@ -0,0 +1,284 @@ +From 2d46c356abd86b2bbddea058a7ca9a0a0c5da386 Mon Sep 17 00:00:00 2001 +From: Anderson Toshiyuki Sasaki +Date: Tue, 14 Jul 2026 16:50:03 +0200 +Subject: [PATCH 1/3] test: use per-case temp file instead of /dev/stdout in + test_sign_runtime_policy + +The test was relying on the default --output value of /dev/stdout, which +is not writable in restricted container environments such as Konflux +build containers (podman with --network=none), causing the test to fail +with "Permission denied". + +Fix by explicitly passing a per-case temp file via --output for each +test case that exercises sign_runtime_policy(). Using mkstemp() with a +case-indexed suffix avoids filename conflicts when tests run in +parallel. + +Co-Authored-By: Claude Sonnet 4.6 (1M context) +Signed-off-by: Anderson Toshiyuki Sasaki +--- + test/test_sign_runtime_policy.py | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/test/test_sign_runtime_policy.py b/test/test_sign_runtime_policy.py +index 7af03f8cf..02e7455a0 100644 +--- a/test/test_sign_runtime_policy.py ++++ b/test/test_sign_runtime_policy.py +@@ -169,7 +169,7 @@ def test_sign_runtime_policy(self): + with tempfile.TemporaryDirectory() as temp_dir: + os.chdir(temp_dir) + +- for case in test_cases: ++ for i, case in enumerate(test_cases): + expected = case["valid"] + del case["valid"] + missing_params = case["missing_params"] +@@ -184,7 +184,11 @@ def test_sign_runtime_policy(self): + with self.assertRaises(SystemExit): + args = parser.parse_args(cli_args) + else: +- args = parser.parse_args(cli_args) ++ # Use a per-case temp file instead of /dev/stdout to avoid ++ # permission issues in restricted container environments (e.g. Konflux). ++ fd, output_file = tempfile.mkstemp(suffix=f"-{i}.json", dir=temp_dir) ++ os.close(fd) ++ args = parser.parse_args(cli_args + ["--output", output_file]) + self.assertTrue(args is not None) + + signed = sign_runtime_policy.sign_runtime_policy(args) + +From 1aa9cc6545e82ad509beff49466bae6e322b2cdc Mon Sep 17 00:00:00 2001 +From: Anderson Toshiyuki Sasaki +Date: Tue, 14 Jul 2026 16:55:06 +0200 +Subject: [PATCH 2/3] test: use per-case temp file instead of /dev/stdout in + test_create_runtime_policy + +test_digest_algorithm_priority and +test_digest_algorithm_priority_exceptions both call +create_runtime_policy() expecting a successful result, which causes the +function to write the output to the default --output value of +/dev/stdout. This fails with "Permission denied" in restricted container +environments such as Konflux build containers (podman with +--network=none). + +Fix by passing a per-case temp file via --output for each test case. +Using mkstemp() with a case-indexed suffix avoids filename conflicts +when tests run in parallel. + +Co-Authored-By: Claude Sonnet 4.6 (1M context) +Signed-off-by: Anderson Toshiyuki Sasaki +--- + test/test_create_runtime_policy.py | 88 +++++++++++++++++------------- + 1 file changed, 49 insertions(+), 39 deletions(-) + +diff --git a/test/test_create_runtime_policy.py b/test/test_create_runtime_policy.py +index eb9c19a6e..3af1981d4 100644 +--- a/test/test_create_runtime_policy.py ++++ b/test/test_create_runtime_policy.py +@@ -771,23 +771,28 @@ def test_digest_algorithm_priority(self): + subparser = main_parser.add_subparsers(title="actions") + parser = create_runtime_policy.get_arg_parser(subparser, parent_parser) + +- for case in test_cases: +- cli_args = ["--verbose"] +- # Prepare argument input +- for k in ["algo_opt", "base_policy", "allowlist", "ima_log", "rootfs"]: +- cli_args.extend(case.get(k, [])) +- +- args = parser.parse_args(cli_args) +- expected_algo = case["expected_algo"] +- expected_source = case["expected_source"] +- +- with keylimePolicyAssertLogs() as logs: +- _policy = create_runtime_policy.create_runtime_policy(args) +- self.assertIn( +- f"Using digest algorithm '{expected_algo}' obtained from the {expected_source}", +- logs.getvalue(), +- msg=f"ARGS: {' '.join(cli_args)}", +- ) ++ with tempfile.TemporaryDirectory() as temp_dir: ++ for i, case in enumerate(test_cases): ++ cli_args = ["--verbose"] ++ # Prepare argument input ++ for k in ["algo_opt", "base_policy", "allowlist", "ima_log", "rootfs"]: ++ cli_args.extend(case.get(k, [])) ++ ++ # Use a per-case temp file instead of /dev/stdout to avoid ++ # permission issues in restricted container environments (e.g. Konflux). ++ fd, output_file = tempfile.mkstemp(suffix=f"-{i}.json", dir=temp_dir) ++ os.close(fd) ++ args = parser.parse_args(cli_args + ["--output", output_file]) ++ expected_algo = case["expected_algo"] ++ expected_source = case["expected_source"] ++ ++ with keylimePolicyAssertLogs() as logs: ++ _policy = create_runtime_policy.create_runtime_policy(args) ++ self.assertIn( ++ f"Using digest algorithm '{expected_algo}' obtained from the {expected_source}", ++ logs.getvalue(), ++ msg=f"ARGS: {' '.join(cli_args)}", ++ ) + + def test_digest_algorithm_priority_exceptions(self): + """Test priority algorithms exceptions""" +@@ -839,28 +844,33 @@ def test_digest_algorithm_priority_exceptions(self): + subparser = main_parser.add_subparsers(title="actions") + parser = create_runtime_policy.get_arg_parser(subparser, parent_parser) + +- for case in test_cases: +- cli_args = ["--verbose"] +- # Prepare argument input +- for k in ["base_policy", "allowlist", "ima_log"]: +- cli_args.extend(case.get(k, [])) +- +- args = parser.parse_args(cli_args) +- expected_algo = case["expected_algo"] +- expected_source = case["expected_source"] +- +- with keylimePolicyAssertLogs() as logs: +- _policy = create_runtime_policy.create_runtime_policy(args) +- if case["expected_mismatch"]: +- self.assertIn( +- f"The digest algorithm in the IMA measurement list does not match the previously set '{expected_algo}' algorithm", +- logs.getvalue(), +- ) +- else: +- self.assertIn( +- f"Using digest algorithm '{expected_algo}' obtained from the {expected_source}", +- logs.getvalue(), +- ) ++ with tempfile.TemporaryDirectory() as temp_dir: ++ for i, case in enumerate(test_cases): ++ cli_args = ["--verbose"] ++ # Prepare argument input ++ for k in ["base_policy", "allowlist", "ima_log"]: ++ cli_args.extend(case.get(k, [])) ++ ++ # Use a per-case temp file instead of /dev/stdout to avoid ++ # permission issues in restricted container environments (e.g. Konflux). ++ fd, output_file = tempfile.mkstemp(suffix=f"-{i}.json", dir=temp_dir) ++ os.close(fd) ++ args = parser.parse_args(cli_args + ["--output", output_file]) ++ expected_algo = case["expected_algo"] ++ expected_source = case["expected_source"] ++ ++ with keylimePolicyAssertLogs() as logs: ++ _policy = create_runtime_policy.create_runtime_policy(args) ++ if case["expected_mismatch"]: ++ self.assertIn( ++ f"The digest algorithm in the IMA measurement list does not match the previously set '{expected_algo}' algorithm", ++ logs.getvalue(), ++ ) ++ else: ++ self.assertIn( ++ f"Using digest algorithm '{expected_algo}' obtained from the {expected_source}", ++ logs.getvalue(), ++ ) + + def test_mixed_algorithms_sources(self): + """Test that mixing digests from different algorithms is not allowed""" + +From c107900aa92aa1b63b5adf6ecb9cf768cc5a79d2 Mon Sep 17 00:00:00 2001 +From: Anderson Toshiyuki Sasaki +Date: Wed, 15 Jul 2026 14:11:56 +0200 +Subject: [PATCH 3/3] test: use temp files in test_mixed and test_unknown + algorithm tests + +Apply the same mkstemp + --output pattern to +test_mixed_algorithms_sources and test_unknown_algorithm_sources for +consistency. These tests currently work because create_runtime_policy() +returns early before reaching the open(args.output) call, but a future +refactor that moves the file write earlier would break them in +restricted container environments. + +Co-Authored-By: Claude Sonnet 4.6 (1M context) +Signed-off-by: Anderson Toshiyuki Sasaki +--- + test/test_create_runtime_policy.py | 70 +++++++++++++++++------------- + 1 file changed, 40 insertions(+), 30 deletions(-) + +diff --git a/test/test_create_runtime_policy.py b/test/test_create_runtime_policy.py +index 3af1981d4..e77974014 100644 +--- a/test/test_create_runtime_policy.py ++++ b/test/test_create_runtime_policy.py +@@ -917,21 +917,26 @@ def test_mixed_algorithms_sources(self): + subparser = main_parser.add_subparsers(title="actions") + parser = create_runtime_policy.get_arg_parser(subparser, parent_parser) + +- for case in test_cases: +- cli_args = [] +- # Prepare argument input +- for k in ["algo_opt", "base policy", "allowlist", "IMA measurement list", "rootfs"]: +- cli_args.extend(case.get(k, [])) +- +- args = parser.parse_args(cli_args) +- +- with keylimePolicyAssertLogs() as logs: +- policy = create_runtime_policy.create_runtime_policy(args) +- self.assertIn( +- f"The digest algorithm in the {case['source']} does not match the previously set 'sha1' algorithm", +- logs.getvalue(), +- ) +- self.assertEqual(policy, None) ++ with tempfile.TemporaryDirectory() as temp_dir: ++ for i, case in enumerate(test_cases): ++ cli_args = [] ++ # Prepare argument input ++ for k in ["algo_opt", "base policy", "allowlist", "IMA measurement list", "rootfs"]: ++ cli_args.extend(case.get(k, [])) ++ ++ # Use a per-case temp file instead of /dev/stdout to avoid ++ # permission issues in restricted container environments (e.g. Konflux). ++ fd, output_file = tempfile.mkstemp(suffix=f"-{i}.json", dir=temp_dir) ++ os.close(fd) ++ args = parser.parse_args(cli_args + ["--output", output_file]) ++ ++ with keylimePolicyAssertLogs() as logs: ++ policy = create_runtime_policy.create_runtime_policy(args) ++ self.assertIn( ++ f"The digest algorithm in the {case['source']} does not match the previously set 'sha1' algorithm", ++ logs.getvalue(), ++ ) ++ self.assertEqual(policy, None) + + def test_unknown_algorithm_sources(self): + """Test that input with digests from unknown algorithms are not allowed""" +@@ -978,18 +983,23 @@ def test_unknown_algorithm_sources(self): + subparser = main_parser.add_subparsers(title="actions") + parser = create_runtime_policy.get_arg_parser(subparser, parent_parser) + +- for case in test_cases: +- cli_args = ["--verbose"] +- # Prepare argument input +- for k in ["algo_opt", "base policy", "allowlist", "IMA measurement list", "rootfs"]: +- cli_args.extend(case.get(k, [])) +- +- args = parser.parse_args(cli_args) +- +- with keylimePolicyAssertLogs() as logs: +- policy = create_runtime_policy.create_runtime_policy(args) +- self.assertIn( +- f"Invalid digest algorithm found in the {case['source']}", +- logs.getvalue(), +- ) +- self.assertEqual(policy, None) ++ with tempfile.TemporaryDirectory() as temp_dir: ++ for i, case in enumerate(test_cases): ++ cli_args = ["--verbose"] ++ # Prepare argument input ++ for k in ["algo_opt", "base policy", "allowlist", "IMA measurement list", "rootfs"]: ++ cli_args.extend(case.get(k, [])) ++ ++ # Use a per-case temp file instead of /dev/stdout to avoid ++ # permission issues in restricted container environments (e.g. Konflux). ++ fd, output_file = tempfile.mkstemp(suffix=f"-{i}.json", dir=temp_dir) ++ os.close(fd) ++ args = parser.parse_args(cli_args + ["--output", output_file]) ++ ++ with keylimePolicyAssertLogs() as logs: ++ policy = create_runtime_policy.create_runtime_policy(args) ++ self.assertIn( ++ f"Invalid digest algorithm found in the {case['source']}", ++ logs.getvalue(), ++ ) ++ self.assertEqual(policy, None) diff --git a/keylime.spec b/keylime.spec index 831c4bd..8f78768 100644 --- a/keylime.spec +++ b/keylime.spec @@ -9,7 +9,7 @@ Name: keylime Version: 7.14.3 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Open source TPM software for Bootstrapping and Maintaining Trust URL: https://github.com/keylime/keylime @@ -32,6 +32,10 @@ Patch: 0004-Disable-push-model-agent-driven-attestation-for-RHEL.patch # Backport: https://github.com/keylime/keylime/pull/1930 Patch: 0005-fix-handle-invalid-config-values-for-typed-server-op.patch +# RHEL-192222 - Fix tests using /dev/stdout in restricted container environments +# Backport: https://github.com/keylime/keylime/pull/1934 +Patch: 0006-fix-test-use-temp-files-instead-of-dev-stdout.patch + License: ASL 2.0 and MIT BuildRequires: git-core @@ -429,6 +433,10 @@ fi %license LICENSE %changelog +* Thu Jul 16 2026 Anderson Toshiyuki Sasaki - 7.14.3-2 +- Fix tests using /dev/stdout in restricted container environments + Resolves: RHEL-192222 + * Mon Jun 01 2026 Sergio Correia - 7.14.1-1 - Update to 7.14.2 Resolves: RHEL-180618