+
📊 Executive Summary
+
+ This security assessment of ${status.target} was conducted using NeuroSploit AI-powered penetration testing platform.
+ The assessment identified ${status.findings.length} security findings across various severity levels.
+ ${severityCounts.critical > 0 ? `${severityCounts.critical} critical vulnerabilities require immediate attention.` : ''}
+ ${severityCounts.high > 0 ? `${severityCounts.high} high-severity issues should be addressed promptly.` : ''}
+
+
+
+
Overall Risk Score
+
${riskScore}/100
+
+
+
+
+ `
+
+ return `
+
+
+
+ {/* CVSS & Meta Info */}
+
+
+ CVSS:
+ = 9 ? 'text-red-500' :
+ finding.cvss_score >= 7 ? 'text-orange-500' :
+ finding.cvss_score >= 4 ? 'text-yellow-500' :
+ 'text-blue-500'
+ }`}>
+ {finding.cvss_score?.toFixed(1) || 'N/A'}
+
+
+ {finding.cwe_id && (
+
+ )}
+
+ {finding.vulnerability_type}
+
+ {finding.confidence && (
+
+ {finding.confidence} confidence
+
+ )}
+
+
+ {/* CVSS Vector */}
+ {finding.cvss_vector && (
+
+ {finding.cvss_vector}
+
+ )}
+
+ {/* Technical Details Section */}
+
+
+
+ Technical Details
+
+
+ {/* Affected Endpoint */}
+
+
Endpoint:
+
+
+
+ {finding.affected_endpoint}
+
+
+
+
+ {/* Parameter */}
+ {finding.parameter && (
+
+ Vulnerable Parameter:
+
+ {finding.parameter}
+
+
+ )}
+
+ {/* Payload */}
+ {finding.payload && (
+
+
+ Payload Used:
+
+
+
+ {finding.payload}
+
+
+ )}
+
+ {/* HTTP Request */}
+ {finding.request && (
+
+
+ HTTP Request:
+
+
+
+ {finding.request}
+
+
+ )}
+
+ {/* HTTP Response */}
+ {finding.response && (
+
+
+ HTTP Response (excerpt):
+
+
+
+ {finding.response}
+
+
+ )}
+
+ {/* Evidence */}
+ {finding.evidence && (
+
+
Evidence:
+
+ {finding.evidence}
+
+
+ )}
+
+
+ {/* Description */}
+ {finding.description && (
+
+
Description
+
{finding.description}
+
+ )}
+
+ {/* Impact */}
+ {finding.impact && (
+
+
Impact
+
{finding.impact}
+
+ )}
+
+ {/* PoC Code */}
+ {finding.poc_code && (
+
+
+
Proof of Concept
+
+
+
+ {finding.poc_code}
+
+
+ )}
+
+ {/* Remediation */}
+ {finding.remediation && (
+
+
Remediation
+
{finding.remediation}
+
+ )}
+
+ {/* References */}
+ {finding.references && finding.references.length > 0 && (
+
+ )}
+
+ )
+
+ const renderLogViewer = (logsToShow: AgentLog[], endRef: React.RefObject, title: string, icon: React.ReactNode) => (
+
+ {logsToShow.length === 0 ? (
+
No {title.toLowerCase()} activity yet...
+ ) : (
+ logsToShow.map((log, i) => {
+ // Special styling for user prompts and AI responses
+ const isUserPrompt = log.message.includes('[USER PROMPT]')
+ const isAIResponse = log.message.includes('[AI RESPONSE]') || log.message.includes('[AI]')
+
+ return (
+
+
+ {new Date(log.time).toLocaleTimeString()}
+
+
+ {isUserPrompt ? :
+ isAIResponse ? :
+ icon}
+
+
+ {log.message}
+
+
+ )
+ })
+ )}
+
+
+ )
+
+ return (
+
+ {/* Header */}
+
+
+
+
+ Agent: {agentId}
+
+
+
+ {PHASE_ICONS[status.status]}
+ {status.status.charAt(0).toUpperCase() + status.status.slice(1)}
+
+ Mode: {MODE_LABELS[status.mode] || status.mode}
+ {status.task && Task: {status.task}}
+
+
+
+ {status.status === 'running' && (
+
+ )}
+ {status.scan_id && (
+
+ )}
+ {/* Always show export if there are findings */}
+ {(status.findings.length > 0 || status.report) && (
+
+
+
+
+ )}
+
+
+
+ {/* Progress with Phase Steps */}
+ {(status.status === 'running' || status.status === 'completed' || status.status === 'stopped') && (
+
+
+ {/* Phase Steps */}
+
+ {SCAN_PHASES.map((phase, index) => {
+ const currentIndex = status.status === 'completed' ? 4 : status.status === 'stopped' ? getPhaseIndex(status.phase) : getPhaseIndex(status.phase)
+ const isActive = index === currentIndex
+ const isCompleted = index < currentIndex || status.status === 'completed'
+ const isStopped = status.status === 'stopped' && index > currentIndex
+
+ return (
+
+
+ {isCompleted ? :
+ isStopped ? :
+ {index + 1}}
+
+
+ {phase.label}
+
+
+ )
+ })}
+
+
+ {/* Progress Bar */}
+
+
+ {PHASE_ICONS[status.phase.toLowerCase()] || }
+ {status.phase.replace(/_/g, ' ')}
+
+
{status.progress}%
+
+
+
+
+ )}
+
+ {/* Stats */}
+
+
+
+
{status.findings_count}
+
Total Findings
+
+
+
+
+
{severityCounts.critical}
+
Critical
+
+
+
+
+
{severityCounts.high}
+
High
+
+
+
+
+
{severityCounts.medium}
+
Medium
+
+
+
+
+
{severityCounts.low}
+
Low
+
+
+
+
+
{severityCounts.info}
+
Info
+
+
+
+
+ {/* Custom Prompt Input */}
+ {status.status === 'running' && (
+
+
+
+
+
Custom AI Prompt
+
+
+ Send a custom instruction to the AI agent. Example: "Test for IDOR on /api/users/[id]" or "Check for XXE in XML endpoints"
+
+
+ setCustomPrompt(e.target.value)}
+ onKeyDown={(e) => e.key === 'Enter' && handleSubmitPrompt()}
+ placeholder="Enter custom vulnerability test prompt..."
+ className="flex-1 bg-dark-800 border border-dark-600 rounded-lg px-4 py-2 text-white placeholder-dark-400 focus:outline-none focus:border-primary-500"
+ />
+
+
+ {promptSentMessage && (
+
+ {promptSentMessage.includes('Failed') ? (
+
+ ) : (
+
+ )}
+ {promptSentMessage} - Check AI Analysis logs for response
+
+ )}
+
+
+ )}
+
+ {/* Findings */}
+
+
+ {status.findings.length === 0 ? (
+
+
+
+ {status.status === 'running' ? 'Scanning for vulnerabilities...' : 'No vulnerabilities found'}
+
+
+ ) : (
+ status.findings.map((finding) => (
+
+ {/* Finding Header */}
+
toggleFinding(finding.id)}
+ >
+
+
+ {expandedFindings.has(finding.id) ? (
+
+ ) : (
+
+ )}
+
+
{finding.title}
+
{finding.affected_endpoint}
+ {finding.parameter && (
+
+ Parameter: {finding.parameter}
+
+ )}
+
+
+
+
+ {finding.ai_verified && (
+
+
+ AI Verified
+
+ )}
+
+
+
+
+ {/* Finding Details */}
+ {expandedFindings.has(finding.id) && renderFindingDetails(finding)}
+
+ ))
+ )}
+
+
+
+ {/* Split Log Viewers */}
+
+ {/* Script Activity Log */}
+
+
+ Script Activity
+
+ {scriptLogs.length}
+
+
+ }
+ subtitle="Tool executions, HTTP requests, scanning progress"
+ >
+ {renderLogViewer(scriptLogs, scriptLogsEndRef, 'Script',
)}
+
+
+ {/* LLM Activity Log */}
+
+
+ AI Analysis
+
+ {llmLogs.length}
+
+
+ }
+ subtitle="LLM reasoning, vulnerability analysis, decisions"
+ >
+ {renderLogViewer(llmLogs, llmLogsEndRef, 'AI', )}
+
+
+
+ {/* Auto-scroll toggle */}
+
+
+
+
+ {/* Report Summary */}
+ {(status.status === 'completed' || status.status === 'stopped') && (status.report || status.findings.length > 0) && (() => {
+ // Use backend report if available, otherwise generate from findings
+ const reportData = status.report || {
+ summary: {
+ target: status.target,
+ mode: status.mode,
+ duration: status.started_at
+ ? `${Math.round((new Date(status.completed_at || new Date()).getTime() - new Date(status.started_at).getTime()) / 60000)} min`
+ : 'N/A',
+ total_findings: status.findings.length,
+ severity_breakdown: {
+ critical: status.findings.filter(f => f.severity === 'critical').length,
+ high: status.findings.filter(f => f.severity === 'high').length,
+ medium: status.findings.filter(f => f.severity === 'medium').length,
+ low: status.findings.filter(f => f.severity === 'low').length,
+ info: status.findings.filter(f => f.severity === 'info').length
+ }
+ },
+ executive_summary: status.status === 'stopped'
+ ? `Scan was stopped by user. ${status.findings.length} finding(s) discovered before stopping.`
+ : null,
+ recommendations: []
+ }
+
+ return (
+
+
+ {status.status === 'stopped' && (
+
+
+
Scan was stopped - showing partial results
+
+ )}
+
+
+
Target
+
{reportData.summary.target}
+
+
+
Mode
+
{MODE_LABELS[reportData.summary.mode] || reportData.summary.mode}
+
+
+
Duration
+
{reportData.summary.duration}
+
+
+
Total Findings
+
{reportData.summary.total_findings}
+
+
+
+ {reportData.executive_summary && (
+
+
Executive Summary
+
{reportData.executive_summary}
+
+ )}
+
+ {reportData.recommendations?.length > 0 && (
+
+
Recommendations
+
+ {reportData.recommendations.map((rec: string, i: number) => (
+ -
+
+ {rec}
+
+ ))}
+
+
+ )}
+
+
+ )
+ })()}
+
+ {/* Error Display */}
+ {status.error && (
+
+
+
+
Agent Error
+
{status.error}
+
+
+ )}
+
+ )
+}
diff --git a/frontend/src/pages/HomePage.tsx b/frontend/src/pages/HomePage.tsx
new file mode 100644
index 0000000..2be9d13
--- /dev/null
+++ b/frontend/src/pages/HomePage.tsx
@@ -0,0 +1,210 @@
+import { useEffect } from 'react'
+import { Link } from 'react-router-dom'
+import { Activity, Shield, AlertTriangle, Plus, ArrowRight } from 'lucide-react'
+import Card from '../components/common/Card'
+import Button from '../components/common/Button'
+import { SeverityBadge } from '../components/common/Badge'
+import { dashboardApi } from '../services/api'
+import { useDashboardStore } from '../store'
+
+export default function HomePage() {
+ const { stats, recentScans, recentVulnerabilities, setStats, setRecentScans, setRecentVulnerabilities, setLoading } = useDashboardStore()
+
+ useEffect(() => {
+ const fetchData = async () => {
+ setLoading(true)
+ try {
+ const [statsData, recentData] = await Promise.all([
+ dashboardApi.getStats(),
+ dashboardApi.getRecent(5)
+ ])
+ setStats(statsData)
+ setRecentScans(recentData.recent_scans)
+ setRecentVulnerabilities(recentData.recent_vulnerabilities)
+ } catch (error) {
+ console.error('Failed to fetch dashboard data:', error)
+ } finally {
+ setLoading(false)
+ }
+ }
+ fetchData()
+ }, [])
+
+ const statCards = [
+ {
+ label: 'Total Scans',
+ value: stats?.scans.total || 0,
+ icon: Activity,
+ color: 'text-blue-400',
+ bgColor: 'bg-blue-500/10',
+ },
+ {
+ label: 'Running Scans',
+ value: stats?.scans.running || 0,
+ icon: Shield,
+ color: 'text-green-400',
+ bgColor: 'bg-green-500/10',
+ },
+ {
+ label: 'Vulnerabilities',
+ value: stats?.vulnerabilities.total || 0,
+ icon: AlertTriangle,
+ color: 'text-red-400',
+ bgColor: 'bg-red-500/10',
+ },
+ {
+ label: 'Critical Issues',
+ value: stats?.vulnerabilities.critical || 0,
+ icon: AlertTriangle,
+ color: 'text-red-500',
+ bgColor: 'bg-red-600/10',
+ },
+ ]
+
+ return (
+
+ {/* Quick Actions */}
+
+
+
Welcome to NeuroSploit
+
AI-Powered Penetration Testing Platform
+
+
+
+
+
+
+ {/* Stats Grid */}
+
+ {statCards.map((stat) => (
+
+
+
+
+
+
+
{stat.value}
+
{stat.label}
+
+
+
+ ))}
+
+
+ {/* Severity Distribution */}
+ {stats && stats.vulnerabilities.total > 0 && (
+
+
+ {stats.vulnerabilities.critical > 0 && (
+
+ {stats.vulnerabilities.critical}
+
+ )}
+ {stats.vulnerabilities.high > 0 && (
+
+ {stats.vulnerabilities.high}
+
+ )}
+ {stats.vulnerabilities.medium > 0 && (
+
+ {stats.vulnerabilities.medium}
+
+ )}
+ {stats.vulnerabilities.low > 0 && (
+
+ {stats.vulnerabilities.low}
+
+ )}
+
+
+ Critical
+ High
+ Medium
+ Low
+
+
+ )}
+
+
+ {/* Recent Scans */}
+
+ View All
+
+ }
+ >
+
+ {recentScans.length === 0 ? (
+
No scans yet. Start your first scan!
+ ) : (
+ recentScans.map((scan) => (
+
+
+
{scan.name || 'Unnamed Scan'}
+
+ {new Date(scan.created_at).toLocaleDateString()}
+
+
+
+
+ {scan.total_vulnerabilities} vulns
+
+
+ ))
+ )}
+
+
+
+ {/* Recent Vulnerabilities */}
+
+ View All
+
+ }
+ >
+
+ {recentVulnerabilities.length === 0 ? (
+
No vulnerabilities found yet.
+ ) : (
+ recentVulnerabilities.slice(0, 5).map((vuln) => (
+
+
+
{vuln.title}
+
{vuln.affected_endpoint}
+
+
+
+ ))
+ )}
+
+
+
+
+ )
+}
diff --git a/frontend/src/pages/NewScanPage.tsx b/frontend/src/pages/NewScanPage.tsx
new file mode 100644
index 0000000..81e4013
--- /dev/null
+++ b/frontend/src/pages/NewScanPage.tsx
@@ -0,0 +1,572 @@
+import { useState, useRef, useEffect } from 'react'
+import { useNavigate } from 'react-router-dom'
+import {
+ Upload, Link as LinkIcon, FileText, Play, AlertTriangle,
+ Bot, Search, Target, Brain, BookOpen, ChevronDown, Key, Settings
+} from 'lucide-react'
+import Card from '../components/common/Card'
+import Button from '../components/common/Button'
+import Input from '../components/common/Input'
+import Textarea from '../components/common/Textarea'
+import { agentApi, targetsApi } from '../services/api'
+import type { AgentTask, AgentMode } from '../types'
+
+type TargetInputMode = 'single' | 'multiple' | 'file'
+
+interface OperationModeInfo {
+ id: AgentMode
+ name: string
+ icon: React.ReactNode
+ description: string
+ warning?: string
+ color: string
+}
+
+const OPERATION_MODES: OperationModeInfo[] = [
+ {
+ id: 'full_auto',
+ name: 'Full Auto',
+ icon: ,
+ description: 'Complete workflow: Recon -> Analyze -> Test -> Report',
+ color: 'primary'
+ },
+ {
+ id: 'recon_only',
+ name: 'Recon Only',
+ icon: ,
+ description: 'Reconnaissance and enumeration only, no vulnerability testing',
+ color: 'blue'
+ },
+ {
+ id: 'prompt_only',
+ name: 'AI Prompt Mode',
+ icon: ,
+ description: 'AI decides everything based on your prompt - full autonomy',
+ warning: 'HIGH TOKEN USAGE - The AI will use more API calls to decide what to do',
+ color: 'purple'
+ },
+ {
+ id: 'analyze_only',
+ name: 'Analyze Only',
+ icon: ,
+ description: 'Analyze provided data without active testing',
+ color: 'green'
+ }
+]
+
+const TASK_CATEGORIES = [
+ { id: 'all', name: 'All Tasks' },
+ { id: 'full_auto', name: 'Full Auto' },
+ { id: 'recon', name: 'Reconnaissance' },
+ { id: 'vulnerability', name: 'Vulnerability' },
+ { id: 'custom', name: 'Custom' },
+ { id: 'reporting', name: 'Reporting' }
+]
+
+export default function NewScanPage() {
+ const navigate = useNavigate()
+ const fileInputRef = useRef(null)
+
+ // Target state
+ const [targetMode, setTargetMode] = useState('single')
+ const [singleUrl, setSingleUrl] = useState('')
+ const [multipleUrls, setMultipleUrls] = useState('')
+ const [uploadedUrls, setUploadedUrls] = useState([])
+ const [urlError, setUrlError] = useState('')
+
+ // Operation mode
+ const [operationMode, setOperationMode] = useState('full_auto')
+
+ // Task library
+ const [tasks, setTasks] = useState([])
+ const [selectedTask, setSelectedTask] = useState(null)
+ const [taskCategory, setTaskCategory] = useState('all')
+ const [showTaskLibrary, setShowTaskLibrary] = useState(false)
+ const [loadingTasks, setLoadingTasks] = useState(false)
+
+ // Custom prompt
+ const [useCustomPrompt, setUseCustomPrompt] = useState(false)
+ const [customPrompt, setCustomPrompt] = useState('')
+
+ // Auth options
+ const [showAuthOptions, setShowAuthOptions] = useState(false)
+ const [authType, setAuthType] = useState<'none' | 'cookie' | 'bearer' | 'basic' | 'header'>('none')
+ const [authValue, setAuthValue] = useState('')
+
+ // Advanced options
+ const [maxDepth, setMaxDepth] = useState(5)
+
+ // UI state
+ const [isLoading, setIsLoading] = useState(false)
+
+ // Load tasks on mount
+ useEffect(() => {
+ loadTasks()
+ }, [])
+
+ const loadTasks = async (category?: string) => {
+ setLoadingTasks(true)
+ try {
+ const taskList = await agentApi.tasks.list(category === 'all' ? undefined : category)
+ setTasks(taskList)
+ } catch (error) {
+ console.error('Failed to load tasks:', error)
+ } finally {
+ setLoadingTasks(false)
+ }
+ }
+
+ const handleCategoryChange = (category: string) => {
+ setTaskCategory(category)
+ loadTasks(category)
+ }
+
+ const handleFileUpload = async (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0]
+ if (!file) return
+
+ try {
+ const result = await targetsApi.upload(file)
+ const validUrls = result.filter((r: any) => r.valid).map((r: any) => r.normalized_url)
+ setUploadedUrls(validUrls)
+ setUrlError('')
+ } catch (error) {
+ setUrlError('Failed to parse file')
+ }
+ }
+
+ const getTargetUrl = (): string => {
+ switch (targetMode) {
+ case 'single':
+ return singleUrl.trim()
+ case 'multiple':
+ return multipleUrls.split(/[,\n]/)[0]?.trim() || ''
+ case 'file':
+ return uploadedUrls[0] || ''
+ default:
+ return ''
+ }
+ }
+
+ const handleStartAgent = async () => {
+ const target = getTargetUrl()
+ if (!target) {
+ setUrlError('Please enter a target URL')
+ return
+ }
+
+ setIsLoading(true)
+ try {
+ // Validate URL
+ const validation = await targetsApi.validateBulk([target])
+ if (!validation[0]?.valid) {
+ setUrlError('Invalid URL format')
+ setIsLoading(false)
+ return
+ }
+
+ // Build request
+ const request: any = {
+ target: validation[0].normalized_url,
+ mode: operationMode,
+ max_depth: maxDepth
+ }
+
+ // Add task or custom prompt
+ if (selectedTask && !useCustomPrompt) {
+ request.task_id = selectedTask.id
+ } else if (useCustomPrompt && customPrompt.trim()) {
+ request.prompt = customPrompt
+ }
+
+ // Add auth if specified
+ if (authType !== 'none' && authValue.trim()) {
+ request.auth_type = authType
+ request.auth_value = authValue
+ }
+
+ // Start agent
+ const response = await agentApi.run(request)
+
+ // Navigate to agent status page
+ navigate(`/agent/${response.agent_id}`)
+ } catch (error) {
+ console.error('Failed to start agent:', error)
+ setUrlError('Failed to start agent. Please try again.')
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ const currentModeInfo = OPERATION_MODES.find(m => m.id === operationMode)!
+
+ return (
+
+ {/* Header */}
+
+
+
+
+ AI Security Agent
+
+
Autonomous penetration testing powered by AI
+
+
+
+ {/* Operation Mode Selector */}
+
+
+ {OPERATION_MODES.map((mode) => (
+
setOperationMode(mode.id)}
+ className={`p-4 rounded-xl border-2 cursor-pointer transition-all ${
+ operationMode === mode.id
+ ? `border-${mode.color}-500 bg-${mode.color}-500/10`
+ : 'border-dark-700 hover:border-dark-500 bg-dark-900/50'
+ }`}
+ >
+
+ {mode.icon}
+ {mode.name}
+
+
{mode.description}
+ {mode.warning && operationMode === mode.id && (
+
+ )}
+
+ ))}
+
+
+
+ {/* Target Input */}
+
+
+ {/* Mode Selector */}
+
+
+
+
+
+
+ {/* Input Fields */}
+ {targetMode === 'single' && (
+
{
+ setSingleUrl(e.target.value)
+ setUrlError('')
+ }}
+ error={urlError}
+ />
+ )}
+
+ {targetMode === 'multiple' && (
+
+ )}
+
+ {targetMode === 'file' && (
+
+
+
fileInputRef.current?.click()}
+ className="border-2 border-dashed border-dark-700 rounded-lg p-8 text-center cursor-pointer hover:border-primary-500 transition-colors"
+ >
+
+
Click to upload a file with URLs
+
Supports .txt, .csv, .lst files
+
+ {uploadedUrls.length > 0 && (
+
+ {uploadedUrls.length} valid URLs loaded - using first URL
+
+ )}
+ {urlError &&
{urlError}
}
+
+ )}
+
+
+
+ {/* Task Library */}
+
+
+
+ Task Library
+
+
+
+ }
+ subtitle="Select a preset task or create a custom prompt"
+ >
+ {/* Custom Prompt Toggle */}
+
+
+ setUseCustomPrompt(e.target.checked)}
+ className="w-4 h-4 rounded border-dark-600 bg-dark-800 text-primary-500 focus:ring-primary-500"
+ />
+
+
+
+
+ {useCustomPrompt ? (
+