From ee331ac5caae41ee4fe886133e4029e06233f2e0 Mon Sep 17 00:00:00 2001 From: Clawyered Date: Mon, 16 Feb 2026 08:58:40 -0500 Subject: [PATCH 1/3] web: add integrity check for marked CDN and cap highlight regex input --- src/channels/web/static/app.js | 4 ++-- src/channels/web/static/index.html | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/channels/web/static/app.js b/src/channels/web/static/app.js index cdc1e06ddce..b3108771a46 100644 --- a/src/channels/web/static/app.js +++ b/src/channels/web/static/app.js @@ -1038,11 +1038,11 @@ function snippetAround(text, query, len) { function highlightQuery(text, query) { if (!query) return escapeHtml(text); const escaped = escapeHtml(text); - const queryEscaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const normalizedQuery = query.slice(0, 100); + const queryEscaped = normalizedQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const re = new RegExp('(' + queryEscaped + ')', 'gi'); return escaped.replace(re, '$1'); } - // --- Logs --- const LOG_MAX_ENTRIES = 2000; diff --git a/src/channels/web/static/index.html b/src/channels/web/static/index.html index bf6c227ced0..ddcf689291c 100644 --- a/src/channels/web/static/index.html +++ b/src/channels/web/static/index.html @@ -5,7 +5,11 @@ IronClaw - + From 2a840d1ba8001868e8362b13e9db3df240cf634e Mon Sep 17 00:00:00 2001 From: Clawyered Date: Mon, 16 Feb 2026 09:37:52 -0500 Subject: [PATCH 2/3] web: normalize memory search query before snippet+highlight matching --- src/channels/web/static/app.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/channels/web/static/app.js b/src/channels/web/static/app.js index b3108771a46..6388803106f 100644 --- a/src/channels/web/static/app.js +++ b/src/channels/web/static/app.js @@ -1000,10 +1000,15 @@ function buildBreadcrumb(path) { return html; } +const MEMORY_SEARCH_QUERY_MAX_LENGTH = 100; + function searchMemory(query) { + const normalizedQuery = normalizeSearchQuery(query); + if (!normalizedQuery) return; + apiFetch('/api/memory/search', { method: 'POST', - body: { query, limit: 20 }, + body: { query: normalizedQuery, limit: 20 }, }).then((data) => { const tree = document.getElementById('memory-tree'); tree.innerHTML = ''; @@ -1014,18 +1019,23 @@ function searchMemory(query) { for (const result of data.results) { const item = document.createElement('div'); item.className = 'search-result'; - const snippet = snippetAround(result.content, query, 120); + const snippet = snippetAround(result.content, normalizedQuery, 120); item.innerHTML = '
' + escapeHtml(result.path) + '
' - + '
' + highlightQuery(snippet, query) + '
'; + + '
' + highlightQuery(snippet, normalizedQuery) + '
'; item.addEventListener('click', () => readMemoryFile(result.path)); tree.appendChild(item); } }).catch(() => {}); } +function normalizeSearchQuery(query) { + return (typeof query === 'string' ? query : '').slice(0, MEMORY_SEARCH_QUERY_MAX_LENGTH); +} + function snippetAround(text, query, len) { + const normalizedQuery = normalizeSearchQuery(query); const lower = text.toLowerCase(); - const idx = lower.indexOf(query.toLowerCase()); + const idx = lower.indexOf(normalizedQuery.toLowerCase()); if (idx < 0) return text.substring(0, len); const start = Math.max(0, idx - Math.floor(len / 2)); const end = Math.min(text.length, start + len); @@ -1038,7 +1048,7 @@ function snippetAround(text, query, len) { function highlightQuery(text, query) { if (!query) return escapeHtml(text); const escaped = escapeHtml(text); - const normalizedQuery = query.slice(0, 100); + const normalizedQuery = normalizeSearchQuery(query); const queryEscaped = normalizedQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const re = new RegExp('(' + queryEscaped + ')', 'gi'); return escaped.replace(re, '$1'); From dbad9503e743c395fbe2470d3ffb908233678144 Mon Sep 17 00:00:00 2001 From: Clawyered Date: Mon, 16 Feb 2026 09:39:04 -0500 Subject: [PATCH 3/3] web: place memory query length constant with top-level config --- src/channels/web/static/app.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/channels/web/static/app.js b/src/channels/web/static/app.js index 6388803106f..398f2f540b9 100644 --- a/src/channels/web/static/app.js +++ b/src/channels/web/static/app.js @@ -12,6 +12,7 @@ let loadingOlder = false; let jobEvents = new Map(); // job_id -> Array of events let jobListRefreshTimer = null; const JOB_EVENTS_CAP = 500; +const MEMORY_SEARCH_QUERY_MAX_LENGTH = 100; // --- Auth --- @@ -1000,8 +1001,6 @@ function buildBreadcrumb(path) { return html; } -const MEMORY_SEARCH_QUERY_MAX_LENGTH = 100; - function searchMemory(query) { const normalizedQuery = normalizeSearchQuery(query); if (!normalizedQuery) return;