A calm place to write long-form, and publish it to the open social web.
skypress.blog/
1import sanitizeHtml from 'sanitize-html';
2
3/**
4 * Sanitise rendered article HTML before injecting it into a reading page.
5 *
6 * Article content comes from arbitrary PDSes and is UNTRUSTED — a malicious record
7 * could embed `<script>`/`onerror`/etc. We allow only the tags + attributes the curated
8 * blocks (Decision 0002) and inline rich text produce; everything else is stripped.
9 * External links get `rel="noopener noreferrer nofollow ugc"` and open in a new tab.
10 */
11const OPTIONS: sanitizeHtml.IOptions = {
12 allowedTags: [
13 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
14 'ul', 'ol', 'li',
15 'blockquote', 'cite',
16 'pre', 'code', 'kbd',
17 'hr', 'figure', 'figcaption', 'img',
18 'strong', 'em', 'b', 'i', 's', 'del', 'ins', 'sub', 'sup', 'mark',
19 'a', 'br', 'span',
20 ],
21 allowedAttributes: {
22 a: [ 'href', 'title', 'rel', 'target' ],
23 img: [ 'src', 'alt' ],
24 '*': [ 'class' ],
25 },
26 allowedSchemes: [ 'http', 'https', 'mailto' ],
27 allowedSchemesByTag: { img: [ 'http', 'https' ] },
28 transformTags: {
29 a: sanitizeHtml.simpleTransform( 'a', {
30 rel: 'noopener noreferrer nofollow ugc',
31 target: '_blank',
32 } ),
33 },
34};
35
36export function sanitizeArticleHtml( html: string ): string {
37 return sanitizeHtml( html, OPTIONS );
38}