Files
readest/apps/readest-app/src/utils/sanitize.ts
T
Huang Xin 24370ca511 feat(reader): render Markdown (.md) files at runtime (#774) (#4816)
Open standalone .md files in the reader without converting to EPUB. A new
makeMarkdownBook (src/utils/md.ts) parses Markdown to sanitized HTML with
marked + DOMPurify, splits the document into sections at H1 boundaries, and
builds an in-memory foliate book (modeled on fb2.js) with a nested heading
TOC. DocumentLoader routes .md/.markdown before the TXT path so a Markdown
file served as text/plain is not converted to EPUB. Layout, font and theme
settings apply the same as for any other format.

Relative-image resolution and Markdown bundle/folder packages are left as
follow-ups (a standalone file has no sibling-asset access on the web).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 05:36:32 +02:00

82 lines
2.2 KiB
TypeScript

import DOMPurify from 'dompurify';
export const sanitizeString = (str?: string) => {
if (!str) return str;
return str.replace(/\u0000/g, '');
};
/**
* Strip untrusted HTML (Send-to-Readest email/web/DOCX conversion, OPDS feed
* descriptions, etc.) down to safe, EPUB-appropriate structural markup. Removes
* scripts, event handlers, styles, iframes, and form controls; keeps headings,
* text, lists, tables, links and images.
*
* Runs against the real DOM, so callers must be on the client (browser or Tauri
* webview), both of which provide `window`/`DOMParser`.
*/
export function sanitizeHtml(html: string): string {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: [
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'p',
'br',
'hr',
'blockquote',
'pre',
'code',
'strong',
'em',
'b',
'i',
'u',
's',
'del',
'ins',
'sup',
'sub',
'span',
'ul',
'ol',
'li',
'dl',
'dt',
'dd',
'table',
'thead',
'tbody',
'tr',
'th',
'td',
'a',
'img',
'figure',
'figcaption',
],
// `id` is allowed so heading anchors survive — the EPUB's nested
// navMap uses `chapter1.xhtml#heading-id` to link the TOC sidebar to
// each section. Without it readers see one entry per chapter only.
// `class` is allowed so Markdown code fences keep their `language-*`
// class for theming (see utils/md.ts).
ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'colspan', 'rowspan', 'id', 'class'],
// Drop anything that would load or run remote code.
FORBID_TAGS: ['script', 'style', 'iframe', 'object', 'embed', 'form', 'input'],
FORBID_ATTR: ['srcset'],
ALLOW_DATA_ATTR: false,
});
}
/**
* Strip scripts and event handlers from an untrusted *document* before it is
* handed to `DOMParser`. Unlike `sanitizeHtml`, this keeps the document
* structure (`<head>`, `<title>`, sectioning elements) so title extraction and
* Readability still work — it only removes anything executable.
*/
export function sanitizeForParsing(html: string): string {
return DOMPurify.sanitize(html, { WHOLE_DOCUMENT: true });
}