diff --git a/frontend/src/components/ui/markdown-renderer.tsx b/frontend/src/components/ui/markdown-renderer.tsx
index a0786867..81f0a96e 100644
--- a/frontend/src/components/ui/markdown-renderer.tsx
+++ b/frontend/src/components/ui/markdown-renderer.tsx
@@ -10,6 +10,103 @@ import { Button } from '@/components/ui/button.tsx';
import { Check, Clipboard } from 'lucide-react';
import { writeClipboardViaBridge } from '@/vscode/bridge';
+const HIGHLIGHT_LINK =
+ 'rounded-sm bg-muted/50 px-1 py-0.5 underline-offset-2 transition-colors';
+const HIGHLIGHT_LINK_HOVER = 'hover:bg-muted';
+const HIGHLIGHT_CODE = 'rounded-sm bg-muted/50 px-1 py-0.5 font-mono text-sm';
+
+function sanitizeHref(href?: string): string | undefined {
+ if (typeof href !== 'string') return undefined;
+ const trimmed = href.trim();
+ // Block dangerous protocols
+ if (/^(javascript|vbscript|data):/i.test(trimmed)) return undefined;
+ // Allow anchors and common relative forms
+ if (
+ trimmed.startsWith('#') ||
+ trimmed.startsWith('./') ||
+ trimmed.startsWith('../') ||
+ trimmed.startsWith('/')
+ )
+ return trimmed;
+ // Allow only https
+ if (/^https:\/\//i.test(trimmed)) return trimmed;
+ // Block everything else by default
+ return undefined;
+}
+
+function isExternalHref(href?: string): boolean {
+ if (!href) return false;
+ return /^https:\/\//i.test(href);
+}
+
+function LinkOverride({
+ href,
+ children,
+ title,
+}: {
+ href?: string;
+ children: React.ReactNode;
+ title?: string;
+}) {
+ const rawHref = typeof href === 'string' ? href : '';
+ const safeHref = sanitizeHref(rawHref);
+
+ const external = isExternalHref(safeHref);
+ const internalOrDisabled = !external;
+
+ if (!safeHref || internalOrDisabled) {
+ // Disabled internal link (relative paths and anchors)
+ return (
+
+ {children}
+
+ );
+ }
+
+ // External link
+ return (
+ {
+ e.stopPropagation();
+ }}
+ >
+ {children}
+
+ );
+}
+
+function InlineCodeOverride({ children, className, ...props }: any) {
+ // Only highlight inline code, not fenced code blocks
+ const hasLanguage =
+ typeof className === 'string' && /\blanguage-/.test(className);
+ if (hasLanguage) {
+ // Likely a fenced block's ; leave className as-is for syntax highlighting
+ return (
+
+ {children}
+
+ );
+ }
+ return (
+
+ {children}
+
+ );
+}
+
interface MarkdownRendererProps {
content: string;
className?: string;
@@ -23,16 +120,8 @@ function MarkdownRenderer({
}: MarkdownRendererProps) {
const overrides = useMemo(
() => ({
- code: {
- component: ({ children, ...props }: any) => (
-
- {children}
-
- ),
- },
+ a: { component: LinkOverride },
+ code: { component: InlineCodeOverride },
strong: {
component: ({ children, ...props }: any) => (