fix(cli): redact body values marked sensitive in diagnostics

Merged
2Y
10d ago
+4696-28

Stacked on #72; review that first. This PR's base is fix/terraform-sensitive-response-logs.

Why

--dry-run and --debug redact body values only by name (password, token, client_secret, …). A value marked sensitive in the schema (x-speakeasy-param-sensitive: true or format: password) but carrying a generic name such as value or passphrase was printed in clear text, and so was a whole body whose schema carries the marker.

What changed

  • Shared redaction: dry-run and debug previews pass request and response bodies through the embedded SDK's redact package from #72 (redact.RequestBody / redact.ResponseBody) before applying the existing name-based preview rules. The SDK stamps the operation ID on each request context, so the schema graph is selected per SDK method, and split request content types each use their own graph. The CLI no longer builds its own request-body graph.
  • Responses: debug response previews now mask schema-marked values too; previously only name-based rules applied.
  • Whole bodies: a body masked as a whole is shown as the mask ((sensitive)) instead of a byte count when it no longer parses as its content type.
  • JSON-encoded form fields: name-based rules apply inside them, so a property named like a credential stays hidden next to a schema-marked one; a field with nothing to redact is shown exactly as sent.
  • Schema-marked values read (sensitive) (the shared mask); name-based redaction still reads [REDACTED].
  • Docs: the CLI template README describes the redaction order and its limits (union variants are not told apart; a marker beside a $ref is not read).
  • Review SDK: zSDKs/sdk-cli/internal/client/diagnostics.go regenerated.

Testing

  • Primary fixture cli-sensitive-body-fields.yaml: marked fields at the top level, nested, in array items and map values, through self and mutual recursion, discriminated unions (including a recursive variant in both member orders), multipart and form bodies with JSON-encoded fields (including an unmarked password beside a marked field, and a marked property whose wire name ends in brackets), repeated scalar fields, split request content types (including an unmarked field that shares a marked field's name in another content type), whole bodies marked sensitive (JSON, form, multipart, text), sensitive additionalProperties beside declared fields, a union of a sensitive array and a plain object, and a response with a marked field.
  • TestDiagnosticsRedactSensitiveBodyFields runs each case under human dry-run, JSON dry-run and debug. TestDiagnosticsSensitiveBodyPreservesWireAndResponse checks the server still receives the secrets; TestDiagnosticsKeepUnmarkedFieldSharingSensitiveName checks unmarked fields stay readable; TestDebugRedactsSensitiveResponseFields checks debug hides a marked response value while decoded output keeps it.
  • TARGET=review make test-cli: 690 passed, 2 skipped.
  • TARGET=primary make test-cli: 1889 tests; the only failures are the four TestConfigure* tests, which depend on agent-mode detection being off and pass with the detection variables unset.

Public-safety check

  • This change contains no credentials, customer documents, private repository URLs, private filesystem paths, or unredacted private logs.
  • Title, body, comments, and commit messages name no customers or customer-derived identifiers, private paths or trackers, or workflow provenance, and are understandable without private context (.claude/skills/public-repo-communication/SKILL.md).
  • Generated fixtures and review SDK changes are public-safe.
  • I reviewed git diff --check.