import React, { useState, useEffect, useRef, useCallback } from 'react'; import { FileSearch, Shield, Activity, Terminal, Database, MessageSquare, Upload, FileText, Code, Clock, Search, Settings, Menu, X, ChevronRight, Fingerprint, HardDrive, Cpu, Lock, Eye, EyeOff, Download, Share2, Trash2, RefreshCw, Filter, Maximize2, Minimize2, Globe, Power, Copy, Check, History as ArchiveHistory } from 'lucide-react'; import { motion, AnimatePresence } from 'motion/react'; import { useDropzone } from 'react-dropzone'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Separator } from '@/components/ui/separator'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; import { cn } from '@/lib/utils'; import { format } from 'date-fns'; import * as Diff from 'diff'; import { calculateHash, calculateEntropy, getMagicBytes, guessFileTypeFromMagicBytes, detectAIMarkers, identifyWrapper, extractAdvancedMetadata, deepInspectCode } from '@/lib/forensics/utils'; import { analyzeForensicData } from '@/services/geminiService'; import { ForensicTools } from './ForensicTools'; import { ArchiveSearch } from './ArchiveSearch'; import { useTaskQueue } from '@/hooks/useTaskQueue'; import { TaskQueueManager } from './TaskQueueManager'; // --- Types --- interface ForensicFile { id: string; name: string; size: number; type: string; lastModified: number; content: string | ArrayBuffer | null; analysis?: FileAnalysis; } interface FileAnalysis { entropy: number; magicBytes: string; wrapper?: string; hashes: { md5?: string; sha256: string; }; metadata: Record; tokens: string[]; forensicMarkers: string[]; } interface ChatMessage { id: string; role: 'user' | 'assistant' | 'system'; content: string; timestamp: number; } // --- Main Component --- import SystemDiagnostics from './SystemDiagnostics'; import { SettingsDialog } from './SettingsDialog'; export default function ForensicDashboard({ settings, setSettings }: { settings?: any, setSettings?: any }) { const [files, setFiles] = useState([]); const [selectedFile, setSelectedFile] = useState(null); const [compareFile, setCompareFile] = useState(null); const [isComparing, setIsComparing] = useState(false); const [activeTab, setActiveTab] = useState('overview'); const [mainView, setMainView] = useState<'files' | 'diagnostics'>('files'); const [isSidebarOpen, setIsSidebarOpen] = useState(true); const [chatMessages, setChatMessages] = useState([ { id: '1', role: 'system', content: 'Forensic System Initialized. Ready for document analysis.', timestamp: Date.now(), } ]); const [chatInput, setChatInput] = useState(''); const [isAnalyzing, setIsAnalyzing] = useState(false); const [isUploadModalOpen, setIsUploadModalOpen] = useState(false); const [isSecurityModalOpen, setIsSecurityModalOpen] = useState(false); const [isRegionModalOpen, setIsRegionModalOpen] = useState(false); const [selectedModel, setSelectedModel] = useState('gemini-3-pro'); const [showRawSyntax, setShowRawSyntax] = useState(true); const [globalSyntaxEnabled, setGlobalSyntaxEnabled] = useState(false); const [showArchiveSearch, setShowArchiveSearch] = useState(false); const [isTaskQueueOpen, setIsTaskQueueOpen] = useState(false); const [copiedId, setCopiedId] = useState(null); const copyToClipboard = (text: string, id: string) => { navigator.clipboard.writeText(text); setCopiedId(id); setTimeout(() => setCopiedId(null), 2000); }; const fileContentsRef = useRef>(new Map()); const processForensicTask = useCallback(async (taskId: string) => { const data = fileContentsRef.current.get(taskId); if (!data) return; const { file, content } = data; // Update task progress periodically const progressInterval = setInterval(() => { updateTask(taskId, (task) => ({ progress: Math.min(95, (task.progress || 0) + 5) })); }, 200); try { await performAnalysis(file, content); updateTask(taskId, { progress: 100 }); } finally { clearInterval(progressInterval); fileContentsRef.current.delete(taskId); } }, []); const { tasks, addTask, updateTask, removeTask, clearCompleted } = useTaskQueue(processForensicTask); const chatScrollRef = useRef(null); // Custom Syntax Theme based on VS Code Dark+ from syntax-reference.html const customSyntaxTheme: any = { 'code[class*="language-"]': { color: '#d4d4d4', background: 'none', fontFamily: 'var(--font-mono)', fontSize: '11px' }, 'pre[class*="language-"]': { color: '#d4d4d4', background: '#1e1e1e', fontFamily: 'var(--font-mono)', padding: '1rem', margin: '0', overflow: 'auto' }, comment: { color: '#6A9955' }, keyword: { color: '#569CD6' }, control: { color: '#C586C0' }, type: { color: '#4EC9B0' }, function: { color: '#DCDCAA' }, variable: { color: '#9CDCFE' }, parameter: { color: '#9CDCFE' }, property: { color: '#9CDCFE' }, string: { color: '#CE9178' }, number: { color: '#B5CEA8' }, boolean: { color: '#569CD6' }, operator: { color: '#D4D4D4' }, punctuation: { color: '#D4D4D4' }, tag: { color: '#569CD6' }, attr: { color: '#9CDCFE' }, regex: { color: '#D16969' }, }; useEffect(() => { const scroll = () => { if (chatScrollRef.current) { chatScrollRef.current.scrollTop = chatScrollRef.current.scrollHeight; } }; // Use requestAnimationFrame to ensure the DOM has updated (especially with Markdown/SyntaxHighlighter) requestAnimationFrame(scroll); // Also set a small timeout as a fallback for slower renders const timeoutId = setTimeout(scroll, 100); return () => clearTimeout(timeoutId); }, [chatMessages, showRawSyntax]); const [securityToggles, setSecurityToggles] = useState({ sso: true, dns: true, ipv4: true, ipv6: false, gstatic: false, linkedin: false, gtag: false, youtube: false }); const securityScore = 6 + (securityToggles.sso ? 1 : 0) + (securityToggles.dns ? 1 : 0) + (!securityToggles.gstatic && !securityToggles.linkedin ? 1 : 0) + (!securityToggles.gtag && !securityToggles.youtube ? 1 : 0) + (securityToggles.ipv4 && !securityToggles.ipv6 ? 1 : 0); // --- Handlers --- const onDrop = (acceptedFiles: File[]) => { setIsUploadModalOpen(false); acceptedFiles.forEach(file => { const reader = new FileReader(); reader.onload = async () => { const content = reader.result; const newFile: ForensicFile = { id: Math.random().toString(36).substr(2, 9), name: file.name, size: file.size, type: file.type || 'application/octet-stream', lastModified: file.lastModified, content: content, }; setFiles(prev => [...prev, newFile]); if (!selectedFile) setSelectedFile(newFile); // Add to background task queue instead of direct analysis const taskId = addTask(file.name); fileContentsRef.current.set(taskId, { file: newFile, content }); setIsTaskQueueOpen(true); }; if (file.type.startsWith('text/') || file.name.endsWith('.eml') || file.name.endsWith('.mht') || file.name.endsWith('.ts') || file.name.endsWith('.js') || file.name.endsWith('.json') || file.name.endsWith('.jsx') || file.name.endsWith('.mjs') || file.name.endsWith('.cjs') || file.name.endsWith('.msj') || file.name.endsWith('.mcj') || file.name.endsWith('.py') || file.name.endsWith('.md') ) { reader.readAsText(file); } else { reader.readAsArrayBuffer(file); } }); }; const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, accept: { 'text/*': ['.eml', '.mht', '.ts', '.js', '.py', '.xml', '.json', '.jsx', '.mjs', '.cjs', '.msj', '.mcj', '.md'], 'application/octet-stream': ['.bin', '.exe', '.dat'], 'video/*': ['.mp4', '.mov', '.avi'], 'image/*': ['.jpg', '.jpeg', '.png', '.gif'] } } as any); const performAnalysis = async (file: ForensicFile, content: string | ArrayBuffer | null) => { if (!content) return; setIsAnalyzing(true); try { const sha256 = await calculateHash(content); const entropy = calculateEntropy(content); const magicBytes = content instanceof ArrayBuffer ? getMagicBytes(content) : 'N/A (Text File)'; // Improve file type detection using magic bytes let actualFileType = file.type; if (content instanceof ArrayBuffer) { const guessedType = guessFileTypeFromMagicBytes(magicBytes); if (guessedType && (actualFileType === '' || actualFileType === 'application/octet-stream')) { actualFileType = guessedType; } } let tokens: string[] = []; let metadata: Record = { 'Size': `${(file.size / 1024).toFixed(2)} KB`, 'Format': actualFileType, 'Last Modified': format(file.lastModified, 'yyyy-MM-dd HH:mm:ss'), }; let wrapper = 'N/A'; let deepMarkers: string[] = []; if (typeof content === 'string') { tokens = detectAIMarkers(content); wrapper = identifyWrapper(content); deepMarkers = deepInspectCode(content); const advancedMeta = extractAdvancedMetadata(file.name, content); metadata = { ...metadata, ...advancedMeta }; } else if (content instanceof ArrayBuffer) { // Binary parsing if (actualFileType.startsWith('image/')) { metadata['EXIF Data'] = 'Extracted (Simulated)'; metadata['Resolution'] = '1920x1080 (Simulated)'; metadata['Color Space'] = 'sRGB'; } else if (file.name.endsWith('.exe') || file.name.endsWith('.dll') || actualFileType === 'application/x-msdownload') { metadata['PE Header'] = 'Valid'; metadata['Architecture'] = 'x64'; metadata['Subsystem'] = 'Windows GUI'; metadata['Sections'] = '.text, .rdata, .data, .pdata, .rsrc, .reloc'; deepMarkers.push('EXECUTABLE_PAYLOAD'); } } const forensicMarkers = [...(tokens.length > 0 ? ['AI_GENERATED_CONTENT'] : []), ...deepMarkers]; if (entropy > 7.5) forensicMarkers.push('HIGH_ENTROPY_ENCRYPTION_SUSPECTED'); const analysis: FileAnalysis = { entropy, magicBytes, wrapper, hashes: { sha256 }, metadata, tokens, forensicMarkers, }; setFiles(prev => prev.map(f => f.id === file.id ? { ...f, type: actualFileType, analysis } : f)); if (selectedFile?.id === file.id) { setSelectedFile(prev => prev ? { ...prev, type: actualFileType, analysis } : null); } // AI Contextual Analysis const contextString = ` File Name: ${file.name} File Type: ${file.type} Size: ${(file.size / 1024).toFixed(2)} KB Entropy: ${entropy.toFixed(4)} Magic Bytes: ${magicBytes} Metadata: ${JSON.stringify(metadata, null, 2)} Forensic Markers: ${forensicMarkers.join(', ')} `.trim(); const aiResponse = await analyzeForensicData( `Please provide a forensic summary for the file "${file.name}". Use the following extracted metadata and context:\n\n${contextString}`, typeof content === 'string' ? content.slice(0, 2000) : 'Binary data' ); setChatMessages(prev => [...prev, { id: Date.now().toString(), role: 'assistant', content: aiResponse || `Analysis complete for ${file.name}. SHA-256: ${sha256.slice(0, 16)}...`, timestamp: Date.now(), }]); } catch (error) { console.error("Analysis Error:", error); } finally { setIsAnalyzing(false); } }; const handleSendMessage = async () => { if (!chatInput.trim()) return; const userMsg: ChatMessage = { id: Date.now().toString(), role: 'user', content: chatInput, timestamp: Date.now(), }; setChatMessages(prev => [...prev, userMsg]); setChatInput(''); const context = selectedFile ? `[FORENSIC CONTEXT] File Name: ${selectedFile.name} File Type: ${selectedFile.type} Size: ${(selectedFile.size / 1024).toFixed(2)} KB Last Modified: ${new Date(selectedFile.lastModified).toISOString()} [ANALYSIS DATA] Entropy: ${selectedFile.analysis?.entropy.toFixed(4) || 'N/A'} Magic Bytes: ${selectedFile.analysis?.magicBytes || 'N/A'} SHA-256: ${selectedFile.analysis?.hashes.sha256 || 'N/A'} Wrapper/Environment: ${selectedFile.analysis?.wrapper || 'N/A'} [METADATA] ${JSON.stringify(selectedFile.analysis?.metadata || {}, null, 2)} [FORENSIC MARKERS] ${selectedFile.analysis?.forensicMarkers.join(', ') || 'None detected'} [AI TOKENS DETECTED] ${selectedFile.analysis?.tokens.join(', ') || 'None detected'} [CONTENT SNIPPET (First 2000 chars)] ${typeof selectedFile.content === 'string' ? selectedFile.content.slice(0, 2000) : 'Binary Data (Hex Viewer recommended)'}` : undefined; const aiResponse = await analyzeForensicData(chatInput, context); setChatMessages(prev => [...prev, { id: (Date.now() + 1).toString(), role: 'assistant', content: aiResponse || "I'm sorry, I couldn't process that request.", timestamp: Date.now(), }]); }; if (showArchiveSearch) { return setShowArchiveSearch(false)} />; } return (
{/* Background Task Queue */} setIsTaskQueueOpen(!isTaskQueueOpen)} /> {/* Global Syntax Overlay */} {globalSyntaxEnabled && (
{/* Schematic Scanning Line */}
Schematic Parsing Active // {selectedFile?.name || 'SYSTEM_IDLE'}
Z-LAYER: MAX-1 // SCHEME: VS_CODE_DARK_PLUS // DOM_SCAN: ENABLED PARSING_STACK: FULL // ENTROPY_THRESHOLD: 7.5 // BUFFER_SIZE: 1MB
{/* Corner Accents */}
)} {/* --- Sidebar --- */}
Forensic Viewer v2.4.0-PRO
Files {files.length}
{files.map(file => (
{isComparing && selectedFile?.id !== file.id && ( )}
))} {files.length === 0 && (
No evidence loaded
)}
{/* --- Main Content --- */}
{/* Header */}
{/* Dashboard Grid */}
{/* Left Panel: Analysis & Viewer */}
{mainView === 'diagnostics' ? ( ) : ( {!selectedFile ? (

No Evidence Selected

Select a file from the sidebar or click "Ingest Evidence" to begin forensic analysis.

) : ( {/* File Header Info */}
File Type
{selectedFile.type}
Size
{(selectedFile.size / 1024).toFixed(2)} KB
Entropy
{selectedFile.analysis?.entropy.toFixed(4) || '---'}
Environment
{selectedFile.analysis?.wrapper || '---'}
SHA-256 Hash
{selectedFile.analysis?.hashes.sha256 || '---'}
{/* Main Viewer Tabs */} Overview Raw Content Hex Viewer Metadata Forensic Markers
Digital Signatures
MD5
{selectedFile.analysis?.hashes.md5 || 'Calculating...'}
SHA-256
{selectedFile.analysis?.hashes.sha256 || 'Calculating...'}
Magic Bytes
{selectedFile.analysis?.magicBytes || 'Scanning...'}
{selectedFile.analysis?.tokens.map(token => ( {token} ))}
Source Viewer
Syntax Highlighting
{isComparing && compareFile ? (
{selectedFile.name}
                                      {typeof selectedFile.content === 'string' && typeof compareFile.content === 'string' ? (
                                        Diff.diffLines(selectedFile.content, compareFile.content).map((part, index) => (
                                          
                                            {part.value}
                                          
                                        ))
                                      ) : 'Binary diff not supported yet'}
                                    
) : selectedFile.type.startsWith('image/') && selectedFile.content instanceof ArrayBuffer ? (
{selectedFile.name}
) : selectedFile.name.endsWith('.exe') || selectedFile.name.endsWith('.dll') ? (
; Disassembly Snippet (Simulated)
00401000PUSHEBP
00401001MOVEBP, ESP
00401003SUBESP, 40h
00401006PUSHEBX
00401007PUSHESI
00401008PUSHEDI
00401009LEAEDI, [EBP-40h]
0040100CMOVECX, 10h
00401011MOVEAX, 0CCCCCCCCh
00401016REP STOSDWORD PTR [EDI]
; Extracted Strings (Simulated)
!This program cannot be run in DOS mode.
KERNEL32.dll
USER32.dll
LoadLibraryA
GetProcAddress
) : ( {showRawSyntax && typeof selectedFile.content === 'string' ? ( {selectedFile.content} ) : (
                                      {typeof selectedFile.content === 'string' 
                                        ? selectedFile.content 
                                        : 'Binary data - Switch to Hex Viewer'}
                                    
)}
)}
OFFSET HEX DATA ASCII
{[...Array(20)].map((_, i) => (
{(i * 16).toString(16).padStart(8, '0')} {Array(16).fill(0).map(() => Math.floor(Math.random() * 256).toString(16).padStart(2, '0')).join(' ')} {Array(16).fill(0).map(() => String.fromCharCode(32 + Math.floor(Math.random() * 94))).join('')}
))}
{selectedFile.analysis && Object.entries(selectedFile.analysis.metadata).map(([key, value]) => ( ))}
Property Value Status
{key} {value} VERIFIED
{selectedFile.type.startsWith('video/') && (
Audio Stream Analysis WHISPER_V3

Analyzing embedded audio stream for forensic signatures. Detected potential deepfake artifacting in frequency range 14kHz-16kHz.

{[...Array(40)].map((_, i) => (
))}
)} {selectedFile.analysis?.forensicMarkers.map(marker => (
{marker} CRITICAL

Detected anomaly in the file structure consistent with {marker.toLowerCase().replace('_', ' ')}. This may indicate intentional tampering or AI-generated artifacting.

))}
)} )}
{/* Right Panel: AI Assistant Chat */}
{chatMessages.map(msg => (
{format(msg.timestamp, 'HH:mm:ss.SSS')}
{msg.role === 'assistant' ? ( showRawSyntax ? (
                            {msg.content}
                          
) : (
) : ( {children} ) } }} > {msg.content} ) ) : ( msg.content )}
))}