(function () { 'use strict'; const BLOCKED_TAGS = new Set([ 'SCRIPT', 'IFRAME', 'OBJECT', 'EMBED', 'APPLET', 'META', 'BASE' ]); const URL_ATTRS = new Set([ 'href', 'src', 'xlink:href', 'formaction', 'poster' ]); function resolveElement(selectorOrElement) { if (!selectorOrElement) return null; if (typeof selectorOrElement === 'string') { return document.querySelector(selectorOrElement); } return selectorOrElement; } function escapeHTML(value) { return String(value ?? '') .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } function clear(selectorOrElement) { const el = resolveElement(selectorOrElement); if (!el) return; el.replaceChildren(); } function setText(selectorOrElement, value) { const el = resolveElement(selectorOrElement); if (!el) return; el.textContent = value ?? ''; } function setMessage(selectorOrElement, message, className) { const el = resolveElement(selectorOrElement); if (!el) return; const div = document.createElement('div'); div.className = className || 'empty'; div.textContent = message ?? ''; el.replaceChildren(div); } function isDangerousUrl(value) { const normalized = String(value || '') .trim() .replace(/[\u0000-\u001F\u007F\s]+/g, '') .toLowerCase(); return ( normalized.startsWith('javascript:') || normalized.startsWith('vbscript:') || normalized.startsWith('data:text/html') || normalized.startsWith('data:application/xhtml') ); } function sanitizeTree(root) { const walker = document.createTreeWalker( root, NodeFilter.SHOW_ELEMENT, null ); const elements = []; while (walker.nextNode()) { elements.push(walker.currentNode); } for (const el of elements) { if (BLOCKED_TAGS.has(el.tagName)) { el.remove(); continue; } for (const attr of Array.from(el.attributes)) { const name = attr.name.toLowerCase(); const value = attr.value; if (name.startsWith('on')) { el.removeAttribute(attr.name); continue; } if (URL_ATTRS.has(name) && isDangerousUrl(value)) { el.removeAttribute(attr.name); continue; } if (name === 'style' && /expression\s*\(|url\s*\(\s*javascript:/i.test(value)) { el.removeAttribute(attr.name); continue; } } } } function setBadge(selectorOrElement, message, className) { const el = resolveElement(selectorOrElement); if (!el) return; const span = document.createElement('span'); span.className = className || ''; span.textContent = message ?? ''; el.replaceChildren(span); } function setHTML(selectorOrElement, html) { const el = resolveElement(selectorOrElement); if (!el) return; const template = document.createElement('template'); template.innerHTML = String(html ?? ''); sanitizeTree(template.content); el.replaceChildren(template.content.cloneNode(true)); } window.EngeradiosDOM = { escapeHTML, clear, setText, setMessage, setBadge, setHTML }; })();