Skip to content

Commit 9da1a06

Browse files
committed
feat(security): implement Content Security Policy using DOMPurify for chat content sanitization and update next.config.mjs for enhanced security
1 parent 4b48108 commit 9da1a06

6 files changed

Lines changed: 65 additions & 4 deletions

File tree

SortVision/next.config.mjs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,37 @@
11
/** @type {import('next').NextConfig} */
22
const isProd = process.env.NODE_ENV === 'production';
33

4+
const cspScriptSources = [
5+
"'self'",
6+
"'unsafe-inline'",
7+
...(isProd ? [] : ["'unsafe-eval'"]),
8+
'https://vercel.live',
9+
'https://vitals.vercel-insights.com',
10+
'https://va.vercel-scripts.com',
11+
'https://www.googletagmanager.com',
12+
'https://www.google-analytics.com',
13+
'https://fonts.googleapis.com',
14+
'https://www.sortvision.com',
15+
];
16+
17+
const contentSecurityPolicy = [
18+
"default-src 'self'",
19+
`script-src ${cspScriptSources.join(' ')}`,
20+
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://www.sortvision.com",
21+
"style-src-elem 'self' 'unsafe-inline' https://fonts.googleapis.com https://www.sortvision.com",
22+
`script-src-elem ${cspScriptSources.join(' ')}`,
23+
"font-src 'self' https://fonts.gstatic.com data:",
24+
"img-src 'self' data: https: blob:",
25+
"media-src 'self' data: blob:",
26+
"connect-src 'self' https: data: blob:",
27+
"frame-src 'none'",
28+
"object-src 'none'",
29+
"base-uri 'self'",
30+
"form-action 'self'",
31+
"frame-ancestors 'none'",
32+
"worker-src 'self' blob:",
33+
].join('; ');
34+
435
const nextConfig = {
536
// Next.js 16 - Use App Router (default)
637
// output: 'export', // Removed for SSR/SSG support
@@ -81,7 +112,7 @@ const nextConfig = {
81112
},
82113
{
83114
key: 'Content-Security-Policy',
84-
value: "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://vercel.live https://vitals.vercel-insights.com https://va.vercel-scripts.com https://www.googletagmanager.com https://www.google-analytics.com https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://www.sortvision.com; style-src-elem 'self' 'unsafe-inline' https://fonts.googleapis.com https://www.sortvision.com; script-src-elem 'self' 'unsafe-inline' 'unsafe-eval' https://vercel.live https://vitals.vercel-insights.com https://va.vercel-scripts.com https://www.googletagmanager.com https://www.google-analytics.com https://fonts.googleapis.com https://www.sortvision.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' data: https: blob:; media-src 'self' data: blob:; connect-src 'self' https: data: blob:; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; worker-src 'self' blob:;",
115+
value: contentSecurityPolicy,
85116
},
86117
],
87118
};

SortVision/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"class-variance-authority": "^0.7.1",
4949
"clsx": "^2.1.1",
5050
"cors": "^2.8.6",
51+
"dompurify": "^3.4.1",
5152
"dotenv": "^17.4.2",
5253
"ejs": "^5.0.2",
5354
"filelist": "^2.0.2",

SortVision/pnpm-lock.yaml

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SortVision/src/app/layout.jsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ export const metadata = {
4343
contact: 'https://github.com/alienx5499',
4444
twitter: '@alienx5499',
4545
github: 'https://github.com/alienx5499',
46-
'Content-Security-Policy':
47-
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://vercel.live https://vitals.vercel-insights.com https://va.vercel-scripts.com https://www.googletagmanager.com https://www.google-analytics.com https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com data:; img-src 'self' data: https: blob:; media-src 'self' data: blob:; connect-src 'self' https: data: blob:; frame-src 'none'; object-src 'none'; base-uri 'self'; form-action 'self'; worker-src 'self' blob:;",
4846
},
4947
verification: {
5048
google: 'google12e2679e2ea95334',

SortVision/src/components/chatbot/ChatModal.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
CardDescription,
99
CardContent,
1010
} from '@/components/ui/card';
11+
import { sanitizeChatHtml } from './sanitizeChatHtml';
1112

1213
const ChatModal = ({
1314
isOpen,
@@ -111,7 +112,9 @@ const ChatModal = ({
111112
)}
112113
<div
113114
className="flex-1"
114-
dangerouslySetInnerHTML={{ __html: msg.content }}
115+
dangerouslySetInnerHTML={{
116+
__html: sanitizeChatHtml(msg.content),
117+
}}
115118
/>
116119
</div>
117120
</div>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import DOMPurify from 'dompurify';
2+
3+
const SANITIZE_OPTIONS = {
4+
ALLOWED_TAGS: ['div', 'p', 'span', 'br', 'b', 'strong', 'i', 'em', 'code'],
5+
ALLOWED_ATTR: ['class'],
6+
FORBID_TAGS: ['style', 'script', 'iframe', 'object', 'embed'],
7+
FORBID_ATTR: ['onerror', 'onclick', 'onload', 'style'],
8+
};
9+
10+
export function sanitizeChatHtml(content) {
11+
return DOMPurify.sanitize(String(content || ''), SANITIZE_OPTIONS);
12+
}

0 commit comments

Comments
 (0)