Critical Securities (5) fixes
This commit is contained in:
@@ -2,6 +2,7 @@ import { useState, useRef } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
|
||||
import { Volume2, Loader2, Square } from 'lucide-react';
|
||||
import type { ChatMessage } from '../types';
|
||||
import { ToolActivity } from './ToolActivity';
|
||||
@@ -9,6 +10,15 @@ import { QuestionActivity } from './QuestionActivity';
|
||||
import { getRawUrl } from '../../FileViewer/file-types';
|
||||
import { useFilesAPI } from '../../../hooks/useFilesAPI';
|
||||
|
||||
const sanitizeSchema = {
|
||||
...defaultSchema,
|
||||
tagNames: (defaultSchema.tagNames ?? []).filter((tag) => tag !== 'script' && tag !== 'iframe' && tag !== 'object' && tag !== 'embed' && tag !== 'form'),
|
||||
attributes: {
|
||||
...defaultSchema.attributes,
|
||||
'*': (defaultSchema.attributes?.['*'] ?? []).filter((attr) => typeof attr === 'string' && !attr.startsWith('on')),
|
||||
},
|
||||
};
|
||||
|
||||
// Matches absolute image file paths, e.g. /home/user/pic.png or /tmp/photo.jpg
|
||||
const IMAGE_PATH_RE = /(\/(?:home\/[^/\s]+\/)?[^\s`"'<>\n\r[\]()]+\.(?:png|jpg|jpeg|gif|webp|svg|bmp|ico))/gi;
|
||||
|
||||
@@ -129,7 +139,7 @@ export const MessageBubble = ({ message, onAnswer }: MessageBubbleProps) => {
|
||||
<div className="max-w-[85%]">
|
||||
<div className="rounded-2xl rounded-tl-sm bg-muted/50 border border-border/50 px-4 py-2.5">
|
||||
<div className="chat-md">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw, [rehypeSanitize, sanitizeSchema]]}>
|
||||
{injectImages(assistantText)}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
@@ -178,7 +188,7 @@ export const StreamingBubble = ({ text }: StreamingBubbleProps) => {
|
||||
<div className="flex justify-start">
|
||||
<div className="max-w-[85%] rounded-2xl rounded-tl-sm bg-muted/50 border border-border/50 px-4 py-2.5">
|
||||
<div className="chat-md">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw, [rehypeSanitize, sanitizeSchema]]}>
|
||||
{injectImages(text)}
|
||||
</ReactMarkdown>
|
||||
<span className="inline-block w-2 h-4 bg-duck-teal/60 animate-pulse ml-0.5 align-middle" />
|
||||
|
||||
@@ -35,7 +35,8 @@ export const PreviewProvider = ({ children }: PreviewProviderProps) => {
|
||||
setStopped(false);
|
||||
try {
|
||||
const res = await client.post<DevServerResponse>('/dev-server/start', { slug: targetSlug });
|
||||
setUrl(res.url);
|
||||
const token = client.token;
|
||||
setUrl(token ? `${res.url}?token=${encodeURIComponent(token)}` : res.url);
|
||||
setPort(res.port);
|
||||
} catch (err: unknown) {
|
||||
const msg = err && typeof err === 'object' && 'message' in err ? String(err.message) : 'Failed to start dev server';
|
||||
@@ -81,7 +82,8 @@ export const PreviewProvider = ({ children }: PreviewProviderProps) => {
|
||||
const status = await client.get<DevServerStatus>(`/dev-server/status?slug=${encodeURIComponent(slug)}`);
|
||||
if (cancelled) return;
|
||||
if (status.running && status.url) {
|
||||
setUrl(status.url);
|
||||
const token = client.token;
|
||||
setUrl(token ? `${status.url}?token=${encodeURIComponent(token)}` : status.url);
|
||||
setPort(status.port ?? null);
|
||||
} else {
|
||||
startServer(slug);
|
||||
|
||||
Reference in New Issue
Block a user