From e6ec050ec3921cfcf1503d349d7596cfc320f748 Mon Sep 17 00:00:00 2001 From: Pete Christianson Date: Wed, 4 Mar 2026 00:42:08 -0800 Subject: [PATCH] Fix XSS in memory-browser-panel Replace dangerouslySetInnerHTML with React elements for inline formatting (bold/italic). New renderInlineFormatting() helper returns React nodes instead of raw HTML strings, eliminating XSS risk from user-controlled memory content. Co-Authored-By: Claude Opus 4.6 --- .../panels/memory-browser-panel.tsx | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/components/panels/memory-browser-panel.tsx b/src/components/panels/memory-browser-panel.tsx index 7eeba6e..5980125 100644 --- a/src/components/panels/memory-browser-panel.tsx +++ b/src/components/panels/memory-browser-panel.tsx @@ -271,6 +271,30 @@ export function MemoryBrowserPanel() { )) } + const renderInlineFormatting = (text: string): React.ReactNode[] => { + const parts: React.ReactNode[] = [] + const regex = /(\*\*.*?\*\*|\*.*?\*)/g + let lastIndex = 0 + let match: RegExpExecArray | null + let key = 0 + while ((match = regex.exec(text)) !== null) { + if (match.index > lastIndex) { + parts.push(text.slice(lastIndex, match.index)) + } + const m = match[0] + if (m.startsWith('**') && m.endsWith('**')) { + parts.push({m.slice(2, -2)}) + } else if (m.startsWith('*') && m.endsWith('*')) { + parts.push({m.slice(1, -1)}) + } + lastIndex = regex.lastIndex + } + if (lastIndex < text.length) { + parts.push(text.slice(lastIndex)) + } + return parts + } + const renderMarkdown = (content: string) => { // Improved markdown rendering with proper line handling const lines = content.split('\n') @@ -323,20 +347,10 @@ export function MemoryBrowserPanel() { elements.push(
) } else if (trimmedLine.length > 0) { if (inList) inList = false - // Handle inline formatting — escape HTML entities first to prevent XSS - let content = trimmedLine - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, ''') - // Simple bold formatting - content = content.replace(/\*\*(.*?)\*\*/g, '$1') - // Simple italic formatting - content = content.replace(/\*(.*?)\*/g, '$1') - elements.push( -

+

+ {renderInlineFormatting(trimmedLine)} +

) } }