Initial commit
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
// Renders formatted text from the content module through an allow-list: paragraphs, line breaks,
|
||||
// bold, italic, lists and safe links only. Everything else is dropped, so pasted content can
|
||||
// never inject scripts or styles into the site.
|
||||
|
||||
const ALLOWED = new Set(['p', 'br', 'strong', 'b', 'em', 'i', 'ul', 'ol', 'li', 'a'])
|
||||
const SAFE_LINK = /^(\/|#|https:\/\/|mailto:|tel:)/i
|
||||
|
||||
export function sanitize(html: string): string {
|
||||
return (html || '')
|
||||
.replace(/<(script|style|iframe|object|embed|svg|math)[\s\S]*?<\/\1>/gi, '')
|
||||
.replace(/<!--[\s\S]*?-->/g, '')
|
||||
.replace(/<\s*(\/?)\s*([a-z0-9]+)([^>]*)>/gi, (_m, close: string, tag: string, attrs: string) => {
|
||||
const t = tag.toLowerCase()
|
||||
if (!ALLOWED.has(t)) return ''
|
||||
if (close) return t === 'br' ? '' : `</${t}>`
|
||||
if (t === 'a') {
|
||||
const href = (attrs.match(/href\s*=\s*"([^"]*)"/i) || attrs.match(/href\s*=\s*'([^']*)'/i) || [])[1] || ''
|
||||
if (!SAFE_LINK.test(href.trim())) return '<a>'
|
||||
const external = /^https:\/\//i.test(href)
|
||||
const safe = href.replace(/"/g, '"')
|
||||
return external ? `<a href="${safe}" target="_blank" rel="noopener noreferrer">` : `<a href="${safe}">`
|
||||
}
|
||||
return `<${t}>`
|
||||
})
|
||||
}
|
||||
|
||||
export function RichText({ html, className }: { html: string; className?: string }) {
|
||||
if (!html) return null
|
||||
return <div className={className} dangerouslySetInnerHTML={{ __html: sanitize(html) }} />
|
||||
}
|
||||
|
||||
// Plain text with \n turned into line breaks (for headings that allow two lines).
|
||||
export function Lines({ text }: { text: string }) {
|
||||
const parts = (text || '').split(/\\n|\n/)
|
||||
return (
|
||||
<>
|
||||
{parts.map((part, i) => (
|
||||
<span key={i}>
|
||||
{part}
|
||||
{i < parts.length - 1 && <br />}
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user