From ac54c63cb0dd6569143595840fc35ad12c245037 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 21 Oct 2025 22:46:57 +0000 Subject: [PATCH] security(council): sanitize console logs to prevent sensitive data exposure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit addresses security vulnerabilities in console logging that could expose sensitive information such as API keys, stack traces, user data, and system internals. Changes: 1. Error object logging (lines 156, 208, 219, 251, 269, 831, 836) - Changed from: err?.message || err - Changed to: err?.message || 'unknown error' - Prevents logging full error objects with stack traces and system paths 2. Full error object parameters (lines 603, 640) - Changed from: googleErr.message || googleErr / toolErr.message || toolErr - Changed to: googleErr.message || 'unknown error' - Prevents exposing API credentials or system internals in error objects 3. Perplexity error handling (line 719) - Changed from: String(perplexityErr) - Changed to: 'Unknown Perplexity error' - Prevents exposing API keys or authentication details 4. Lens issues logging (line 818) - Changed from: Logging full issue objects with content - Changed to: Logging only issue types and counts - Prevents exposing user input or LLM response content All changes include SECURITY comments explaining the rationale for each sanitization to maintain code clarity and prevent regression. Security Impact: - Prevents leak of file system paths - Prevents leak of API keys in error messages - Prevents leak of user data or PII - Prevents leak of LLM responses - Reduces attack surface for information disclosure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- backend/council.js | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/backend/council.js b/backend/council.js index 70afcd42..baffc6df 100644 --- a/backend/council.js +++ b/backend/council.js @@ -153,7 +153,8 @@ async function ensureVectorIndex() { vectorIndexReady = true; } catch (err) { // eslint-disable-next-line no-console - console.warn(`[council] ensureIndex failed: ${err?.message || err}`); + // SECURITY: Only log error message to prevent stack trace/system path exposure + console.warn(`[council] ensureIndex failed: ${err?.message || 'unknown error'}`); } } @@ -205,7 +206,8 @@ async function captureMemoryEntry({ text, agent, role, tags = [], meta = {}, rou await vectorMemory.upsertDocs([doc]); } catch (err) { // eslint-disable-next-line no-console - console.warn(`[council] memory upsert failed: ${err?.message || err}`); + // SECURITY: Only log error message to prevent stack trace/system path exposure + console.warn(`[council] memory upsert failed: ${err?.message || 'unknown error'}`); } return; } @@ -216,7 +218,8 @@ async function captureMemoryEntry({ text, agent, role, tags = [], meta = {}, rou noteMemory.add(body, tagList, MEMORY_SOURCE, baseMeta); } catch (err) { // eslint-disable-next-line no-console - console.warn(`[council] note memory add failed: ${err?.message || err}`); + // SECURITY: Only log error message to prevent stack trace/system path exposure + console.warn(`[council] note memory add failed: ${err?.message || 'unknown error'}`); } } } @@ -248,7 +251,8 @@ async function recallMemoryEntries({ agent, role = "agent_response", query } = { .filter(Boolean); } catch (err) { // eslint-disable-next-line no-console - console.warn(`[council] memory recall failed: ${err?.message || err}`); + // SECURITY: Only log error message to prevent stack trace/system path exposure + console.warn(`[council] memory recall failed: ${err?.message || 'unknown error'}`); } } else if (noteMemory && typeof noteMemory.recall === "function") { try { @@ -266,7 +270,8 @@ async function recallMemoryEntries({ agent, role = "agent_response", query } = { .filter(Boolean); } catch (err) { // eslint-disable-next-line no-console - console.warn(`[council] note memory recall failed: ${err?.message || err}`); + // SECURITY: Only log error message to prevent stack trace/system path exposure + console.warn(`[council] note memory recall failed: ${err?.message || 'unknown error'}`); } } return []; @@ -595,10 +600,11 @@ async function runWithCouncil(raw){ } } } catch (googleErr) { - console.warn(`[council] Google ${id} route failed:`, googleErr.message || googleErr); + // SECURITY: Only log error message to prevent exposing API credentials or system internals + console.warn(`[council] Google ${id} route failed:`, googleErr.message || 'unknown error'); return { agent: id, - output: `Google ${id} error: ${googleErr.message || googleErr}`, + output: `Google ${id} error: ${googleErr.message || 'An error occurred'}`, meta: { error: true, routed: "google" } }; } @@ -631,7 +637,8 @@ async function runWithCouncil(raw){ } } } catch (toolErr) { - console.warn(`[council] ${id} tool route failed, using Claude fallback:`, toolErr.message || toolErr); + // SECURITY: Only log error message to prevent exposing API credentials or system internals + console.warn(`[council] ${id} tool route failed, using Claude fallback:`, toolErr.message || 'unknown error'); } } @@ -709,7 +716,8 @@ async function runWithCouncil(raw){ }); } } catch (perplexityErr) { - const message = perplexityErr?.message || String(perplexityErr); + // SECURITY: Only extract error message to prevent exposing API keys or system internals + const message = perplexityErr?.message || 'Unknown Perplexity error'; console.warn(`[council] Perplexity call failed:`, message); perplexityMeta = { tool: toolName, @@ -808,7 +816,12 @@ async function runWithCouncil(raw){ // Log issues if any (but don't fail the request) if (!result.passed && result.totalIssues.length > 0) { - console.log(`[council] Lens issues for @${id}:`, result.totalIssues.slice(0, 3)); + // SECURITY: Only log issue count and types to prevent exposing user data or LLM response content + const issueSummary = result.totalIssues.slice(0, 3).map(issue => ({ + lens: issue.lens || 'unknown', + type: issue.type || issue.severity || 'issue' + })); + console.log(`[council] Lens issues for @${id} (${result.totalIssues.length} total):`, issueSummary); } // Capture response with lens metadata @@ -828,7 +841,8 @@ async function runWithCouncil(raw){ } }); } catch (lensErr) { - console.warn(`[council] lens framework failed:`, lensErr.message || lensErr); + // SECURITY: Only log error message to prevent stack trace/system path exposure + console.warn(`[council] lens framework failed:`, lensErr.message || 'unknown error'); // Still capture response even if lenses fail await captureMemoryEntry({ text: finalOutput,