Conversation
classifyError now runs the finished envelope through core's redactUrlQuery: the url field, plus every http(s) URL embedded in message and cause. Classification still reads the unredacted text. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Critical redaction gaps remain for uppercase URL schemes and TUI-displayed errors.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 2
Open (2)
What changed in this PR
Adds URL query-secret redaction to CLI JSON error envelopes while preserving error classification behavior.
Changes:
- Redacts URLs in envelope fields, messages, causes, and stderr output.
- Adds focused redaction and serialization tests.
| File | Review |
|---|---|
clients/cli/src/error-handler.ts |
Implements redaction. Unresolved: uppercase schemes bypass redaction (critical, 4 votes); trailing punctuation may leak credential suffixes (moderate, 1 vote); TUI errors remain unredacted (critical, 1 vote). |
clients/cli/__tests__/error-handler.test.ts |
Tests envelope redaction, classification, and serialization; regression coverage is still needed for uppercase schemes and credential-ending punctuation. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
…review) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
|
Copilot round 1
|
… review) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
|
Copilot round 2
|
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
OAuth state can still leak, and embedded URL redaction can corrupt closing delimiters.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (1)
Resolved since last review (2)
| ...(envelope.cause !== undefined && { | ||
| cause: redactUrlsInText(envelope.cause), | ||
| }), | ||
| ...(envelope.url !== undefined && { url: redactUrlQuery(envelope.url) }), |
There was a problem hiding this comment.
Declined, with no change. state is excluded from SENSITIVE_BODY_FIELDS on purpose, and the core test for redactUrlQuery asserts that it survives.
stateis a single-use CSRF nonce bound to the client session that started the flow. It is not a credential: holding it grants nothing, and it is dead once the callback has been handled.- CLI/TUI error output may not apply the same URL-redaction as the web client's OAuth timeout path #2423 asks the CLI for parity with the web client, and its own suggested fix is to reuse
redactUrlQuery. That is exactly what this PR does. - Adding
statewould change the redaction policy of the shared helper, and with it what the web client's Network log records. That is a cross-client policy decision beyond this issue, not a CLI defect.
|
Copilot round 3: review loop closed
The loop stops here: this round held only a finding declined as out of scope (pr-flow 7c). |

Closes #2423
What changed
The CLI's JSON error envelope (
{"error":{...}}on stderr) was built fromcontext.url, aCliExitCodeError's ownenvelope.url, and the rawerror.message/causechain with no redaction. That meant an OAuthcode,access_token,client_secretand so on in a server URL could be echoed verbatim to a terminal, a CI log, or a pipe.classifyErrorinclients/cli/src/error-handler.tsnow passes the finished envelope through core's existingredactUrlQuery, the same helper the web client's Network log andOAuthRequestTimeoutErroralready use:url: redacted directly.messageandcause: every embeddedhttp(s)://URL is redacted in place. Trailing sentence punctuation is split off first and put back, so…?code=abc.keeps its full stop.Both
formatErrorOutputandhandleErrorgo throughclassifyError, so both sinks are covered. Classification still reads the unredacted text. Redaction runs on the output rather than the input, because redaction rewrites parameter values, and a pattern test on the rewritten copy could produce a different exit code. A test pins this.TUI (and web) on-screen errors: out of scope, tracked in #2490
The TUI does not write errors to a serialized or piped sink the way the CLI envelope does. It renders error text onto its own Ink screen, and so does the web client, which puts
err.messagestraight into toasts unredacted. The web client appliesredactUrlQueryin only two places: the Network log andOAuthRequestTimeoutError. The TUI gets the latter already scrubbed. So the TUI is at parity with the web client today, and the gap parity left open was the CLI envelope.Redacting displayed error text in both interactive clients is new work, tracked in #2490.
Tests
clients/cli/__tests__/error-handler.test.tsgains anenvelope URL redactionblock covering:CliExitCodeError's own urlnpm run local:gatepasses.error-handler.tscoverage is 100 / 95.3 / 100 / 100.🤖 Generated with Claude Code