fix(CLI-40,CLI-41,CLI-44): exact-secret scrub, uniform malformed-reply contract, Go terminal-error mislabel

CLI-40: port the exact-secret credential scrub to Rust/Java/.NET (Go/Python
already did it). AuthenticateUser/WriteSecured(2) helpers now redact the exact
caller-supplied secret from any surfaced error, as defense-in-depth on top of the
by-construction guarantee. Rust hand-writes a redacting Debug (derived Debug would
leak the reply); Java/.NET rebuild the same exception type with the redacted
message and do not carry the secret-bearing original forward (so ToString/stack
traces stay clean too).

CLI-41: uniform malformed-reply contract for AuthenticateUser/ArchestrAUserToId/
AddBufferedItem across all five clients — typed payload, else a present int32
return_value, else a typed malformed-reply error. Fixes Go/Java silent-0, .NET
NRE, and Rust's own internal inconsistency.

CLI-44: the Go event goroutine's Recv-error path now uses a non-blocking
sendTerminalEventResult on the reserved slot, so a genuine terminal stream error
is reported as itself instead of being mislabeled ErrSlowConsumer under overflow.

Riders from the CLI-37/38 review: (a) .NET ToDiagnosticSummary and Python
_mxaccess_message surface the raw success member (diagnostics-only parity with
Rust); (b) the status-conversion fixture carries an independent wantSuccess
boolean and the Go/.NET fixture tests assert against it instead of recomputing
the formula under test.

Shared fixtures (authenticate-user.{echoed-credential,missing-payload,
return-value-only}.reply.json) + manifest + ClientBehaviorFixtures.md +
ClientLibrariesDesign.md updated in the same change. Tracking: CLI-40/41/44 -> Done.
This commit is contained in:
Joseph Doherty
2026-08-07 06:42:40 -04:00
parent d2bb32d97b
commit dc7fd16dd5
33 changed files with 1465 additions and 97 deletions
+35
View File
@@ -70,6 +70,35 @@ The rules those fixtures lock in are:
negative. Positive COM success codes such as `S_FALSE` pass, matching COM
semantics.
### Malformed-Reply And Credential-Redaction Conformance
Three further command reply fixtures pin the id/handle-extraction and
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.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 |
The rules those fixtures lock in are:
- **Malformed-reply extraction (CLI-41).** Every helper that extracts a scalar
id/handle (`AuthenticateUser`, `ArchestrAUserToId`, `AddBufferedItem`, and the
handle extractors) prefers the typed payload; when it is absent it falls back
to `return_value` **only** when `return_value` is present with the expected
int32 variant; when neither is present it raises a typed malformed-reply error.
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.
## Event Streams
Event stream fixtures live in
@@ -100,6 +129,12 @@ behavior. A language helper may expose native booleans, integers, strings,
arrays, and timestamps, but it must keep `rawDiagnostic`, raw data type fields,
and raw byte payloads accessible when conversion is incomplete.
Each status case also carries an independent `wantSuccess` boolean alongside its
`status` object. The success/failure conformance tests assert the helper's
verdict against this fixture-declared expectation rather than recomputing it from
`category` (the same formula under test), so a regression in the verdict rule
cannot hide behind a self-consistent computation.
## Auth, Timeout, And Cancel Behavior
Authentication fixtures live in `clients/proto/fixtures/behavior/auth/`. They
+13 -2
View File
@@ -126,11 +126,22 @@ rules across all five clients (see
failing before a prior `AuthenticateUser` + `AdviseSupervisory` surfaces the
native failure unchanged — the helper does not pre-validate or reorder it.
**Malformed-reply extraction:** the id/handle-returning helpers
(`AuthenticateUser`, `ArchestrAUserToId`, `AddBufferedItem`, and the handle
extractors) follow one contract across all five clients — prefer the typed
payload; fall back to `return_value` only when it is present with the expected
int32 variant; when neither is present, raise a typed malformed-reply error.
They never surface a proto3 default `0` and never throw a null-reference (CLI-41).
**Credential handling:** `AuthenticateUser` credentials and `WriteSecured`
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. Each client's test suite asserts a distinctive credential is
absent from any surfaced error.
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
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.
Shipped in all five clients (.NET / Go / Rust / Python / Java).