diff --git a/package-lock.json b/package-lock.json index 2ebf246ce..5d485a9d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@types/md5": "^2.3.2", "@walletconnect/client": "^1.8.0", "buffer": "^6.0.3", - "dompurify": "^2.4.0", + "dompurify": "^2.4.1", "ethers": "^5.7.2", "events": "^3.3.0", "katex": "^0.16.3", @@ -34,7 +34,7 @@ "@rsksmart/mock-web3-provider": "^1.0.1", "@sveltejs/vite-plugin-svelte": "^1.1.0", "@tsconfig/svelte": "^3.0.0", - "@types/dompurify": "^2.3.4", + "@types/dompurify": "^2.4.0", "@types/katex": "^0.14.0", "@types/lodash": "^4.14.188", "@typescript-eslint/eslint-plugin": "^5.42.0", @@ -1091,9 +1091,9 @@ } }, "node_modules/@types/dompurify": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.3.4.tgz", - "integrity": "sha512-EXzDatIb5EspL2eb/xPGmaC8pePcTHrkDCONjeisusLFrVfl38Pjea/R0YJGu3k9ZQadSvMqW0WXPI2hEo2Ajg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz", + "integrity": "sha512-IDBwO5IZhrKvHFUl+clZxgf3hn2b/lU6H1KaBShPkQyGJUQ0xwebezIPSuiyGwfz1UzJWQl4M7BDxtHtCCPlTg==", "dev": true, "dependencies": { "@types/trusted-types": "*" @@ -2794,9 +2794,9 @@ } }, "node_modules/dompurify": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz", - "integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz", + "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==" }, "node_modules/ecc-jsbn": { "version": "0.1.2", @@ -7937,9 +7937,9 @@ } }, "@types/dompurify": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.3.4.tgz", - "integrity": "sha512-EXzDatIb5EspL2eb/xPGmaC8pePcTHrkDCONjeisusLFrVfl38Pjea/R0YJGu3k9ZQadSvMqW0WXPI2hEo2Ajg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz", + "integrity": "sha512-IDBwO5IZhrKvHFUl+clZxgf3hn2b/lU6H1KaBShPkQyGJUQ0xwebezIPSuiyGwfz1UzJWQl4M7BDxtHtCCPlTg==", "dev": true, "requires": { "@types/trusted-types": "*" @@ -9230,9 +9230,9 @@ } }, "dompurify": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz", - "integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz", + "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==" }, "ecc-jsbn": { "version": "0.1.2", diff --git a/package.json b/package.json index 32d0078d2..34d99f1c2 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@rsksmart/mock-web3-provider": "^1.0.1", "@sveltejs/vite-plugin-svelte": "^1.1.0", "@tsconfig/svelte": "^3.0.0", - "@types/dompurify": "^2.3.4", + "@types/dompurify": "^2.4.0", "@types/katex": "^0.14.0", "@types/lodash": "^4.14.188", "@typescript-eslint/eslint-plugin": "^5.42.0", @@ -45,7 +45,7 @@ "@types/md5": "^2.3.2", "@walletconnect/client": "^1.8.0", "buffer": "^6.0.3", - "dompurify": "^2.4.0", + "dompurify": "^2.4.1", "ethers": "^5.7.2", "events": "^3.3.0", "katex": "^0.16.3", diff --git a/src/Markdown.svelte b/src/Markdown.svelte index 895e94cca..e492040d0 100644 --- a/src/Markdown.svelte +++ b/src/Markdown.svelte @@ -24,7 +24,8 @@ let container: HTMLElement; const render = (content: string): string => - dompurify.sanitize(marked.parse(content, { headerIds: false })); + // eslint-disable-next-line @typescript-eslint/naming-convention + dompurify.sanitize(marked.parse(content), { SANITIZE_DOM: false }); onMount(() => { // Don't underline tags that contain images. diff --git a/src/utils.ts b/src/utils.ts index d556e19cd..362fbd49b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -521,6 +521,27 @@ const katexMarkedExtension = { }), }; +// Converts self closing anchor tags into empty anchor tags, to avoid erratic wrapping behaviour +// e.g. -> +const anchorMarkedExtension = { + name: "sanitizedAnchor", + level: "block", + start: (src: string) => src.match(//)?.index, + tokenizer(src: string) { + const match = src.match(/^/); + if (match) { + return { + type: "sanitizedAnchor", + raw: match[0], + text: match[1].trim(), + }; + } + }, + renderer: (token: marked.Tokens.Generic) => { + return ``; + }, +}; + // Overwrites the rendering of heading tokens. // Since there are possible non ASCII characters in headings, // we escape them by replacing them with dashes and, @@ -536,8 +557,6 @@ export const renderer = { }, }; -export const markdownExtensions = [emojisMarkedExtension, katexMarkedExtension]; - export function twemoji(node: HTMLElement) { twemojiModule.parse(node, { base: process.env.hashRouting ? "./" : "/", @@ -546,3 +565,9 @@ export function twemoji(node: HTMLElement) { className: `txt-emoji`, }); } + +export const markdownExtensions = [ + emojisMarkedExtension, + katexMarkedExtension, + anchorMarkedExtension, +];