From b1e9028e4fb032170c8ac711800eebb821cee652 Mon Sep 17 00:00:00 2001 From: Lex Date: Mon, 19 Oct 2026 19:03:50 +0700 Subject: [PATCH] fix(errors): redact bearer tokens in error message/nextAction and secret-keyed string arrays (#420) --- src/lib/errors.ts | 6 +++--- src/lib/redact.ts | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/lib/errors.ts b/src/lib/errors.ts index b87452ac..6913a370 100644 --- a/src/lib/errors.ts +++ b/src/lib/errors.ts @@ -256,12 +256,12 @@ export class ApiError extends CLIError { apiUrl?: string, serverNextAction?: string, ) { - super(envelope.message, exitCodeFor(envelope.code)); + super(redactDeep(envelope.message) as string, exitCodeFor(envelope.code)); this.name = 'ApiError'; this.code = envelope.code; this.requestId = envelope.requestId; - this.nextAction = envelope.nextAction; - this.serverNextAction = serverNextAction ?? envelope.nextAction; + this.nextAction = redactDeep(envelope.nextAction) as string; + this.serverNextAction = redactDeep(serverNextAction ?? envelope.nextAction) as string; // Redact at the single point every ApiError is constructed — server // envelopes (fromEnvelope), locally-fabricated ones (authRequired, // localValidationError), and any future direct `new ApiError(...)` — so diff --git a/src/lib/redact.ts b/src/lib/redact.ts index e7de38b1..b66359b3 100644 --- a/src/lib/redact.ts +++ b/src/lib/redact.ts @@ -139,7 +139,13 @@ export function redactDeep(value: T): T { result[key] = v; continue; } - result[key] = isSecretKey(key) && typeof v === 'string' ? REDACTED : redactDeep(v); + if (isSecretKey(key) && typeof v === 'string') { + result[key] = REDACTED; + } else if (isSecretKey(key) && Array.isArray(v)) { + result[key] = v.map(item => (typeof item === 'string' ? REDACTED : redactDeep(item))); + } else { + result[key] = redactDeep(v); + } } return result as unknown as T; }