You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
validate_public_safe_value classifies a payload's field names before it recurses into values, and its documented contract is that classification is exact after case, separator and camelCase normalisation. Both the surface-pattern pre-check and the normalisation run on str(key). For a key that is not a str, that fold produces Python's repr, so the name the boundary classifies is not the name the caller used — and the wrapper text it injects can move the name out of every rule.
Measured on main at 27f0fc93b, calling validate_public_safe_value directly:
key passed in
normalized name the boundary sees
verdict
"raw"
raw
rejected — unbounded raw payload field
b"raw"
b_raw
accepted
"access_token"
access_token
rejected — credential-bearing field
b"access_token"
b_access_token
rejected (the _token suffix still matches)
b"api_key"
b_api_key
accepted
So the escape is name-dependent, not universal: it defeats the exact-equality rules (raw) and the family membership checks (api_key flattens to bapikey, which is in no family), while accidentally surviving the suffix rules. The tests in #5070 deliberately do not pin the accepted cases, because encoding them as expected behaviour would make the hole harder to close.
Reachability
I did not find a caller that passes non-str keys today: the payloads reaching this function are JSON-shaped status and projection documents, whose keys are strings after parsing. That is why this is reported as a boundary-consistency defect rather than a demonstrated leak. The function is reached from loopx/control_plane/runtime/public_safety.py itself and from loopx/capabilities/reliability_diagnostics/envelope.py, and it is a fail-closed guard whose whole value depends on the caller not having to think about key types.
Desired outcome / 期望行为
One rule for what a field name is. Either reject a mapping whose key is not a str before any classification, with a message that says the key type is not nameable, or decode a bytes key explicitly and then classify it. Do not rely on str(key), whose repr wrapper is what moves the name out of the rules.
The existing behaviour for str keys must not move in either direction, and whatever shape is chosen should be stated in the public-safety contract rather than left to the fold.
Acceptance / 验收
A mapping keyed by bytes that names a credential or raw-payload field is not accepted as public-safe.
The chosen rule is asserted for both an exact-equality member (raw) and a family member (api_key), since those are the two clauses the current fold defeats.
A benign non-str key does not become a false positive if the decision is to decode rather than reject; if the decision is to reject, the message names the key type instead of pretending the name was classified.
Problem / 问题
validate_public_safe_valueclassifies a payload's field names before it recurses into values, and its documented contract is that classification is exact after case, separator and camelCase normalisation. Both the surface-pattern pre-check and the normalisation run onstr(key). For a key that is not astr, that fold produces Python'srepr, so the name the boundary classifies is not the name the caller used — and the wrapper text it injects can move the name out of every rule.Measured on
mainat27f0fc93b, callingvalidate_public_safe_valuedirectly:"raw"rawb"raw"b_raw"access_token"access_tokenb"access_token"b_access_token_tokensuffix still matches)b"api_key"b_api_keySo the escape is name-dependent, not universal: it defeats the exact-equality rules (
raw) and the family membership checks (api_keyflattens tobapikey, which is in no family), while accidentally surviving the suffix rules. The tests in #5070 deliberately do not pin the accepted cases, because encoding them as expected behaviour would make the hole harder to close.Reachability
I did not find a caller that passes non-
strkeys today: the payloads reaching this function are JSON-shaped status and projection documents, whose keys are strings after parsing. That is why this is reported as a boundary-consistency defect rather than a demonstrated leak. The function is reached fromloopx/control_plane/runtime/public_safety.pyitself and fromloopx/capabilities/reliability_diagnostics/envelope.py, and it is a fail-closed guard whose whole value depends on the caller not having to think about key types.Desired outcome / 期望行为
One rule for what a field name is. Either reject a mapping whose key is not a
strbefore any classification, with a message that says the key type is not nameable, or decode abyteskey explicitly and then classify it. Do not rely onstr(key), whosereprwrapper is what moves the name out of the rules.The existing behaviour for
strkeys must not move in either direction, and whatever shape is chosen should be stated in the public-safety contract rather than left to the fold.Acceptance / 验收
bytesthat names a credential or raw-payload field is not accepted as public-safe.raw) and a family member (api_key), since those are the two clauses the current fold defeats.str-key case in the existing public-safety tests still passes unchanged, including the spelling-equivalence set added in test(public-safety): pin that a credential key is one key in any spelling #5070.strkey does not become a false positive if the decision is to decode rather than reject; if the decision is to reject, the message names the key type instead of pretending the name was classified.