Files
windwiderstand/src/lib/markdown-safe.ts
T
Peter Meier e239c85961
Deploy / verify (push) Successful in 1m4s
Deploy / deploy (push) Successful in 1m21s
feat(markdown): span[style] in Markdown erlauben, alles andere HTML strippen
2026-05-28 13:14:58 +02:00

47 lines
1.6 KiB
TypeScript

/**
* Zentral konfigurierter `marked`-Parser. Wichtig: deaktiviert das
* Rendern von **rohem HTML** in Markdown-Quellen — sonst kann ein
* Editor (oder ein kompromittiertes Editor-Konto) `<script>` oder
* `<iframe>`-Tags direkt einschleusen. Marked rendert sie standardmäßig
* wörtlich durch.
*
* `marked` ist Singleton: einmaliger Side-Effect-Import setzt die
* Renderer-Hooks global. Komponenten und Loader importieren `marked`
* von hier statt direkt von "marked".
*
* Komponenten, die `marked.parse(...)` aufrufen, sollten das Resultat
* weiterhin nur über `{@html}` einsetzen — der Sanitizer hier deckt das
* Markdown-→-HTML-Mapping ab. Roh-HTML-Felder (HtmlBlock) werden zusätzlich
* über `$lib/sanitize-blocks.server` gefiltert.
*/
import { marked } from 'marked';
let configured = false;
function configure() {
if (configured) return;
marked.setOptions({ gfm: true });
marked.use({
renderer: {
// Allow only <span style="..."> and </span> — strip all other raw HTML.
html(token: { text: string }) {
let s = token.text;
// Protect <span style="…"> (style value must not contain < or >)
s = s.replace(/<span\s+style="([^"<>]{0,400})"\s*>/gi, '\x00span:$1\x00');
s = s.replace(/<\/span>/gi, '\x00/span\x00');
// Strip all remaining HTML tags
s = s.replace(/<[^>]+>/g, '');
// Restore safe spans
s = s.replace(/\x00span:([^\x00]*)\x00/g, '<span style="$1">');
s = s.replace(/\x00\/span\x00/g, '</span>');
return s;
},
},
});
configured = true;
}
configure();
export { marked };