From 051e21cbafc5dac742d062ab0a4f2465ac27dd80 Mon Sep 17 00:00:00 2001 From: paul Date: Sun, 13 Jul 2025 19:11:02 +0200 Subject: [PATCH] fix(security): sanitize CMS content to prevent XSS attacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add DOMPurify to sanitize HTML content in LegalPage component - Prevents stored XSS vulnerabilities in CMS-managed content - Maintains legitimate HTML formatting while removing malicious scripts - Addresses critical XSS vulnerability from security scan 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/package-lock.json | 30 +++++++++++++++++++++++-- frontend/package.json | 2 ++ frontend/src/pages/public/LegalPage.tsx | 3 ++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 89fbca8..99c40f2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,21 +1,23 @@ { - "name": "photo-sharing-frontend", + "name": "picpeak-frontend", "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "photo-sharing-frontend", + "name": "picpeak-frontend", "version": "1.0.0", "dependencies": { "@tanstack/react-query": "^5.0.0", "@tiptap/extension-link": "^2.25.0", "@tiptap/react": "^2.25.0", "@tiptap/starter-kit": "^2.25.0", + "@types/dompurify": "^3.0.5", "@types/react-google-recaptcha": "^2.1.9", "axios": "^1.3.2", "clsx": "^2.0.0", "date-fns": "^2.29.3", + "dompurify": "^3.2.6", "i18next": "^25.3.1", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", @@ -1917,6 +1919,15 @@ "@babel/types": "^7.20.7" } }, + "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==", + "license": "MIT", + "dependencies": { + "@types/trusted-types": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1995,6 +2006,12 @@ "@types/react": "*" } }, + "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==", + "license": "MIT" + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", @@ -2814,6 +2831,15 @@ "dev": true, "license": "MIT" }, + "node_modules/dompurify": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index b78cb9d..46f7ff7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,10 +15,12 @@ "@tiptap/extension-link": "^2.25.0", "@tiptap/react": "^2.25.0", "@tiptap/starter-kit": "^2.25.0", + "@types/dompurify": "^3.0.5", "@types/react-google-recaptcha": "^2.1.9", "axios": "^1.3.2", "clsx": "^2.0.0", "date-fns": "^2.29.3", + "dompurify": "^3.2.6", "i18next": "^25.3.1", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", diff --git a/frontend/src/pages/public/LegalPage.tsx b/frontend/src/pages/public/LegalPage.tsx index 0e4c5bf..600670a 100644 --- a/frontend/src/pages/public/LegalPage.tsx +++ b/frontend/src/pages/public/LegalPage.tsx @@ -3,6 +3,7 @@ import { useParams, Link, useNavigate } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { ArrowLeft, Home } from 'lucide-react'; +import DOMPurify from 'dompurify'; import { Loading, Card } from '../../components/common'; import { cmsService } from '../../services/cms.service'; import { api } from '../../config/api'; @@ -103,7 +104,7 @@ export const LegalPage: React.FC = () => {