fix: enhance security by enclosing html in code blocks and csp (#2604)
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||
import { oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import { Check, Copy } from './icons';
|
||||
@@ -77,12 +76,42 @@ const MarkdownCode = React.forwardRef(function MarkdownCode(
|
||||
);
|
||||
});
|
||||
|
||||
// Detect if content contains HTML
|
||||
const containsHTML = (str: string) => {
|
||||
const htmlRegex = /<[^>]*>/;
|
||||
return htmlRegex.test(str);
|
||||
};
|
||||
|
||||
// Wrap HTML content in code blocks
|
||||
const wrapHTMLInCodeBlock = (content: string) => {
|
||||
if (containsHTML(content)) {
|
||||
// Split content by code blocks to preserve existing ones
|
||||
const parts = content.split(/(```[\s\S]*?```)/g);
|
||||
return parts
|
||||
.map((part) => {
|
||||
// If part is already a code block, leave it as is
|
||||
if (part.startsWith('```') && part.endsWith('```')) {
|
||||
return part;
|
||||
}
|
||||
// If part contains HTML, wrap it in HTML code block
|
||||
if (containsHTML(part)) {
|
||||
return `\`\`\`html\n${part}\n\`\`\``;
|
||||
}
|
||||
return part;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
return content;
|
||||
};
|
||||
|
||||
export default function MarkdownContent({ content, className = '' }: MarkdownContentProps) {
|
||||
// Process content before rendering
|
||||
const processedContent = wrapHTMLInCodeBlock(content);
|
||||
|
||||
return (
|
||||
<div className="w-full overflow-x-hidden">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm]}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
className={`prose prose-sm text-textStandard dark:prose-invert w-full max-w-full word-break
|
||||
prose-pre:p-0 prose-pre:m-0 !p-0
|
||||
prose-code:break-all prose-code:whitespace-pre-wrap
|
||||
@@ -105,7 +134,7 @@ export default function MarkdownContent({ content, className = '' }: MarkdownCon
|
||||
code: MarkdownCode,
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
{processedContent}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -762,6 +762,44 @@ const registerGlobalHotkey = (accelerator: string) => {
|
||||
};
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
// Add CSP headers to all sessions
|
||||
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
|
||||
callback({
|
||||
responseHeaders: {
|
||||
...details.responseHeaders,
|
||||
'Content-Security-Policy': [
|
||||
"default-src 'self';" +
|
||||
// Allow inline styles since we use them in our React components
|
||||
"style-src 'self' 'unsafe-inline';" +
|
||||
// Scripts only from our app
|
||||
"script-src 'self';" +
|
||||
// Images from our app and data: URLs (for base64 images)
|
||||
"img-src 'self' data: https:;" +
|
||||
// Connect to our local API and specific external services
|
||||
"connect-src 'self' http://127.0.0.1:*" +
|
||||
// Don't allow any plugins
|
||||
"object-src 'none';" +
|
||||
// Don't allow any frames
|
||||
"frame-src 'none';" +
|
||||
// Font sources
|
||||
"font-src 'self';" +
|
||||
// Media sources
|
||||
"media-src 'none';" +
|
||||
// Form actions
|
||||
"form-action 'none';" +
|
||||
// Base URI restriction
|
||||
"base-uri 'self';" +
|
||||
// Manifest files
|
||||
"manifest-src 'self';" +
|
||||
// Worker sources
|
||||
"worker-src 'self';" +
|
||||
// Upgrade insecure requests
|
||||
'upgrade-insecure-requests;',
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Register the default global hotkey
|
||||
registerGlobalHotkey('CommandOrControl+Alt+Shift+G');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user