Clean description with DOMPurify

This commit is contained in:
Justin Edmund 2023-07-04 16:56:54 -07:00
parent 277a248ba1
commit ed01ea6955
3 changed files with 35 additions and 13 deletions

View file

@ -2,12 +2,8 @@ import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useSnapshot } from 'valtio' import { useSnapshot } from 'valtio'
import { useTranslation } from 'next-i18next' import { useTranslation } from 'next-i18next'
import classNames from 'classnames'
import clonedeep from 'lodash.clonedeep' import clonedeep from 'lodash.clonedeep'
import DOMPurify from 'dompurify'
import Linkify from 'react-linkify'
import LiteYouTubeEmbed from 'react-lite-youtube-embed'
import reactStringReplace from 'react-string-replace'
import Button from '~components/common/Button' import Button from '~components/common/Button'
import SegmentedControl from '~components/common/SegmentedControl' import SegmentedControl from '~components/common/SegmentedControl'
@ -55,15 +51,14 @@ const PartyFooter = (props: Props) => {
// State: Data // State: Data
const [remixes, setRemixes] = useState<Party[]>([]) const [remixes, setRemixes] = useState<Party[]>([])
const [embeddedDescription, setEmbeddedDescription] = const [sanitizedDescription, setSanitizedDescription] = useState('')
useState<React.ReactNode>()
useEffect(() => { useEffect(() => {
if (partySnapshot.description) { if (partySnapshot.description) {
const purified = DOMPurify.sanitize(partySnapshot.description) const purified = DOMPurify.sanitize(partySnapshot.description)
setSanitizedDescription(purified) setSanitizedDescription(purified)
} else { } else {
setEmbeddedDescription('') setSanitizedDescription('')
} }
}, [partySnapshot.description]) }, [partySnapshot.description])
@ -211,14 +206,17 @@ const PartyFooter = (props: Props) => {
) )
const descriptionSection = ( const descriptionSection = (
<section className={styles.description}> <>
{partySnapshot && {partySnapshot &&
partySnapshot.description && partySnapshot.description &&
partySnapshot.description.length > 0 && ( partySnapshot.description.length > 0 && (
<Linkify>{embeddedDescription}</Linkify> <section
className={styles.description}
dangerouslySetInnerHTML={{ __html: sanitizedDescription }}
/>
)} )}
{(!partySnapshot || !partySnapshot.description) && ( {(!partySnapshot || !partySnapshot.description) && (
<div className={styles.noDescription}> <section className={styles.noDescription}>
<h3>{t('footer.description.empty')}</h3> <h3>{t('footer.description.empty')}</h3>
{props.editable && ( {props.editable && (
<EditPartyModal <EditPartyModal
@ -234,9 +232,9 @@ const PartyFooter = (props: Props) => {
/> />
</EditPartyModal> </EditPartyModal>
)} )}
</div> </section>
)} )}
</section> </>
) )
const remixesSection = ( const remixesSection = (

22
package-lock.json generated
View file

@ -24,6 +24,7 @@
"cmdk": "^0.2.0", "cmdk": "^0.2.0",
"cookies-next": "^2.1.1", "cookies-next": "^2.1.1",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
"dompurify": "^3.0.4",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fix-date": "^1.1.6", "fix-date": "^1.1.6",
"i18next": "^21.6.13", "i18next": "^21.6.13",
@ -65,6 +66,7 @@
"@storybook/nextjs": "latest", "@storybook/nextjs": "latest",
"@storybook/react": "latest", "@storybook/react": "latest",
"@storybook/testing-library": "latest", "@storybook/testing-library": "latest",
"@types/dompurify": "^3.0.2",
"@types/lodash.clonedeep": "^4.5.6", "@types/lodash.clonedeep": "^4.5.6",
"@types/lodash.debounce": "^4.0.6", "@types/lodash.debounce": "^4.0.6",
"@types/node": "17.0.11", "@types/node": "17.0.11",
@ -7041,6 +7043,15 @@
"integrity": "sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==", "integrity": "sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==",
"dev": true "dev": true
}, },
"node_modules/@types/dompurify": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.2.tgz",
"integrity": "sha512-YBL4ziFebbbfQfH5mlC+QTJsvh0oJUrWbmxKMyEdL7emlHJqGR2Qb34TEFKj+VCayBvjKy3xczMFNhugThUsfQ==",
"dev": true,
"dependencies": {
"@types/trusted-types": "*"
}
},
"node_modules/@types/ejs": { "node_modules/@types/ejs": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz", "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz",
@ -7389,6 +7400,12 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/trusted-types": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz",
"integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==",
"dev": true
},
"node_modules/@types/unist": { "node_modules/@types/unist": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz",
@ -10577,6 +10594,11 @@
"url": "https://github.com/fb55/domhandler?sponsor=1" "url": "https://github.com/fb55/domhandler?sponsor=1"
} }
}, },
"node_modules/dompurify": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.4.tgz",
"integrity": "sha512-ae0mA+Qiqp6C29pqZX3fQgK+F91+F7wobM/v8DRzDqJdZJELXiFUx4PP4pK/mzUS0xkiSEx3Ncd9gr69jg3YsQ=="
},
"node_modules/domutils": { "node_modules/domutils": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",

View file

@ -31,6 +31,7 @@
"cmdk": "^0.2.0", "cmdk": "^0.2.0",
"cookies-next": "^2.1.1", "cookies-next": "^2.1.1",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
"dompurify": "^3.0.4",
"fast-deep-equal": "^3.1.3", "fast-deep-equal": "^3.1.3",
"fix-date": "^1.1.6", "fix-date": "^1.1.6",
"i18next": "^21.6.13", "i18next": "^21.6.13",
@ -72,6 +73,7 @@
"@storybook/nextjs": "latest", "@storybook/nextjs": "latest",
"@storybook/react": "latest", "@storybook/react": "latest",
"@storybook/testing-library": "latest", "@storybook/testing-library": "latest",
"@types/dompurify": "^3.0.2",
"@types/lodash.clonedeep": "^4.5.6", "@types/lodash.clonedeep": "^4.5.6",
"@types/lodash.debounce": "^4.0.6", "@types/lodash.debounce": "^4.0.6",
"@types/node": "17.0.11", "@types/node": "17.0.11",