mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2026-10-12 02:57:12 +00:00
58 lines
1.5 KiB
TypeScript
58 lines
1.5 KiB
TypeScript
import { Pipe, PipeTransform } from '@angular/core'
|
|
import { Marked, Renderer, Tokens } from 'marked'
|
|
|
|
const ALLOWED_LINK_PROTOCOLS = ['http:', 'https:', 'mailto:']
|
|
|
|
function escapeHtml(text: string): string {
|
|
return text
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''')
|
|
}
|
|
|
|
function safeHref(href: string): string | null {
|
|
try {
|
|
const url = new URL(href)
|
|
return ALLOWED_LINK_PROTOCOLS.includes(url.protocol) ? url.href : null
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
// Treat chat content as untrusted: no raw HTML, no images, and only
|
|
// absolute links. Angular sanitizer runs on top of this as well.
|
|
const renderer: Partial<Renderer> = {
|
|
html({ text }: Tokens.HTML | Tokens.Tag): string {
|
|
return escapeHtml(text)
|
|
},
|
|
image({ text }: Tokens.Image): string {
|
|
return escapeHtml(text)
|
|
},
|
|
link(this: Renderer, { href, title, tokens }: Tokens.Link): string {
|
|
const text = this.parser.parseInline(tokens)
|
|
const url = safeHref(href)
|
|
if (!url) return text
|
|
const titleAttr = title ? ` title="${escapeHtml(title)}"` : ''
|
|
return `<a href="${escapeHtml(url)}"${titleAttr} target="_blank" rel="noopener noreferrer nofollow">${text}</a>`
|
|
},
|
|
}
|
|
|
|
const markdown = new Marked({
|
|
async: false,
|
|
gfm: true,
|
|
breaks: true,
|
|
renderer,
|
|
})
|
|
|
|
@Pipe({
|
|
name: 'markdown',
|
|
})
|
|
export class MarkdownPipe implements PipeTransform {
|
|
transform(value: string | null | undefined): string {
|
|
if (!value) return ''
|
|
return markdown.parse(value) as string
|
|
}
|
|
}
|