From 477274f1d9a5a3a23966fd05c012e8aab7dd03aa Mon Sep 17 00:00:00 2001 From: Bartok9 Date: Fri, 29 May 2026 01:13:04 -0400 Subject: [PATCH] fix(config): allow underscore-prefixed internal/test keys past schema validation The schema validation added in this PR (#34250) rejected underscore- prefixed config keys like '_test.shim_marker', breaking the Docker privilege-drop test suite (tests/docker/test_docker_exec_privilege_drop.py) which writes such markers via 'hermes config set _test. 1' to probe config.yaml file ownership after the UID-drop shim runs. Ten Docker tests failed in build-amd64 CI for this reason: test_shim_drops_root_to_hermes_uid (_test.shim_marker) test_shim_short_circuits_for_non_root (_test.shim_short_circuit) test_shim_opt_out_keeps_root (_test.opt_out) test_shim_opt_out_strict_truthiness[*] (_test.falsy) x6 test_e2e_login_then_supervised_gateway (_test.e2e_marker) Fix: treat a leading underscore on the TOP-LEVEL segment as an internal/test marker that bypasses schema validation. Mirrors Python's own '_private' convention. The escape is narrow: - Only the first segment is checked, so a genuine typo in a sub-key under a known top-level key (e.g. 'agent._max_turns') is still flagged. - Real typos ('agent.max_turn' -> 'agent.max_turns') still caught. - The headline #34067 bug ('gateway.discord.gateway_restart_notification') still caught. Tests (5 new in TestValidateConfigKey): - 4 parametrized cases for accepted underscore-prefixed keys (_test.shim_marker, _internal, _test.nested.deep.marker, _x) - test_underscore_only_first_segment_escapes: confirms agent._max_turns (underscore in a SUB-key, not the top) is still rejected. All 58 tests in test_set_config_value.py pass. Refs: #34067 #34250 Co-authored-by: Cursor --- hermes_cli/config.py | 14 ++++++++++++++ tests/hermes_cli/test_set_config_value.py | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/hermes_cli/config.py b/hermes_cli/config.py index c67dd2d167e5..04e4887e645f 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -8645,6 +8645,20 @@ def _validate_config_key(key: str) -> tuple[bool, Optional[str]]: segments = key.split(".") top = segments[0] + + # ── Underscore-prefixed keys are internal/test markers ─────────── + # A leading underscore on the top-level segment (e.g. ``_test.shim_marker``) + # signals an intentionally non-schema, internal key. Test harnesses and + # tooling use these to write a deterministic marker into config.yaml + # without polluting the user-facing schema (see the Docker privilege-drop + # shim test, which writes ``_test.shim_marker`` to probe file ownership). + # Python's own convention treats a leading underscore as "private"; we + # honour that here so schema validation never blocks deliberately-internal + # keys. This is narrow: only the FIRST segment is checked, so a real typo + # like ``agent._max_turns`` still gets caught at the sub-key level. + if top.startswith("_"): + return True, None + known = _known_top_level_keys() # ── First-segment validation ───────────────────────────────────── diff --git a/tests/hermes_cli/test_set_config_value.py b/tests/hermes_cli/test_set_config_value.py index 9b8c3a90de71..86ba5aa3c921 100644 --- a/tests/hermes_cli/test_set_config_value.py +++ b/tests/hermes_cli/test_set_config_value.py @@ -587,3 +587,25 @@ class TestValidateConfigKey: if expected_in_suggestion is not None: assert suggestion is not None and expected_in_suggestion in suggestion, \ f"Expected suggestion to contain {expected_in_suggestion!r}, got {suggestion!r}" + + @pytest.mark.parametrize("key", [ + "_test.shim_marker", + "_internal", + "_test.nested.deep.marker", + "_x", + ]) + def test_underscore_prefixed_keys_are_accepted(self, key): + """Underscore-prefixed top-level keys are internal/test markers and + bypass schema validation. The Docker privilege-drop shim test writes + ``_test.shim_marker`` to probe config.yaml ownership; that must not + be rejected. (Regression: #34250 schema validation broke this.)""" + from hermes_cli.config import _validate_config_key + is_known, _ = _validate_config_key(key) + assert is_known, f"Expected underscore-prefixed {key!r} to be accepted" + + def test_underscore_only_first_segment_escapes(self): + """The underscore escape only applies to the FIRST segment. A real + typo in a sub-key (e.g. agent._max_turns) is still caught.""" + from hermes_cli.config import _validate_config_key + is_known, suggestion = _validate_config_key("agent._max_turns") + assert not is_known, "Sub-key typo under a known top-level key must still be flagged"