From 108bde99818893617dc79b2cc4db680ba6486c84 Mon Sep 17 00:00:00 2001 From: tulong66 Date: Sun, 24 May 2026 09:03:11 +0900 Subject: [PATCH] fix: redact localhost URLs before saving memories Prevent EverMem hub transcripts from hitting cloud 403 responses by removing localhost URLs from stop-hook payloads before upload. Co-Authored-By: Claude Opus 4.7 --- hooks/scripts/store-memories.js | 10 ++++- scripts/test-redact-sensitive-urls.js | 65 +++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 scripts/test-redact-sensitive-urls.js diff --git a/hooks/scripts/store-memories.js b/hooks/scripts/store-memories.js index 6924902..c91a203 100755 --- a/hooks/scripts/store-memories.js +++ b/hooks/scripts/store-memories.js @@ -92,6 +92,12 @@ try { return text && text.trim().length > 0; } + function redactSensitiveUrls(text) { + if (!text) return text; + + return text.replace(/\bhttps?:\/\/(localhost|127\.0\.0\.1|\[::1\])(?::\d+)?(?:\/[^\s"'`]*)?/gi, '[LOCAL_URL_REDACTED]'); + } + /** * Extract the last turn's user input and assistant response * @@ -192,8 +198,8 @@ try { // Extract the last turn's content const lastTurn = extractLastTurn(lines); - const lastUser = lastTurn.user; - const lastAssistant = lastTurn.assistant; + const lastUser = redactSensitiveUrls(lastTurn.user); + const lastAssistant = redactSensitiveUrls(lastTurn.assistant); debug('extracted:', { userLength: lastUser?.length || 0, diff --git a/scripts/test-redact-sensitive-urls.js b/scripts/test-redact-sensitive-urls.js new file mode 100644 index 0000000..b67bf6d --- /dev/null +++ b/scripts/test-redact-sensitive-urls.js @@ -0,0 +1,65 @@ +#!/usr/bin/env node + +import { mkdtempSync, writeFileSync, readFileSync, rmSync, existsSync } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { spawnSync } from 'child_process'; + +const pluginRoot = '/Users/deepzen/projects/evermem-claude-code'; +const storeScript = join(pluginRoot, 'hooks/scripts/store-memories.js'); +const tempDir = mkdtempSync(join(tmpdir(), 'evermem-redact-')); +const transcriptPath = join(tempDir, 'transcript.jsonl'); +const debugLogPath = '/tmp/evermem-debug.log'; + +const transcript = [ + JSON.stringify({ + type: 'user', + message: { + content: 'evermem:hub\n/evermem:hub\nOpen http://localhost:3456/?key=${EVERMEM_API_KEY}' + } + }), + JSON.stringify({ + type: 'assistant', + message: { + content: [ + { type: 'text', text: 'Memory Hub 服务已启动。\n\nhttp://localhost:3456/?key=fake-secret-key' } + ] + } + }) +].join('\n'); + +writeFileSync(transcriptPath, transcript + '\n', 'utf8'); +writeFileSync(debugLogPath, '', 'utf8'); + +const result = spawnSync('node', [storeScript], { + input: JSON.stringify({ transcript_path: transcriptPath, cwd: '/Users/deepzen' }), + encoding: 'utf8', + env: { + ...process.env, + EVERMEM_DEBUG: '1', + EVERMEM_API_KEY: process.env.EVERMEM_API_KEY || '' + } +}); + +const stdout = result.stdout || ''; +const debugLog = existsSync(debugLogPath) ? readFileSync(debugLogPath, 'utf8') : ''; + +try { + if (stdout.includes('FAILED (403)')) { + console.error('Expected sensitive URL content to be redacted before save, but save still failed with 403.'); + if (debugLog) console.error(debugLog); + process.exit(1); + } + + const leakedLocalUrlPattern = /https?:\/\/(localhost|127\.0\.0\.1|\[::1\])/i; + if (leakedLocalUrlPattern.test(debugLog)) { + console.error('Expected debug log to avoid leaking raw localhost URLs.'); + console.error(debugLog); + process.exit(1); + } + + console.log('Sensitive URL content was redacted before save.'); + process.exit(0); +} finally { + rmSync(tempDir, { recursive: true, force: true }); +}