From 4c3f1e86809f1cddc75fa548b08f65c9ea066435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robin=20Br=C3=A4mer?= <22003767+robinbraemer@users.noreply.github.com> Date: Thu, 13 Aug 2026 23:30:53 +0200 Subject: [PATCH] fix: accept disabled source-only runner capabilities --- script/validate_runner_catalog.rb | 7 ++++++- test/runner_catalog_test.rb | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/script/validate_runner_catalog.rb b/script/validate_runner_catalog.rb index 8771d36..3468e76 100644 --- a/script/validate_runner_catalog.rb +++ b/script/validate_runner_catalog.rb @@ -18,6 +18,7 @@ class RunnerCatalogValidator "privilegedContainers" => "privileged containers", "ordinaryBuildAndTestTooling" => BASELINE_CAPABILITY }.freeze + SOURCE_ONLY_FALSE_CAPABILITIES = %w[nestedVirtualization].freeze PUBLIC_CAPABILITIES = (CAPABILITY_NAMES.values + [BASELINE_CAPABILITY]).freeze PROFILE_DEFINITIONS = { "akua-x64-ci-v2" => { @@ -385,7 +386,11 @@ def normalize_profile(source_profile) def normalize_capabilities(capabilities) fail_with("source capability schema drift") unless capabilities.is_a?(Hash) - fail_with("source capability schema drift") unless capabilities.keys.all? { |key| CAPABILITY_NAMES.key?(key) } + allowed_source_keys = CAPABILITY_NAMES.keys + SOURCE_ONLY_FALSE_CAPABILITIES + fail_with("source capability schema drift") unless capabilities.keys.all? { |key| allowed_source_keys.include?(key) } + SOURCE_ONLY_FALSE_CAPABILITIES.each do |key| + fail_with("source-only capability must remain disabled") unless capabilities.fetch(key, false) == false + end result = CAPABILITY_NAMES.each_with_object([]) do |(key, name), values| value = capabilities.fetch(key, false) fail_with("source capability schema drift") unless value == true || value == false diff --git a/test/runner_catalog_test.rb b/test/runner_catalog_test.rb index 33c46f0..54f647a 100644 --- a/test/runner_catalog_test.rb +++ b/test/runner_catalog_test.rb @@ -164,6 +164,17 @@ def test_trusted_validator_rejects_truthy_non_boolean_capabilities end end + def test_trusted_validator_rejects_enabled_source_only_capability + with_source_candidate do |candidate, source| + source_file = File.join(source, SOURCE_PATH) + source_catalog = YAML.safe_load(File.read(source_file), aliases: false) + source_catalog.fetch("profiles").last.fetch("capabilities")["nestedVirtualization"] = true + File.write(source_file, YAML.dump(source_catalog)) + update_source_hash(candidate, source_file) + assert_validator_failure(candidate, "source-only capability must remain disabled", source) + end + end + def test_trusted_validator_rejects_missing_baseline_capability with_source_candidate do |candidate, source| source_file = File.join(source, SOURCE_PATH) @@ -343,6 +354,7 @@ def source_catalog "buildx" => profile.dig("capabilities", "guaranteed").include?("Buildx"), "serviceContainers" => profile.dig("capabilities", "guaranteed").include?("service containers"), "privilegedContainers" => profile.dig("capabilities", "guaranteed").include?("privileged containers"), + "nestedVirtualization" => false, "ordinaryBuildAndTestTooling" => true } profile.merge(