feat(sanitization): enhance HTML sanitization with improved DOMPurify configuration
- Updated the `sanitizeHTML` function to include additional sanitization options for better security. - Introduced a URL regex to validate links and added a hook to manage `rel` attributes for external links. - Ensured that only safe attributes and tags are retained, further preventing XSS vulnerabilities.
This commit is contained in:
parent
e958b3761d
commit
0bb9ee4327
1 changed files with 27 additions and 12 deletions
|
|
@ -65,16 +65,15 @@
|
||||||
// Global HTML sanitization function using DOMPurify
|
// Global HTML sanitization function using DOMPurify
|
||||||
window.sanitizeHTML = function(html) {
|
window.sanitizeHTML = function(html) {
|
||||||
if (!html) return '';
|
if (!html) return '';
|
||||||
|
const URL_RE = /^(https?:|mailto:)/i;
|
||||||
// Use DOMPurify with strict configuration for toast notifications
|
const config = {
|
||||||
const purified = DOMPurify.sanitize(html, {
|
ALLOWED_TAGS: ['a', 'b', 'br', 'code', 'del', 'div', 'em', 'i', 'p', 'pre', 's', 'span', 'strong',
|
||||||
ALLOWED_TAGS: ['a', 'b', 'br', 'code', 'del', 'div', 'em', 'i', 'p', 'pre', 's', 'span',
|
'u'
|
||||||
'strong', 'u'
|
|
||||||
],
|
],
|
||||||
ALLOWED_ATTR: ['class', 'href', 'target', 'title'],
|
ALLOWED_ATTR: ['class', 'href', 'target', 'title', 'rel'],
|
||||||
ALLOW_DATA_ATTR: false,
|
ALLOW_DATA_ATTR: false,
|
||||||
FORBID_TAGS: ['script', 'object', 'embed', 'applet', 'iframe', 'form', 'input', 'button',
|
FORBID_TAGS: ['script', 'object', 'embed', 'applet', 'iframe', 'form', 'input', 'button', 'select',
|
||||||
'select', 'textarea', 'details', 'summary', 'dialog', 'style'
|
'textarea', 'details', 'summary', 'dialog', 'style'
|
||||||
],
|
],
|
||||||
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onfocus', 'onblur', 'onchange',
|
FORBID_ATTR: ['onerror', 'onload', 'onclick', 'onmouseover', 'onfocus', 'onblur', 'onchange',
|
||||||
'onsubmit', 'ontoggle', 'style'
|
'onsubmit', 'ontoggle', 'style'
|
||||||
|
|
@ -82,10 +81,26 @@
|
||||||
KEEP_CONTENT: true,
|
KEEP_CONTENT: true,
|
||||||
RETURN_DOM: false,
|
RETURN_DOM: false,
|
||||||
RETURN_DOM_FRAGMENT: false,
|
RETURN_DOM_FRAGMENT: false,
|
||||||
SANITIZE_DOM: true
|
SANITIZE_DOM: true,
|
||||||
|
SANITIZE_NAMED_PROPS: true,
|
||||||
|
SAFE_FOR_TEMPLATES: true,
|
||||||
|
ALLOWED_URI_REGEXP: URL_RE
|
||||||
|
};
|
||||||
|
|
||||||
|
// One-time hook registration (idempotent pattern)
|
||||||
|
if (!window.__dpLinkHook) {
|
||||||
|
DOMPurify.addHook('afterSanitizeAttributes', node => {
|
||||||
|
if (node.nodeName === 'A' && node.hasAttribute('href')) {
|
||||||
|
const href = node.getAttribute('href') || '';
|
||||||
|
if (!URL_RE.test(href)) node.removeAttribute('href');
|
||||||
|
if (node.getAttribute('target') === '_blank') {
|
||||||
|
node.setAttribute('rel', 'noopener noreferrer');
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
console.log(purified);
|
window.__dpLinkHook = true;
|
||||||
return purified;
|
}
|
||||||
|
return DOMPurify.sanitize(html, config);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!('theme' in localStorage)) {
|
if (!('theme' in localStorage)) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue