Skip to content

Commit 478ca41

Browse files
committed
Dynamically add KaTeX style and tweak sanitization
1 parent 763834c commit 478ca41

File tree

5 files changed

+161
-7
lines changed

5 files changed

+161
-7
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
"redux": "^4.0.4",
127127
"redux-devtools-extension": "^2.13.8",
128128
"rehype-katex": "^3.0.0",
129+
"rehype-raw": "^4.0.2",
129130
"rehype-stringify": "^6.0.1",
130131
"remark": "^11.0.2",
131132
"remark-breaks": "^1.0.3",

src/components/common/MarkdownRender.tsx

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ import palette from '../../lib/styles/palette';
1717
import math from 'remark-math';
1818
import remark2rehype from 'remark-rehype';
1919
import katex from 'rehype-katex';
20+
import raw from 'rehype-raw';
21+
import remarkParse from 'remark-parse';
2022
import stringify from 'rehype-stringify';
21-
import 'katex/dist/katex.min.css';
23+
import { Helmet } from 'react-helmet-async';
24+
import katexWhitelist from '../../lib/katexWhitelist';
2225

2326
export interface MarkdownRenderProps {
2427
markdown: string;
@@ -155,12 +158,15 @@ function filter(html: string) {
155158
'span',
156159
'img',
157160
'del',
161+
...katexWhitelist.tags,
158162
],
159163
allowedAttributes: {
160164
a: ['href', 'name', 'target'],
161165
img: ['src'],
162166
iframe: ['src', 'allow', 'allowfullscreen', 'scrolling', 'class'],
163-
'*': ['class', 'id'],
167+
'*': ['class', 'id', 'aria-hidden'],
168+
span: ['style'],
169+
...katexWhitelist.attributes,
164170
},
165171
allowedStyles: {
166172
'*': {
@@ -171,6 +177,9 @@ function filter(html: string) {
171177
],
172178
'text-align': [/^left$/, /^right$/, /^center$/],
173179
},
180+
span: {
181+
...katexWhitelist.styles,
182+
},
174183
},
175184
allowedIframeHostnames: ['www.youtube.com', 'codesandbox.io', 'codepen.io'],
176185
});
@@ -194,11 +203,16 @@ const MarkdownRender: React.FC<MarkdownRenderProps> = ({
194203
ssrEnabled
195204
? filter(
196205
remark()
206+
.use(remarkParse)
207+
.use(remark2rehype, { allowDangerousHTML: true })
208+
.use(raw)
197209
.use(breaks)
198210
.use(prismPlugin)
199-
.use(htmlPlugin)
200211
.use(embedPlugin)
201212
.use(slug)
213+
.use(math)
214+
.use(katex)
215+
.use(stringify)
202216
.processSync(markdown)
203217
.toString(),
204218
)
@@ -216,13 +230,14 @@ const MarkdownRender: React.FC<MarkdownRenderProps> = ({
216230

217231
useEffect(() => {
218232
remark()
233+
.use(remarkParse)
234+
.use(remark2rehype, { allowDangerousHTML: true })
235+
.use(raw)
219236
.use(breaks)
220237
.use(prismPlugin)
221-
.use(htmlPlugin)
222238
.use(embedPlugin)
223239
.use(slug)
224240
.use(math)
225-
.use(remark2rehype)
226241
.use(katex)
227242
.use(stringify)
228243
.process(markdown, (err: any, file: any) => {
@@ -260,6 +275,16 @@ const MarkdownRender: React.FC<MarkdownRenderProps> = ({
260275

261276
return (
262277
<Typography>
278+
<Helmet>
279+
{/\$(.*)\$/.test(markdown) && (
280+
<link
281+
rel="stylesheet"
282+
href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css"
283+
integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq"
284+
crossOrigin="anonymous"
285+
/>
286+
)}
287+
</Helmet>
263288
{editing ? (
264289
<MarkdownRenderErrorBoundary
265290
onError={() => setHasTagError(true)}

src/lib/katexWhitelist.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
const tags = [
2+
'math',
3+
'annotation',
4+
'semantics',
5+
'mtext',
6+
'mn',
7+
'mo',
8+
'mi',
9+
'mspace',
10+
'mover',
11+
'munder',
12+
'munderover',
13+
'msup',
14+
'msub',
15+
'msubsup',
16+
'mfrac',
17+
'mroot',
18+
'msqrt',
19+
'mtable',
20+
'mtr',
21+
'mtd',
22+
'mlabeledtr',
23+
'mrow',
24+
'menclose',
25+
'mstyle',
26+
'mpadded',
27+
'mphantom',
28+
'mglyph',
29+
];
30+
31+
const attributes = tags.reduce((acc, current) => {
32+
acc[current] = ['*'];
33+
return acc;
34+
}, {} as Record<string, string[]>);
35+
36+
const styles = [
37+
'background-color',
38+
'border-bottom-width',
39+
'border-color',
40+
'border-right-style',
41+
'border-right-width',
42+
'border-top-width',
43+
'border-style',
44+
'border-width',
45+
'bottom',
46+
'color',
47+
'height',
48+
'left',
49+
'margin',
50+
'margin-left',
51+
'margin-right',
52+
'margin-top',
53+
'min-width',
54+
'padding-left',
55+
'position',
56+
'top',
57+
'width',
58+
'vertical-align',
59+
].reduce((acc, current) => {
60+
acc[current] = [/.*/];
61+
return acc;
62+
}, {} as Record<string, RegExp[]>);
63+
64+
const katexWhitelist = {
65+
tags,
66+
attributes,
67+
styles,
68+
};
69+
70+
export default katexWhitelist;

src/types/missingTypes.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ declare module 'remark-slug';
77
declare module 'remark-math';
88
declare module 'remark-rehype';
99
declare module 'rehype-katex';
10+
declare module 'remark-parse';
1011
declare module 'rehype-stringify';
12+
declare module 'rehype-raw';
1113
declare module 'unist-util-visit';
1214
declare module 'strip-markdown';

yarn.lock

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6234,6 +6234,18 @@ hash.js@^1.0.0, hash.js@^1.0.3:
62346234
inherits "^2.0.3"
62356235
minimalistic-assert "^1.0.1"
62366236

6237+
hast-to-hyperscript@^7.0.0:
6238+
version "7.0.4"
6239+
resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-7.0.4.tgz#7c4c037d9a8ea19b0a3fdb676a26448ad922353d"
6240+
integrity sha512-vmwriQ2H0RPS9ho4Kkbf3n3lY436QKLq6VaGA1pzBh36hBi3tm1DO9bR+kaJIbpT10UqaANDkMjxvjVfr+cnOA==
6241+
dependencies:
6242+
comma-separated-tokens "^1.0.0"
6243+
property-information "^5.3.0"
6244+
space-separated-tokens "^1.0.0"
6245+
style-to-object "^0.2.1"
6246+
unist-util-is "^3.0.0"
6247+
web-namespaces "^1.1.2"
6248+
62376249
hast-util-from-parse5@^5.0.0:
62386250
version "5.0.3"
62396251
resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-5.0.3.tgz#3089dc0ee2ccf6ec8bc416919b51a54a589e097c"
@@ -6255,6 +6267,20 @@ hast-util-parse-selector@^2.0.0:
62556267
resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.4.tgz#60c99d0b519e12ab4ed32e58f150ec3f61ed1974"
62566268
integrity sha512-gW3sxfynIvZApL4L07wryYF4+C9VvH3AUi7LAnVXV4MneGEgwOByXvFo18BgmTWnm7oHAe874jKbIB1YhHSIzA==
62576269

6270+
hast-util-raw@^5.0.0:
6271+
version "5.0.2"
6272+
resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-5.0.2.tgz#62288f311ec2f35e066a30d5e0277f963ad43a67"
6273+
integrity sha512-3ReYQcIHmzSgMq8UrDZHFL0oGlbuVGdLKs8s/Fe8BfHFAyZDrdv1fy/AGn+Fim8ZuvAHcJ61NQhVMtyfHviT/g==
6274+
dependencies:
6275+
hast-util-from-parse5 "^5.0.0"
6276+
hast-util-to-parse5 "^5.0.0"
6277+
html-void-elements "^1.0.0"
6278+
parse5 "^5.0.0"
6279+
unist-util-position "^3.0.0"
6280+
web-namespaces "^1.0.0"
6281+
xtend "^4.0.0"
6282+
zwitch "^1.0.0"
6283+
62586284
hast-util-sanitize@^2.0.0:
62596285
version "2.0.1"
62606286
resolved "https://registry.yarnpkg.com/hast-util-sanitize/-/hast-util-sanitize-2.0.1.tgz#de2f32c5aa9e93b61903e4381be6c49887d3829e"
@@ -6278,6 +6304,17 @@ hast-util-to-html@^6.0.0:
62786304
unist-util-is "^3.0.0"
62796305
xtend "^4.0.1"
62806306

6307+
hast-util-to-parse5@^5.0.0:
6308+
version "5.1.2"
6309+
resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-5.1.2.tgz#09d27bee9ba9348ea05a6cfcc44e02f9083969b6"
6310+
integrity sha512-ZgYLJu9lYknMfsBY0rBV4TJn2xiwF1fXFFjbP6EE7S0s5mS8LIKBVWzhA1MeIs1SWW6GnnE4In6c3kPb+CWhog==
6311+
dependencies:
6312+
hast-to-hyperscript "^7.0.0"
6313+
property-information "^5.0.0"
6314+
web-namespaces "^1.0.0"
6315+
xtend "^4.0.0"
6316+
zwitch "^1.0.0"
6317+
62816318
hast-util-to-text@^2.0.0:
62826319
version "2.0.0"
62836320
resolved "https://registry.yarnpkg.com/hast-util-to-text/-/hast-util-to-text-2.0.0.tgz#c59afa8798145c10d40c2f34f92900f4dfc8ac69"
@@ -10434,7 +10471,7 @@ prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2,
1043410471
object-assign "^4.1.1"
1043510472
react-is "^16.8.1"
1043610473

10437-
property-information@^5.0.0:
10474+
property-information@^5.0.0, property-information@^5.3.0:
1043810475
version "5.4.0"
1043910476
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.4.0.tgz#16e08f13f4e5c4a7be2e4ec431c01c4f8dba869a"
1044010477
integrity sha512-nmMWAm/3vKFGmmOWOcdLjgq/Hlxa+hsuR/px1Lp/UGEyc5A22A6l78Shc2C0E71sPmAqglni+HrS7L7VJ7AUCA==
@@ -11113,6 +11150,13 @@ rehype-parse@^6.0.0:
1111311150
parse5 "^5.0.0"
1111411151
xtend "^4.0.0"
1111511152

11153+
rehype-raw@^4.0.2:
11154+
version "4.0.2"
11155+
resolved "https://registry.yarnpkg.com/rehype-raw/-/rehype-raw-4.0.2.tgz#5d3191689df96c8c651ce5f51a6c668d2c07b9c8"
11156+
integrity sha512-xQt94oXfDaO7sK9mJBtsZXkjW/jm6kArCoYN+HqKZ51O19AFHlp3Xa5UfZZ2tJkbpAZzKtgVUYvnconk9IsFuA==
11157+
dependencies:
11158+
hast-util-raw "^5.0.0"
11159+
1111611160
rehype-stringify@^6.0.1:
1111711161
version "6.0.1"
1111811162
resolved "https://registry.yarnpkg.com/rehype-stringify/-/rehype-stringify-6.0.1.tgz#b6aa9f84d5276c5d247c62fc1ea15983a35a4575"
@@ -12399,6 +12443,13 @@ [email protected]:
1239912443
dependencies:
1240012444
inline-style-parser "0.1.1"
1240112445

12446+
style-to-object@^0.2.1:
12447+
version "0.2.3"
12448+
resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.2.3.tgz#afcf42bc03846b1e311880c55632a26ad2780bcb"
12449+
integrity sha512-1d/k4EY2N7jVLOqf2j04dTc37TPOv/hHxZmvpg8Pdh8UYydxeu/C1W1U4vD8alzf5V2Gt7rLsmkr4dxAlDm9ng==
12450+
dependencies:
12451+
inline-style-parser "0.1.1"
12452+
1240212453
styled-components@^4.4.1:
1240312454
version "4.4.1"
1240412455
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-4.4.1.tgz#e0631e889f01db67df4de576fedaca463f05c2f2"
@@ -13390,7 +13441,7 @@ wbuf@^1.1.0, wbuf@^1.7.3:
1339013441
dependencies:
1339113442
minimalistic-assert "^1.0.0"
1339213443

13393-
web-namespaces@^1.1.2:
13444+
web-namespaces@^1.0.0, web-namespaces@^1.1.2:
1339413445
version "1.1.4"
1339513446
resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"
1339613447
integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==
@@ -13941,3 +13992,8 @@ zip-stream@^1.2.0:
1394113992
compress-commons "^1.2.0"
1394213993
lodash "^4.8.0"
1394313994
readable-stream "^2.0.0"
13995+
13996+
zwitch@^1.0.0:
13997+
version "1.0.5"
13998+
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"
13999+
integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==

0 commit comments

Comments
 (0)