From 9dccb4c9ad1396fef68e0ff881fd116d997d921b Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 11 Feb 2026 04:52:38 +0000 Subject: [PATCH] fix: address PR #144 code review feedback Addresses all concerns raised by Copilot and Codex reviewers: **Type Safety (Copilot #1)**: - Remove dynamic property access `logger[logLevel]` - Use explicit if/else with proper type checking **Visibility (Codex #1)**: - Non-retry errors (protobuf, parsing) always log as ERROR - Remove warn downgrade for unexpected errors **Defensive Coding (Copilot #3)**: - Use `String(originalError?.message ?? originalError)` - Prevents crashes on undefined/null message property **Scope Creep (Codex #2)**: - Add stack trace validation to console.log override - Only suppress logs originating from libsignal module - Prevents affecting unrelated application logs **Logic Clarity (Copilot #2)**: - Clarify that onRetry hooks handle intermediate logging - Maintain clear distinction between retry vs non-retry errors https://claude.ai/code/session_01SoNUGBEWbJwWWws3F2fuzh --- src/Signal/libsignal.ts | 37 ++++++++++++++++++++-------------- src/Utils/decode-wa-message.ts | 16 +++++++-------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/Signal/libsignal.ts b/src/Signal/libsignal.ts index 7b590b18..b098a2a8 100644 --- a/src/Signal/libsignal.ts +++ b/src/Signal/libsignal.ts @@ -30,22 +30,29 @@ console.log = function(...args: unknown[]) { if (args.length > 0 && typeof args[0] === 'string') { const msg = args[0] - // Suppress session lifecycle dumps (cryptographic details on every encrypt/decrypt) - if (msg.startsWith('Closing session')) { - return - } + // Check if this log comes from libsignal by examining the call stack + // This prevents suppressing unrelated logs from other modules + const stack = new Error().stack || '' + const isFromLibsignal = stack.includes('libsignal') || stack.includes('session_cipher') - // Suppress transient decryption errors that are auto-recovered by retry logic - // These flood logs during normal operation when sessions are being re-established - // Final failures are still logged via our structured logger (see decode-wa-message.ts) - if ( - msg.includes('Session error') || - msg.includes('Bad MAC') || - msg.includes('MessageCounterError') || - msg.includes('Key used already or never filled') || - msg.includes('Failed to decrypt message with any known session') - ) { - return // suppress - our retry system handles these gracefully + if (isFromLibsignal) { + // Suppress session lifecycle dumps (cryptographic details on every encrypt/decrypt) + if (msg.startsWith('Closing session')) { + return + } + + // Suppress transient decryption errors that are auto-recovered by retry logic + // These flood logs during normal operation when sessions are being re-established + // Final failures are still logged via our structured logger (see decode-wa-message.ts) + if ( + msg.includes('Session error') || + msg.includes('Bad MAC') || + msg.includes('MessageCounterError') || + msg.includes('Key used already or never filled') || + msg.includes('Failed to decrypt message with any known session') + ) { + return // suppress - our retry system handles these gracefully + } } } diff --git a/src/Utils/decode-wa-message.ts b/src/Utils/decode-wa-message.ts index d643701c..1ff3f7d3 100644 --- a/src/Utils/decode-wa-message.ts +++ b/src/Utils/decode-wa-message.ts @@ -405,12 +405,10 @@ export const decryptMessageNode = ( ...(isRetryExhausted && { retriesExhausted: true, attempts: err.attempts }) } - // Smart logging: Only show ERROR on final failure - // During retries, libsignal may log internally - we can't suppress those - // But we can avoid adding our own duplicate error logs for each retry + // Smart logging based on error type and retry status if (isCorrupted) { // Corrupted session errors are expected and auto-recovered - // Only log as ERROR if this is the final failure after all retries + // Only log as ERROR if retries exhausted, otherwise WARN on first attempt if (isRetryExhausted) { logger.error( errorContext, @@ -450,9 +448,10 @@ export const decryptMessageNode = ( logger.debug(errorContext, 'No session record - will retry') } } else { - // Unknown/unexpected error - always log as error - const logLevel = isRetryExhausted ? 'error' : 'warn' - logger[logLevel]( + // Unknown/unexpected errors (protobuf, parsing, etc.) + // These don't go through retry, so always log as ERROR + // Type-safe explicit logging instead of dynamic property access + logger.error( errorContext, isRetryExhausted ? `Failed to decrypt message after ${err.attempts} attempts` @@ -461,7 +460,8 @@ export const decryptMessageNode = ( } fullMessage.messageStubType = proto.WebMessageInfo.StubType.CIPHERTEXT - fullMessage.messageStubParameters = [originalError.message.toString()] + // Safe coercion handling edge cases where message might be undefined + fullMessage.messageStubParameters = [String(originalError?.message ?? originalError)] } } }