From c688d5fdb3503e2ca7dfaa490cde2a2487689b7c Mon Sep 17 00:00:00 2001 From: JunZ-Leo <100498253+JunZ-Leo@users.noreply.github.com> Date: Fri, 25 Sep 2026 23:23:57 +0800 Subject: [PATCH] test(public-safety): pin that a credential key is one key in any spelling `normalize_public_safe_field_name` is the single decision point in front of exact field classification, and the suffix families and the `raw` / `raw_*` rule only exist because it splits camelCase: `reviewToken` folds to `reviewtoken`, which belongs to no family, so without the boundary renaming a field to camelCase walks out of the rule untouched. No test held that equivalence, and the function had no direct reference in `tests/`. The set pins seven spellings of one family member onto one verdict, five suffix-shaped names that only the split classifies, the raw rule's camelCase reach, seven benign camelCase keys the boundary must not sweep up, and that the rejection names the caller's own key through nested mappings and lists. Production code is unchanged. Signed-off-by: JunZ-Leo <100498253+JunZ-Leo@users.noreply.github.com> --- .../test_public_safety_field_name_spelling.py | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 tests/control_plane/test_public_safety_field_name_spelling.py diff --git a/tests/control_plane/test_public_safety_field_name_spelling.py b/tests/control_plane/test_public_safety_field_name_spelling.py new file mode 100644 index 000000000..6a49799b9 --- /dev/null +++ b/tests/control_plane/test_public_safety_field_name_spelling.py @@ -0,0 +1,94 @@ +"""One credential key must read as one key, however the caller spells it. + +`validate_public_safe_value` classifies field names exactly, after +`normalize_public_safe_field_name` folds case, separators and camelCase +boundaries. The suffix families (`_token`, `_secret`, `_password`, +`_credential`, `_credentials`) and the raw-payload rule (`raw`, `raw_*`) only +exist once the words are split: `reviewToken` flattens to `reviewtoken`, which +belongs to no family, so without the boundary a caller that renames a field to +camelCase would walk out of the rule untouched. These tests pin the boundary and +the keys it must not sweep up. +""" + +import pytest + +from loopx.control_plane.runtime.public_safety import ( + normalize_public_safe_field_name, + validate_public_safe_value, +) + +ACCESS_TOKEN_SPELLINGS = [ + "access_token", + "accessToken", + "ACCESS_TOKEN", + "Access Token", + "access-token", + "access.token", + " accessToken ", +] + +SUFFIXED_CREDENTIAL_NAMES = [ + "reviewToken", + "rotateSecret", + "viewerPassword", + "handoffCredential", + "noteToken", +] + +CREDENTIAL_SUFFIXES = ("_token", "_secret", "_password", "_credential", "_credentials") + +BENIGN_CAMEL_NAMES = [ + "tokenCount", + "keyId", + "secretLevel", + "taskClass", + "cacheHitRatio", + "lru_cache_hits", + "goal_id", +] + + +@pytest.mark.parametrize("key", ACCESS_TOKEN_SPELLINGS) +def test_every_spelling_of_a_family_member_reaches_one_verdict(key: str) -> None: + assert normalize_public_safe_field_name(key) == "access_token" + with pytest.raises(ValueError, match="is a credential-bearing field") as raised: + validate_public_safe_value({key: "synthetic"}, path="p") + assert str(raised.value) == f"p.{key} is a credential-bearing field" + + +@pytest.mark.parametrize("key", SUFFIXED_CREDENTIAL_NAMES) +def test_suffix_rules_fire_only_because_camel_case_is_split(key: str) -> None: + normalized = normalize_public_safe_field_name(key) + + assert normalized.endswith(CREDENTIAL_SUFFIXES) + assert "_" in normalized + with pytest.raises(ValueError, match="is a credential-bearing field") as raised: + validate_public_safe_value({key: "synthetic"}, path="p") + assert str(raised.value) == f"p.{key} is a credential-bearing field" + + +def test_the_raw_payload_rule_reaches_camel_case_names_too() -> None: + assert normalize_public_safe_field_name("rawOutput") == "raw_output" + assert normalize_public_safe_field_name("raw") == "raw" + for key in ("raw", "raw_body", "rawOutput"): + with pytest.raises(ValueError, match="unbounded raw payload field"): + validate_public_safe_value({key: "synthetic"}, path="p") + + +@pytest.mark.parametrize("key", BENIGN_CAMEL_NAMES) +def test_a_camel_boundary_alone_does_not_widen_the_rules(key: str) -> None: + normalized = normalize_public_safe_field_name(key) + + assert "_" in normalized or normalized == key.casefold() + assert not normalized.endswith(CREDENTIAL_SUFFIXES) + validate_public_safe_value({key: "synthetic"}, path="p") + + +def test_nested_and_listed_payloads_keep_naming_the_callers_key() -> None: + with pytest.raises(ValueError) as nested: + validate_public_safe_value({"outer": {"reviewToken": "synthetic"}}, path="p") + assert str(nested.value) == "p.outer.reviewToken is a credential-bearing field" + + with pytest.raises(ValueError) as listed: + validate_public_safe_value([{"accessToken": "synthetic"}], path="p") + assert str(listed.value) == "p[0].accessToken is a credential-bearing field"