mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-29 06:32:28 +00:00
fix(web): MermaidDiagram XSS, isSafeId allows @, rawMermaid typed
- Add DOMPurify.sanitize() to MermaidDiagram.tsx dangerouslySetInnerHTML (was rendering AI-generated SVG unsanitized — the highest-risk surface) - Add @ to isSafeId regex for scoped npm packages (@angular/core, etc.) - Add rawMermaid to ProcessData interface, remove (process as any) casts - Update security tests: @scope/pkg now accepted, @angular/core added Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
8919b80efc
commit
4c2b3b86f0
5 changed files with 9 additions and 6 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { useEffect, useRef, useState } from 'react';
|
||||
import mermaid from 'mermaid';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { AlertTriangle, Maximize2 } from 'lucide-react';
|
||||
import { ProcessFlowModal } from './ProcessFlowModal';
|
||||
import type { ProcessData } from '../lib/mermaid-generator';
|
||||
|
|
@ -140,7 +141,7 @@ export const MermaidDiagram = ({ code }: MermaidDiagramProps) => {
|
|||
<div
|
||||
ref={containerRef}
|
||||
className="flex items-center justify-center p-4 overflow-auto max-h-[400px]"
|
||||
dangerouslySetInnerHTML={{ __html: svg }}
|
||||
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true, svgFilters: true } }) }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -138,8 +138,8 @@ export const ProcessFlowModal = ({ process, onClose, onFocusInGraph, isFullScree
|
|||
const renderDiagram = async () => {
|
||||
try {
|
||||
// Check if we have raw mermaid code (from AI chat) or need to generate it
|
||||
const mermaidCode = (process as any).rawMermaid
|
||||
? (process as any).rawMermaid
|
||||
const mermaidCode = process.rawMermaid
|
||||
? process.rawMermaid
|
||||
: generateProcessMermaid(process);
|
||||
const id = `mermaid-${Date.now()}`;
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { ProcessFlowModal } from './ProcessFlowModal';
|
|||
import type { ProcessData, ProcessStep } from '../lib/mermaid-generator';
|
||||
|
||||
/** Validate that an ID contains only expected node identifier characters (no Cypher metacharacters or spaces) */
|
||||
const isSafeId = (id: string): boolean => /^[a-zA-Z0-9_:.\-/]+$/.test(id);
|
||||
const isSafeId = (id: string): boolean => /^[a-zA-Z0-9_:.\-/@]+$/.test(id);
|
||||
|
||||
export const ProcessesPanel = () => {
|
||||
const { graph, runQuery, setHighlightedNodeIds, highlightedNodeIds } = useAppState();
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export interface ProcessData {
|
|||
steps: ProcessStep[];
|
||||
edges?: ProcessEdge[]; // CALLS edges between steps for branching
|
||||
clusters?: string[];
|
||||
rawMermaid?: string; // AI-generated mermaid code (sanitized before rendering)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const validRelType = (t: string): boolean =>
|
|||
(REL_TYPES as readonly string[]).includes(t);
|
||||
|
||||
const isSafeId = (id: string): boolean =>
|
||||
/^[a-zA-Z0-9_:.\-/]+$/.test(id);
|
||||
/^[a-zA-Z0-9_:.\-/@]+$/.test(id);
|
||||
|
||||
const isWriteQuery = (cypher: string): boolean =>
|
||||
/\b(CREATE|DELETE|SET|MERGE|REMOVE|DROP|DETACH)\b/.test(cypher.toUpperCase());
|
||||
|
|
@ -104,13 +104,14 @@ describe('isSafeId – identifier allowlist regex', () => {
|
|||
['community id', 'comm_5'],
|
||||
['file path id', 'File:src/index.ts'],
|
||||
['nested path id', 'Function:src/utils/helpers.ts:doStuff'],
|
||||
['scoped npm package', 'Module:@scope/pkg'],
|
||||
['angular-style id', 'Module:@angular/core'],
|
||||
])('accepts valid ID: %s', (_desc, id) => {
|
||||
expect(isSafeId(id)).toBe(true);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['with spaces', 'Process:my process'],
|
||||
['with @ symbol', 'Module:@scope/pkg'],
|
||||
])('rejects ID with unsafe chars: %s', (_desc, id) => {
|
||||
expect(isSafeId(id)).toBe(false);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue