Files
admin/apps/backend/app/base_modules/WebManager/services/content_sanitizer.ts
T

104 lines
2.3 KiB
TypeScript

import sanitizeHtml from 'sanitize-html'
const ALLOWED_REL_VALUES = new Set(['nofollow', 'noopener', 'noreferrer'])
const ALLOWED_URL_PROTOCOLS = new Set(['http:', 'https:', 'mailto:', 'tel:'])
const URL_KEYS = new Set(['href', 'url', 'externalUrl'])
const RICH_TEXT_OPTIONS: sanitizeHtml.IOptions = {
allowedTags: [
'a',
'b',
'blockquote',
'br',
'code',
'em',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'hr',
'i',
'li',
'ol',
'p',
'pre',
's',
'span',
'strong',
'u',
'ul',
],
allowedAttributes: {
'*': ['class', 'title'],
'a': ['href', 'target', 'rel'],
},
allowedSchemes: ['http', 'https', 'mailto', 'tel'],
allowProtocolRelative: true,
disallowedTagsMode: 'discard',
nonTextTags: [
'script',
'style',
'textarea',
'option',
'iframe',
'object',
'embed',
'form',
'svg',
'math',
],
transformTags: {
a: (tagName, attribs) => {
if (attribs.target !== undefined && !['_blank', '_self'].includes(attribs.target)) {
delete attribs.target
}
if (attribs.rel !== undefined) {
const rel = attribs.rel
.split(/\s+/)
.filter((value) => ALLOWED_REL_VALUES.has(value))
.join(' ')
if (rel) attribs.rel = rel
else delete attribs.rel
}
if (attribs.target === '_blank') attribs.rel = 'noopener noreferrer'
return { tagName, attribs }
},
},
}
export function sanitizeRichText(value: string) {
return sanitizeHtml(value, RICH_TEXT_OPTIONS)
}
export function sanitizeUrl(value: string) {
try {
const parsed = new URL(value, 'https://relative.invalid')
return ALLOWED_URL_PROTOCOLS.has(parsed.protocol) ? value : ''
} catch {
return ''
}
}
export function sanitizePayload(value: unknown, key = ''): unknown {
if (typeof value === 'string') {
return URL_KEYS.has(key) ? sanitizeUrl(value) : sanitizeRichText(value)
}
if (Array.isArray(value)) return value.map((item) => sanitizePayload(item))
if (value && typeof value === 'object') {
return Object.fromEntries(
Object.entries(value).map(([childKey, childValue]) => [
childKey,
sanitizePayload(childValue, childKey),
])
)
}
return value
}