From 35071150b7ba98f7fc5d7cae22f60931eb9e01f2 Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Fri, 20 Mar 2026 05:42:32 -0700 Subject: [PATCH] fix: sanitize SessionStart session summaries (#710) --- scripts/hooks/session-start.js | 5 ++--- tests/hooks/hooks.test.js | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/scripts/hooks/session-start.js b/scripts/hooks/session-start.js index a724e1ea..9f949616 100644 --- a/scripts/hooks/session-start.js +++ b/scripts/hooks/session-start.js @@ -40,11 +40,10 @@ async function main() { log(`[SessionStart] Latest: ${latest.path}`); // Read and inject the latest session content into Claude's context - const content = readFile(latest.path); + const content = stripAnsi(readFile(latest.path)); if (content && !content.includes('[Session context goes here]')) { // Only inject if the session has actual content (not the blank template) - // Strip ANSI escape codes that may have leaked from terminal output (#642) - output(`Previous session summary:\n${stripAnsi(content)}`); + output(`Previous session summary:\n${content}`); } } diff --git a/tests/hooks/hooks.test.js b/tests/hooks/hooks.test.js index 19b7526d..3fe58f07 100644 --- a/tests/hooks/hooks.test.js +++ b/tests/hooks/hooks.test.js @@ -415,6 +415,36 @@ async function runTests() { passed++; else failed++; + if ( + await asyncTest('strips ANSI escape codes from injected session content', async () => { + const isoHome = path.join(os.tmpdir(), `ecc-ansi-start-${Date.now()}`); + const sessionsDir = path.join(isoHome, '.claude', 'sessions'); + fs.mkdirSync(sessionsDir, { recursive: true }); + fs.mkdirSync(path.join(isoHome, '.claude', 'skills', 'learned'), { recursive: true }); + + const sessionFile = path.join(sessionsDir, '2026-02-11-winansi00-session.tmp'); + fs.writeFileSync( + sessionFile, + '\x1b[H\x1b[2J\x1b[3J# Real Session\n\nI worked on \x1b[1;36mWindows terminal handling\x1b[0m.\x1b[K\n' + ); + + try { + const result = await runScript(path.join(scriptsDir, 'session-start.js'), '', { + HOME: isoHome, + USERPROFILE: isoHome + }); + assert.strictEqual(result.code, 0); + assert.ok(result.stdout.includes('Previous session summary'), 'Should inject real session content'); + assert.ok(result.stdout.includes('Windows terminal handling'), 'Should preserve sanitized session text'); + assert.ok(!result.stdout.includes('\x1b['), 'Should not emit ANSI escape codes'); + } finally { + fs.rmSync(isoHome, { recursive: true, force: true }); + } + }) + ) + passed++; + else failed++; + if ( await asyncTest('reports learned skills count', async () => { const isoHome = path.join(os.tmpdir(), `ecc-skills-start-${Date.now()}`);