diff --git a/.gitignore b/.gitignore index 7902578c..3985a9c3 100644 --- a/.gitignore +++ b/.gitignore @@ -200,3 +200,5 @@ cython_debug/ # Build metadata files - generated at build time GIT_SHA RUNTIME_INFO +node_modules +frontend/node_modules diff --git a/frontend/templates/files.html b/frontend/templates/files.html index 10892093..457040c1 100644 --- a/frontend/templates/files.html +++ b/frontend/templates/files.html @@ -1538,6 +1538,28 @@ }); } + function escapeHtml(str) { + if (!str) return ''; + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } + + function sanitizeHighlight(html) { + if (!html) return ''; + let safe = String(html) + .replace(//gi, '\x00MARK_OPEN\x00') + .replace(/<\/mark>/gi, '\x00MARK_CLOSE\x00'); + safe = escapeHtml(safe); + safe = safe + .replace(/\x00MARK_OPEN\x00/g, '') + .replace(/\x00MARK_CLOSE\x00/g, ''); + return safe; + } + function renderSearchResults(data, q) { const panel = document.getElementById('search-results-panel'); const list = document.getElementById('search-results-list'); @@ -1555,25 +1577,31 @@ list.innerHTML = results.map(hit => { const fmt = hit._formatted || {}; - const title = fmt.document_title || hit.document_title || hit.original_filename || __i18n.untitled; - const filename = fmt.original_filename || hit.original_filename || ''; - const snippet = fmt.ocr_text || ''; - const tags = Array.isArray(hit.tags) ? hit.tags.join(', ') : (hit.tags || ''); - const docType = hit.document_type || ''; + const titleRaw = fmt.document_title || hit.document_title || hit.original_filename || __i18n.untitled; + const filenameRaw = fmt.original_filename || hit.original_filename || ''; + const snippetRaw = fmt.ocr_text || ''; + const tagsRaw = Array.isArray(hit.tags) ? hit.tags.join(', ') : (hit.tags || ''); + const docTypeRaw = hit.document_type || ''; + + const safeTitle = fmt.document_title ? sanitizeHighlight(titleRaw) : escapeHtml(titleRaw); + const safeFilename = escapeHtml(filenameRaw); + const safeSnippet = sanitizeHighlight(snippetRaw); + const safeTags = escapeHtml(tagsRaw); + const safeDocType = escapeHtml(docTypeRaw); return `
-
${title}
- ${filename ? `
${filename}
` : ''} - ${docType ? `${docType}` : ''} - ${tags ? `${tags}` : ''} - ${snippet ? `
…${snippet}…
` : ''} +
${safeTitle}
+ ${safeFilename ? `
${safeFilename}
` : ''} + ${safeDocType ? `${safeDocType}` : ''} + ${safeTags ? `${safeTags}` : ''} + ${safeSnippet ? `
…${safeSnippet}…
` : ''}
- +