From 7623dd5b66ed3ae263f3178478ee49e487c92fe1 Mon Sep 17 00:00:00 2001 From: sickn33 Date: Thu, 19 Mar 2026 19:39:23 +0100 Subject: [PATCH] fix: harden JSON-LD insertion in prerender route generation --- apps/web-app/scripts/prerender-routes.js | 45 ++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/apps/web-app/scripts/prerender-routes.js b/apps/web-app/scripts/prerender-routes.js index 957f06b9..d66d30d3 100644 --- a/apps/web-app/scripts/prerender-routes.js +++ b/apps/web-app/scripts/prerender-routes.js @@ -72,7 +72,48 @@ function escapeHtml(value) { } function escapeScriptJson(value) { - return String(value).replace(/<\/script/gi, '<\\/script'); + return String(value) + .replaceAll('<', '\\u003c') + .replaceAll('>', '\\u003e') + .replaceAll('&', '\\u0026') + .replaceAll('\u2028', '\\u2028') + .replaceAll('\u2029', '\\u2029'); +} + +function removeExistingJsonLdScripts(html) { + const marker = 'data-seo-jsonld="true"'; + let remaining = html; + let lowered = html.toLowerCase(); + + while (true) { + const markerIndex = lowered.indexOf(marker); + if (markerIndex === -1) { + return remaining; + } + + const openTagStart = lowered.lastIndexOf('', markerIndex); + if (openTagEnd === -1) { + return remaining; + } + + const closeTagStart = lowered.indexOf('', closeTagStart + 8); + if (closeTagEnd === -1) { + return remaining; + } + + remaining = `${remaining.slice(0, openTagStart)}${remaining.slice(closeTagEnd + 1)}`; + lowered = remaining.toLowerCase(); + } } function replaceHtmlTag(html, pattern, replacement, insertionPoint) { @@ -103,7 +144,7 @@ function setTitleTag(html, title) { } function setJsonLdTag(html, payload) { - const cleaned = html.replace(/]*data-seo-jsonld="true"[^>]*>[\s\S]*?<\/script>\s*/g, ''); + const cleaned = removeExistingJsonLdScripts(html); const tag = ``; return cleaned.replace('', `\n${tag}\n`); }