Fix XSS vulnerability by sanitizing HTML in blog posts
Added DOMPurify library to sanitize HTML content before rendering with dangerouslySetInnerHTML. This prevents Cross-Site Scripting (XSS) attacks where malicious scripts could be injected through blog post content. Changes: - Installed dompurify and @types/dompurify - Added HTML sanitization in BlogPost.tsx (client/pages/BlogPost.tsx:139) - Added HTML sanitization in AdminBlogEditor.tsx preview (client/components/admin/AdminBlogEditor.tsx:273) Security impact: HIGH - Previously, unsanitized HTML from the API could execute arbitrary JavaScript, potentially stealing user credentials or performing unauthorized actions.
This commit is contained in:
parent
7e1e41bb02
commit
77a2fa68c6
4 changed files with 34 additions and 2 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import { useState } from "react";
|
||||
import DOMPurify from "dompurify";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
|
|
@ -269,7 +270,7 @@ const BlogEditor = ({ onPublish, initialData }: BlogEditorProps) => {
|
|||
{excerpt && (
|
||||
<p className="text-muted-foreground italic">{excerpt}</p>
|
||||
)}
|
||||
<div dangerouslySetInnerHTML={{ __html: html }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useParams, Link } from "react-router-dom";
|
||||
import DOMPurify from "dompurify";
|
||||
import Layout from "@/components/Layout";
|
||||
import SEO from "@/components/SEO";
|
||||
import {
|
||||
|
|
@ -135,7 +136,7 @@ export default function BlogPost() {
|
|||
</CardHeader>
|
||||
<CardContent className="prose max-w-none mt-6">
|
||||
{post.body ? (
|
||||
<div dangerouslySetInnerHTML={{ __html: post.body }} />
|
||||
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(post.body) }} />
|
||||
) : (
|
||||
<p>{post.excerpt}</p>
|
||||
)}
|
||||
|
|
|
|||
28
package-lock.json
generated
28
package-lock.json
generated
|
|
@ -15,6 +15,7 @@
|
|||
"@vercel/analytics": "^1.5.0",
|
||||
"adm-zip": "^0.5.16",
|
||||
"chokidar": "^3.6.0",
|
||||
"dompurify": "^3.3.1",
|
||||
"dotenv": "^17.2.0",
|
||||
"ethers": "^6.13.0",
|
||||
"express": "^4.18.2",
|
||||
|
|
@ -59,6 +60,7 @@
|
|||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@tanstack/react-query": "^5.56.2",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^22.5.5",
|
||||
"@types/nodemailer": "^7.0.3",
|
||||
|
|
@ -6881,6 +6883,16 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/dompurify": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz",
|
||||
"integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/trusted-types": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/draco3d": {
|
||||
"version": "1.4.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/draco3d/-/draco3d-1.4.10.tgz",
|
||||
|
|
@ -7167,6 +7179,13 @@
|
|||
"meshoptimizer": "~0.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
|
||||
|
|
@ -9853,6 +9872,15 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz",
|
||||
"integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "17.2.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz",
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
"@vercel/analytics": "^1.5.0",
|
||||
"adm-zip": "^0.5.16",
|
||||
"chokidar": "^3.6.0",
|
||||
"dompurify": "^3.3.1",
|
||||
"dotenv": "^17.2.0",
|
||||
"ethers": "^6.13.0",
|
||||
"express": "^4.18.2",
|
||||
|
|
@ -83,6 +84,7 @@
|
|||
"@tailwindcss/typography": "^0.5.15",
|
||||
"@tanstack/react-query": "^5.56.2",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/dompurify": "^3.0.5",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^22.5.5",
|
||||
"@types/nodemailer": "^7.0.3",
|
||||
|
|
|
|||
Loading…
Reference in a new issue