fix(CLI-40): scrub the credential from the redacted error's structured reply, route MXACCESS_FAILURE to MxAccess (Rust), fix Go Subscribe terminal-error drop
Code-review follow-up on the CLI-40/41/44 branch. ISSUE 1 (all five, critical): the message-only scrub still leaked the server-echoed credential through the redacted error's structured reply accessor (.NET Reply/Statuses, Java reply()/protocolStatus(), Go MxAccessError.Reply via errors.As, Rust reply()/into_reply(), Python raw_reply). The redacted error now carries a scrubbed clone of the reply (protocol_status.message, diagnostic_message, statuses[].diagnostic_text), with per-language tests asserting the reply accessor no longer contains the credential. ISSUE 2 (Rust, critical): ensure_command_success routed MXACCESS_FAILURE to Error::Command (unlike the other four clients), bypassing attach_secrets and leaking via derived Debug/Display. MXACCESS_FAILURE now routes to Error::MxAccess, fixing the cross-client inconsistency. ISSUE 3 (Go, important): the CLI-44 terminal send was unconditionally non-blocking, dropping a genuine terminal error under a full buffer on the never-drop SubscribeEvents path. It is now reserved-slot-non-blocking only for the cancel-on-overflow path and blocking for the never-drop path. New shared fixture authenticate-user.echoed-credential-mxaccess-failure.reply.json wired into all five suites. Minors: whitespace-secret guard on .NET/Java redact helpers; Java preserves exception subtype on redaction; redaction-helper unit tests (Go/Java/.NET). Docs (ClientBehaviorFixtures.md, ClientLibrariesDesign.md) updated to make the structured-field claim true.
This commit is contained in:
@@ -77,7 +77,8 @@ credential-redaction contracts for the credential-bearing helpers:
|
||||
|
||||
| Fixture | Reply | Expected behavior |
|
||||
|---|---|---|
|
||||
| `authenticate-user.echoed-credential.reply.json` | OK envelope, negative `hresult`, and the caller's credential echoed into `protocolStatus.message`, `statuses[0].diagnosticText`, and `diagnosticMessage` | the surfaced error redacts the exact secret (never leaks the verbatim value) |
|
||||
| `authenticate-user.echoed-credential.reply.json` | OK envelope, negative `hresult`, and the caller's credential echoed into `protocolStatus.message`, `statuses[0].diagnosticText`, and `diagnosticMessage` | the surfaced error redacts the exact secret from **both** the rendered message and the structured reply accessors (never leaks the verbatim value) |
|
||||
| `authenticate-user.echoed-credential-mxaccess-failure.reply.json` | the same echo, but coded `PROTOCOL_STATUS_CODE_MXACCESS_FAILURE` | identical redaction; confirms every client routes the MXAccess-failure protocol code to its MXAccess error type and scrubs it |
|
||||
| `authenticate-user.missing-payload.reply.json` | OK envelope, no `AuthenticateUser` payload, no `return_value` | a typed malformed-reply error, never a proto3 default `0` and never an NRE |
|
||||
| `authenticate-user.return-value-only.reply.json` | OK envelope, `return_value.int32_value = 7`, no typed payload | the id resolves to `7` via the legacy `return_value` compatibility path |
|
||||
|
||||
@@ -91,13 +92,17 @@ The rules those fixtures lock in are:
|
||||
It never surfaces a proto3 default `0` and never throws a null-reference.
|
||||
- **Credential redaction (CLI-40).** The credential-bearing helpers
|
||||
(`AuthenticateUser`, `WriteSecured`/`WriteSecured2`) scrub the exact secret
|
||||
values they were called with from any surfaced error text, replacing each
|
||||
occurrence with the client's redaction marker. This is defense-in-depth on top
|
||||
of the by-construction guarantee that exceptions carry reply-derived text, not
|
||||
the request. The marker is `<redacted>` in the Go, Rust, and Java clients and
|
||||
`[redacted]` in the Python client and the .NET CLI; the assertion each suite
|
||||
makes is that the surfaced message no longer contains the credential and does
|
||||
contain the client's marker.
|
||||
values they were called with from any surfaced error — both the rendered
|
||||
message text **and** the structured reply the error still exposes (a
|
||||
server-echoed credential lives in `protocolStatus.message` and
|
||||
`statuses[].diagnosticText`, which the error's raw-reply accessor would
|
||||
otherwise re-expose to a logger dumping structured fields). The redacted error
|
||||
therefore carries a scrubbed clone of the reply. This is defense-in-depth on
|
||||
top of the by-construction guarantee that exceptions carry reply-derived text,
|
||||
not the request. The marker is `<redacted>` in the Go, Rust, and Java clients
|
||||
and `[redacted]` in the Python client and the .NET CLI; each suite asserts that
|
||||
neither the surfaced message nor the exposed reply still contains the
|
||||
credential, and that the message contains the client's marker.
|
||||
|
||||
## Event Streams
|
||||
|
||||
|
||||
@@ -138,10 +138,16 @@ secured payloads route through each client's secret-redaction seam so they never
|
||||
reach logs, exception text, or `ToString`/`Debug`/`Display` — the value is carried
|
||||
only on the wire. In addition to that by-construction guarantee (exceptions carry
|
||||
reply-derived text, not the request), every client scrubs the **exact** secret
|
||||
values it was called with from any surfaced error text as defense-in-depth, so a
|
||||
values it was called with from any surfaced error as defense-in-depth, so a
|
||||
gateway or MXAccess diagnostic that echoes a credential back cannot leak it
|
||||
(CLI-40). Each client's test suite asserts a distinctive credential is absent
|
||||
from any surfaced error and that the redaction marker is present.
|
||||
(CLI-40). The scrub covers **both** the rendered message and the structured reply
|
||||
the error still exposes (`protocolStatus.message`, `statuses[].diagnosticText`,
|
||||
`diagnosticMessage`): the redacted error carries a scrubbed clone of the reply so
|
||||
a logger dumping the exception's structured fields cannot reintroduce the leak.
|
||||
This holds regardless of whether the reply is coded `OK` (with a failing HRESULT)
|
||||
or `MXACCESS_FAILURE` — every client routes both to its MXAccess error type. Each
|
||||
client's test suite asserts the distinctive credential is absent from both the
|
||||
surfaced message and the exposed reply, and that the redaction marker is present.
|
||||
|
||||
Shipped in all five clients (.NET / Go / Rust / Python / Java).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user