From 5bb00f9e3a4282fa26f79e61ebf5880e24a5b450 Mon Sep 17 00:00:00 2001 From: Bartok9 Date: Mon, 13 Jul 2026 12:46:58 -0400 Subject: [PATCH] fix(config): validate gateway.platforms.* + approvals.* per maintainer review Address hermes-sweeper review on #34250: - Accept top-level platforms..* and gateway.platforms..* (current docs + gateway/config.py resolve platforms under these paths; PlatformConfig.extra keeps them open below the platform-name segment). - Remove approvals from open-dict whitelist so approvals. is schema-validated and refused instead of silently written. - Tests for canonical platform paths and unknown approvals key rejection. --- tests/hermes_cli/test_set_config_value.py | 42 +++++++++++++++++------ 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/tests/hermes_cli/test_set_config_value.py b/tests/hermes_cli/test_set_config_value.py index 86ba5aa3c921..8770483eedfe 100644 --- a/tests/hermes_cli/test_set_config_value.py +++ b/tests/hermes_cli/test_set_config_value.py @@ -403,7 +403,9 @@ class TestStringTypedConfigValues: assert type(node) is type(expected) def test_unknown_keys_keep_existing_coercion(self, _isolated_hermes_home): - set_config_value("custom.enabled", "off") + # ``custom`` is not a known top-level key, so it now requires --force + # (schema validation, #34067); coercion behavior is unchanged. + set_config_value("custom.enabled", "off", force=True) import yaml saved = yaml.safe_load(_read_config(_isolated_hermes_home)) @@ -499,15 +501,33 @@ class TestSchemaValidation: # Nothing was written. assert "discord" not in _read_config(_isolated_hermes_home).split("gateway:")[-1] if "gateway:" in _read_config(_isolated_hermes_home) else True - def test_platforms_prefix_suggests_top_level(self, _isolated_hermes_home, capsys): - """``platforms.discord.foo`` should suggest ``discord.foo`` since - DEFAULT_CONFIG puts platform configs at the top level, not under - a ``platforms:`` namespace.""" + def test_platforms_container_is_accepted(self, _isolated_hermes_home): + """``platforms..`` is a valid current shape: gateway/ + config.py resolves a top-level ``platforms`` map in addition to the + top-level platform blocks, so it must NOT be refused.""" + set_config_value("platforms.discord.enabled", "true") + content = _read_config(_isolated_hermes_home) + assert "enabled: true" in content + + def test_gateway_platforms_nested_is_accepted(self, _isolated_hermes_home): + """Docs configure platforms under ``gateway.platforms.`` — the + canonical layout must validate as known.""" + set_config_value("gateway.platforms.my_platform.extra.token", "abc") + content = _read_config(_isolated_hermes_home) + assert "token: abc" in content + + def test_unknown_approvals_subkey_is_refused(self, _isolated_hermes_home, capsys): + """``approvals`` is a defined schema, so a typo'd sub-key must be + rejected rather than silently written.""" with pytest.raises(SystemExit): - set_config_value("platforms.discord.gateway_restart_notification", "false") - out = capsys.readouterr().out - assert "Did you mean" in out - assert "discord.gateway_restart_notification" in out + set_config_value("approvals.notarealkey", "true") + + def test_known_approvals_subkey_is_accepted(self, _isolated_hermes_home): + """Real ``approvals.*`` keys still validate.""" + set_config_value("approvals.mode", "off") + import yaml + saved = yaml.safe_load(_read_config(_isolated_hermes_home)) + assert saved["approvals"]["mode"] == "off" def test_close_typo_suggests_correct_key(self, _isolated_hermes_home, capsys): """Typo'd top-level keys should get a fuzzy-match suggestion.""" @@ -568,6 +588,9 @@ class TestValidateConfigKey: "mcp_servers.foo.command", "providers.openrouter.api_key", "gateway.strict", + "platforms.discord.enabled", + "gateway.platforms.my_platform.extra.token", + "approvals.mode", ]) def test_known_keys_pass(self, key): from hermes_cli.config import _validate_config_key @@ -576,7 +599,6 @@ class TestValidateConfigKey: @pytest.mark.parametrize("key,expected_in_suggestion", [ ("gateway.discord.gateway_restart_notification", None), # no close suggestion - ("platforms.discord.gateway_restart_notification", "discord.gateway_restart_notification"), ("disco", "discord"), ("agent.max_turn", "agent.max_turns"), ])