Skip to content

[Bug]: a bytes field name is classified through its repr, so it can escape the public-safety rules #5069

Description

@JunZ-Leo

Problem / 问题

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.
  • Every 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.
  • 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions