feat: purify injected html in post & comments

This commit is contained in:
MTG2000
2022-07-23 12:25:56 +03:00
parent fb7c4a11ec
commit ff9c13481f
7 changed files with 50 additions and 9 deletions

View File

@@ -52,7 +52,6 @@ async function getPaymetRequestForItem(lightning_address, amount_in_sat) {
return axios
.get(lnurlCallbackUrl, { params: { amount } })
.then((prResponse) => {
console.log(prResponse.data);
return prResponse.data.pr;
});
}

30
package-lock.json generated
View File

@@ -41,6 +41,7 @@
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dayjs": "^1.11.1",
"dompurify": "^2.3.10",
"env-cmd": "^10.1.0",
"express": "^4.18.1",
"express-session": "^1.17.3",
@@ -111,6 +112,7 @@
"@storybook/testing-library": "^0.0.10",
"@tailwindcss/forms": "^0.5.0",
"@types/chance": "^1.1.3",
"@types/dompurify": "^2.3.3",
"@types/fslightbox-react": "^1.4.2",
"@types/lodash.debounce": "^4.0.7",
"@types/lodash.throttle": "^4.1.7",
@@ -15177,6 +15179,15 @@
"resolved": "https://registry.npmjs.org/@types/direction/-/direction-1.0.0.tgz",
"integrity": "sha512-et1wmqXm/5smJ8lTJfBnwD12/2Y7eVJLKbuaRT0h2xaKAoo1h8Dz2Io22GObDLFwxY1ddXRTLH3Gq5v44Fl/2w=="
},
"node_modules/@types/dompurify": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.3.3.tgz",
"integrity": "sha512-nnVQSgRVuZ/843oAfhA25eRSNzUFcBPk/LOiw5gm8mD9/X7CNcbRkQu/OsjCewO8+VIYfPxUnXvPEVGenw14+w==",
"dev": true,
"dependencies": {
"@types/trusted-types": "*"
}
},
"node_modules/@types/eslint": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz",
@@ -21637,6 +21648,11 @@
"resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz",
"integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ=="
},
"node_modules/dompurify": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.10.tgz",
"integrity": "sha512-o7Fg/AgC7p/XpKjf/+RC3Ok6k4St5F7Q6q6+Nnm3p2zGWioAY6dh0CbbuwOhH2UcSzKsdniE/YnE2/92JcsA+g=="
},
"node_modules/domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
@@ -79432,6 +79448,15 @@
"resolved": "https://registry.npmjs.org/@types/direction/-/direction-1.0.0.tgz",
"integrity": "sha512-et1wmqXm/5smJ8lTJfBnwD12/2Y7eVJLKbuaRT0h2xaKAoo1h8Dz2Io22GObDLFwxY1ddXRTLH3Gq5v44Fl/2w=="
},
"@types/dompurify": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.3.3.tgz",
"integrity": "sha512-nnVQSgRVuZ/843oAfhA25eRSNzUFcBPk/LOiw5gm8mD9/X7CNcbRkQu/OsjCewO8+VIYfPxUnXvPEVGenw14+w==",
"dev": true,
"requires": {
"@types/trusted-types": "*"
}
},
"@types/eslint": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz",
@@ -84640,6 +84665,11 @@
"resolved": "https://registry.npmjs.org/domino/-/domino-2.1.6.tgz",
"integrity": "sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ=="
},
"dompurify": {
"version": "2.3.10",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.10.tgz",
"integrity": "sha512-o7Fg/AgC7p/XpKjf/+RC3Ok6k4St5F7Q6q6+Nnm3p2zGWioAY6dh0CbbuwOhH2UcSzKsdniE/YnE2/92JcsA+g=="
},
"domutils": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",

View File

@@ -36,6 +36,7 @@
"cors": "^2.8.5",
"crypto": "^1.0.1",
"dayjs": "^1.11.1",
"dompurify": "^2.3.10",
"env-cmd": "^10.1.0",
"express": "^4.18.1",
"express-session": "^1.17.3",
@@ -162,6 +163,7 @@
"@storybook/testing-library": "^0.0.10",
"@tailwindcss/forms": "^0.5.0",
"@types/chance": "^1.1.3",
"@types/dompurify": "^2.3.3",
"@types/fslightbox-react": "^1.4.2",
"@types/lodash.debounce": "^4.0.7",
"@types/lodash.throttle": "^4.1.7",

View File

@@ -33,6 +33,7 @@ type Props = {
direction?: 'horizontal' | 'vertical'
disableCounter?: boolean
disableShake?: boolean
hideVotesCoun?: boolean
dense?: boolean
size?: 'sm' | 'md'
resetCounterOnRelease?: boolean
@@ -59,6 +60,7 @@ export default function VoteButton({
direction = 'horizontal',
disableCounter = false,
disableShake = false,
hideVotesCoun = false,
dense = false,
resetCounterOnRelease: resetCounterOnReleaseProp = false,
...props }: Props) {
@@ -221,7 +223,7 @@ export default function VoteButton({
${direction === 'vertical' ?
dense ? "py-4 px-12" : "py-8 px-20"
:
dense ? "py-4 px-8" : "p-8"}
dense ? "py-4 px-8" : "p-8 min-w-[80px]"}
${voteCntRef.current > 0 && "outline"} active:outline outline-1 outline-red-500
${btnShakeClass}
`}
@@ -246,7 +248,7 @@ export default function VoteButton({
<MdLocalFireDepartment
className={`text-body2 ${incrementsCount ? "text-red-600" : "text-gray-400"}`}
/><span className="align-middle w-[4ch]"> {numberFormatter(votes + voteCnt)}</span>
/>{!hideVotesCoun && <span className="align-middle w-[4ch]"> {numberFormatter(votes + voteCnt)}</span>}
</div>
<AnimatePresence>
{btnState === 'loading' &&

View File

@@ -1,8 +1,9 @@
import { marked } from "marked";
import { BiComment } from "react-icons/bi";
import VotesCount from "src/Components/VotesCount/VotesCount";
import VoteButton from "src/Components/VoteButton/VoteButton";
import Header from "src/features/Posts/Components/PostCard/Header/Header";
import { Comment } from "../types";
import DOMPurify from 'dompurify';
interface Props {
@@ -15,11 +16,14 @@ export default function CommentCard({ comment, canReply, onReply }: Props) {
return (
<div className="border rounded-12 p-24">
<Header author={comment.author} date={new Date(comment.created_at).toISOString()} />
<p className="text-body4 mt-16 whitespace-pre-line" dangerouslySetInnerHTML={{ __html: marked.parse(comment.body) }}>
<div
className="text-body4 mt-16 whitespace-pre-line"
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(marked.parse(comment.body)) }}
>
</p>
</div>
<div className="flex gap-24 mt-16 items-center">
<VotesCount count={0} />
<VoteButton votes={0} hideVotesCoun onVote={(value, config) => console.log('Voting amount ' + value)} />
{canReply && <button
className="text-gray-600 font-medium hover:bg-gray-100 py-8 px-12 rounded-8"
onClick={onReply}

View File

@@ -8,6 +8,7 @@ import { Menu, MenuItem } from "@szhsin/react-menu";
import { useAppSelector } from "src/utils/hooks";
import { useUpdateStory } from './useUpdateStory'
import { FaPen } from "react-icons/fa";
import DOMPurify from 'dompurify';
interface Props {
@@ -66,7 +67,10 @@ export default function StoryPageContent({ story }: Props) {
</div> */}
</div>
<div className={`mt-42 ${styles.body}`} dangerouslySetInnerHTML={{ __html: marked.parse(story.body) }}>
<div
className={`mt-42 ${styles.body}`}
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(marked.parse(story.body)) }}
>
</div>
</div>
{/* <div id="comments" className="mt-10 comments_col">

View File

@@ -36,7 +36,7 @@ export function createRoute(options: RouteOptions) {
if (options.type === 'post')
return `/blog/post/${options.postType}/${options.id}`
return `/blog/post/${options.postType.toLowerCase()}/${options.id}`
+ (options.title ? `/${toSlug(options.title)}` : "");
if (options.type === 'story')