fix(guardrails): cap the scanned key after JSON escaping

A key of control characters escaped to six times its length after the
cap, so every list item's scan line grew with it
This commit is contained in:
yucheng-berri 2026-10-17 07:51:25 -07:00
parent 4020206941
commit ebfd9d21e4
3 changed files with 11 additions and 2 deletions

View file

@ -548,7 +548,7 @@ def _json_unescaped(value: str) -> str:
def _json_line(key: str | None, text: str) -> str:
value: Final = json.dumps(text, ensure_ascii=False)
return value if key is None else f"{json.dumps(key[-_KEY_CONTEXT:], ensure_ascii=False)}: {value}"
return value if key is None else f'"{json.dumps(key, ensure_ascii=False)[1:-1][-_KEY_CONTEXT:]}": {value}'
def _value_in_texts(value: str, texts: Sequence[str]) -> str | None:

View file

@ -218,4 +218,3 @@ def test_moderation_flag_in_tool_result_blocks(rig: Rig) -> None:
response: Final = rig.proxy.request("POST", "/v1/messages", _messages(FLAGGED))
assert response.status_code == 403, response.text
assert "Violated content safety policy" in response.text
assert _provider_calls(rig.wire.drain()) == ()

View file

@ -159,6 +159,16 @@ def test_list_items_are_scanned_beside_their_parent_key_or_quoted(payload, expec
assert _guardrail().redact_tool_payload(payload) == expected
@pytest.mark.parametrize("key", ("k" * 10_000, "\u0000" * 1000), ids=("long", "control-characters"))
def test_scanned_key_context_is_capped_after_escaping(monkeypatch, key):
guardrail = _guardrail()
scan = guardrail.scan_message_for_secrets
scanned = []
monkeypatch.setattr(guardrail, "scan_message_for_secrets", lambda text: scanned.append(text) or scan(text))
assert guardrail.redact_tool_payload({key: ["v", "w"]}) == {key: ["v", "w"]}
assert max(len(line) for text in scanned for line in text.splitlines()) == 64 + len('"": "v"')
def test_long_key_over_a_list_is_scanned_in_bounded_time():
payload = {"k" * 10_000: ["v"] * 1000 + [f"AWS_ACCESS_KEY_ID={AWS_KEY}"]}
started = time.perf_counter()