NeuroSploit v3.2 - Autonomous AI Penetration Testing Platform

116 modules | 100 vuln types | 18 API routes | 18 frontend pages

Major features:
- VulnEngine: 100 vuln types, 526+ payloads, 12 testers, anti-hallucination prompts
- Autonomous Agent: 3-stream auto pentest, multi-session (5 concurrent), pause/resume/stop
- CLI Agent: Claude Code / Gemini CLI / Codex CLI inside Kali containers
- Validation Pipeline: negative controls, proof of execution, confidence scoring, judge
- AI Reasoning: ReACT engine, token budget, endpoint classifier, CVE hunter, deep recon
- Multi-Agent: 5 specialists + orchestrator + researcher AI + vuln type agents
- RAG System: BM25/TF-IDF/ChromaDB vectorstore, few-shot, reasoning templates
- Smart Router: 20 providers (8 CLI OAuth + 12 API), tier failover, token refresh
- Kali Sandbox: container-per-scan, 56 tools, VPN support, on-demand install
- Full IA Testing: methodology-driven comprehensive pentest sessions
- Notifications: Discord, Telegram, WhatsApp/Twilio multi-channel alerts
- Frontend: React/TypeScript with 18 pages, real-time WebSocket updates
This commit is contained in:
CyberSecurityUP
2026-02-22 17:58:12 -03:00
commit e0935793c5
271 changed files with 132462 additions and 0 deletions

20
backend/models/__init__.py Executable file
View File

@@ -0,0 +1,20 @@
from backend.models.scan import Scan
from backend.models.target import Target
from backend.models.prompt import Prompt
from backend.models.endpoint import Endpoint
from backend.models.vulnerability import Vulnerability, VulnerabilityTest
from backend.models.report import Report
from backend.models.agent_task import AgentTask
from backend.models.vuln_lab import VulnLabChallenge
__all__ = [
"Scan",
"Target",
"Prompt",
"Endpoint",
"Vulnerability",
"VulnerabilityTest",
"Report",
"AgentTask",
"VulnLabChallenge"
]

94
backend/models/agent_task.py Executable file
View File

@@ -0,0 +1,94 @@
"""
NeuroSploit v3 - Agent Task Model
Tracks all agent activities during scans for dashboard visibility.
"""
from datetime import datetime
from typing import Optional
from sqlalchemy import String, Integer, DateTime, Text, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from backend.db.database import Base
import uuid
class AgentTask(Base):
"""Agent task record for tracking scan activities"""
__tablename__ = "agent_tasks"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
scan_id: Mapped[str] = mapped_column(String(36), ForeignKey("scans.id", ondelete="CASCADE"))
# Task identification
task_type: Mapped[str] = mapped_column(String(50)) # recon, analysis, testing, reporting
task_name: Mapped[str] = mapped_column(String(255)) # Human-readable name
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Tool information
tool_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) # nmap, nuclei, claude, httpx, etc.
tool_category: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) # scanner, analyzer, ai, crawler
# Status tracking
status: Mapped[str] = mapped_column(String(20), default="pending") # pending, running, completed, failed, cancelled
# Timing
started_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
duration_ms: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) # Duration in milliseconds
# Results
items_processed: Mapped[int] = mapped_column(Integer, default=0) # URLs tested, hosts scanned, etc.
items_found: Mapped[int] = mapped_column(Integer, default=0) # Endpoints found, vulns found, etc.
result_summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # Brief summary of results
# Error handling
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Metadata
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
# Relationships
scan: Mapped["Scan"] = relationship("Scan", back_populates="agent_tasks")
def to_dict(self) -> dict:
"""Convert to dictionary"""
return {
"id": self.id,
"scan_id": self.scan_id,
"task_type": self.task_type,
"task_name": self.task_name,
"description": self.description,
"tool_name": self.tool_name,
"tool_category": self.tool_category,
"status": self.status,
"started_at": self.started_at.isoformat() if self.started_at else None,
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
"duration_ms": self.duration_ms,
"items_processed": self.items_processed,
"items_found": self.items_found,
"result_summary": self.result_summary,
"error_message": self.error_message,
"created_at": self.created_at.isoformat() if self.created_at else None
}
def start(self):
"""Mark task as started"""
self.status = "running"
self.started_at = datetime.utcnow()
def complete(self, items_processed: int = 0, items_found: int = 0, summary: str = None):
"""Mark task as completed"""
self.status = "completed"
self.completed_at = datetime.utcnow()
self.items_processed = items_processed
self.items_found = items_found
self.result_summary = summary
if self.started_at:
self.duration_ms = int((self.completed_at - self.started_at).total_seconds() * 1000)
def fail(self, error: str):
"""Mark task as failed"""
self.status = "failed"
self.completed_at = datetime.utcnow()
self.error_message = error
if self.started_at:
self.duration_ms = int((self.completed_at - self.started_at).total_seconds() * 1000)

61
backend/models/endpoint.py Executable file
View File

@@ -0,0 +1,61 @@
"""
NeuroSploit v3 - Endpoint Model
"""
from datetime import datetime
from typing import Optional, List
from sqlalchemy import String, Integer, DateTime, Text, JSON, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from backend.db.database import Base
import uuid
class Endpoint(Base):
"""Discovered endpoint model"""
__tablename__ = "endpoints"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
scan_id: Mapped[str] = mapped_column(String(36), ForeignKey("scans.id", ondelete="CASCADE"))
target_id: Mapped[Optional[str]] = mapped_column(String(36), ForeignKey("targets.id", ondelete="SET NULL"), nullable=True)
# Endpoint details
url: Mapped[str] = mapped_column(Text)
method: Mapped[str] = mapped_column(String(10), default="GET")
path: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Parameters
parameters: Mapped[List] = mapped_column(JSON, default=list) # [{name, type, value}]
headers: Mapped[dict] = mapped_column(JSON, default=dict)
# Response info
response_status: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
content_type: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
content_length: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
# Detection
technologies: Mapped[List] = mapped_column(JSON, default=list)
interesting: Mapped[bool] = mapped_column(default=False) # Marked as interesting for testing
# Timestamps
discovered_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
# Relationships
scan: Mapped["Scan"] = relationship("Scan", back_populates="endpoints")
def to_dict(self) -> dict:
"""Convert to dictionary"""
return {
"id": self.id,
"scan_id": self.scan_id,
"target_id": self.target_id,
"url": self.url,
"method": self.method,
"path": self.path,
"parameters": self.parameters,
"headers": self.headers,
"response_status": self.response_status,
"content_type": self.content_type,
"content_length": self.content_length,
"technologies": self.technologies,
"interesting": self.interesting,
"discovered_at": self.discovered_at.isoformat() if self.discovered_at else None
}

44
backend/models/prompt.py Executable file
View File

@@ -0,0 +1,44 @@
"""
NeuroSploit v3 - Prompt Model
"""
from datetime import datetime
from typing import Optional, List
from sqlalchemy import String, Boolean, DateTime, Text, JSON
from sqlalchemy.orm import Mapped, mapped_column
from backend.db.database import Base
import uuid
class Prompt(Base):
"""Prompt model for storing custom and preset prompts"""
__tablename__ = "prompts"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
name: Mapped[str] = mapped_column(String(255))
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
content: Mapped[str] = mapped_column(Text)
# Categorization
is_preset: Mapped[bool] = mapped_column(Boolean, default=False)
category: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) # pentest, bug_bounty, api, etc.
# Parsed vulnerabilities (extracted by AI)
parsed_vulnerabilities: Mapped[List] = mapped_column(JSON, default=list)
# Timestamps
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
def to_dict(self) -> dict:
"""Convert to dictionary"""
return {
"id": self.id,
"name": self.name,
"description": self.description,
"content": self.content,
"is_preset": self.is_preset,
"category": self.category,
"parsed_vulnerabilities": self.parsed_vulnerabilities,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None
}

49
backend/models/report.py Executable file
View File

@@ -0,0 +1,49 @@
"""
NeuroSploit v3 - Report Model
"""
from datetime import datetime
from typing import Optional
from sqlalchemy import String, DateTime, Text, ForeignKey, Boolean
from sqlalchemy.orm import Mapped, mapped_column, relationship
from backend.db.database import Base
import uuid
class Report(Base):
"""Report model"""
__tablename__ = "reports"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
scan_id: Mapped[str] = mapped_column(String(36), ForeignKey("scans.id", ondelete="CASCADE"))
# Report details
title: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
format: Mapped[str] = mapped_column(String(20), default="html") # html, pdf, json
file_path: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Content
executive_summary: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Auto-generation flags
auto_generated: Mapped[bool] = mapped_column(Boolean, default=False) # True if auto-generated on scan completion/stop
is_partial: Mapped[bool] = mapped_column(Boolean, default=False) # True if generated from stopped/incomplete scan
# Timestamps
generated_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
# Relationship
scan: Mapped["Scan"] = relationship("Scan", back_populates="reports")
def to_dict(self) -> dict:
"""Convert to dictionary"""
return {
"id": self.id,
"scan_id": self.scan_id,
"title": self.title,
"format": self.format,
"file_path": self.file_path,
"executive_summary": self.executive_summary,
"auto_generated": self.auto_generated,
"is_partial": self.is_partial,
"generated_at": self.generated_at.isoformat() if self.generated_at else None
}

91
backend/models/scan.py Executable file
View File

@@ -0,0 +1,91 @@
"""
NeuroSploit v3 - Scan Model
"""
from datetime import datetime
from typing import Optional, List
from sqlalchemy import String, Integer, Boolean, DateTime, Text, JSON
from sqlalchemy.orm import Mapped, mapped_column, relationship
from backend.db.database import Base
import uuid
class Scan(Base):
"""Scan model representing a penetration test scan"""
__tablename__ = "scans"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
status: Mapped[str] = mapped_column(String(50), default="pending") # pending, running, completed, failed, stopped
scan_type: Mapped[str] = mapped_column(String(50), default="full") # quick, full, custom
recon_enabled: Mapped[bool] = mapped_column(Boolean, default=True)
# Progress tracking
progress: Mapped[int] = mapped_column(Integer, default=0)
current_phase: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) # recon, testing, reporting
# Configuration
config: Mapped[dict] = mapped_column(JSON, default=dict)
# Custom prompt (if any)
custom_prompt: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
prompt_id: Mapped[Optional[str]] = mapped_column(String(36), nullable=True)
# Authentication for testing (IMPORTANT: Use responsibly with authorization)
auth_type: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) # none, cookie, header, basic, bearer
auth_credentials: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) # Stores auth data securely
custom_headers: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True) # Additional HTTP headers
# Timestamps
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
started_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
duration: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) # Duration in seconds
# Error handling
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Statistics (updated during scan)
total_endpoints: Mapped[int] = mapped_column(Integer, default=0)
total_vulnerabilities: Mapped[int] = mapped_column(Integer, default=0)
critical_count: Mapped[int] = mapped_column(Integer, default=0)
high_count: Mapped[int] = mapped_column(Integer, default=0)
medium_count: Mapped[int] = mapped_column(Integer, default=0)
low_count: Mapped[int] = mapped_column(Integer, default=0)
info_count: Mapped[int] = mapped_column(Integer, default=0)
# Relationships
targets: Mapped[List["Target"]] = relationship("Target", back_populates="scan", cascade="all, delete-orphan")
endpoints: Mapped[List["Endpoint"]] = relationship("Endpoint", back_populates="scan", cascade="all, delete-orphan")
vulnerabilities: Mapped[List["Vulnerability"]] = relationship("Vulnerability", back_populates="scan", cascade="all, delete-orphan")
reports: Mapped[List["Report"]] = relationship("Report", back_populates="scan", cascade="all, delete-orphan")
agent_tasks: Mapped[List["AgentTask"]] = relationship("AgentTask", back_populates="scan", cascade="all, delete-orphan")
def to_dict(self) -> dict:
"""Convert to dictionary"""
return {
"id": self.id,
"name": self.name,
"status": self.status,
"scan_type": self.scan_type,
"recon_enabled": self.recon_enabled,
"progress": self.progress,
"current_phase": self.current_phase,
"config": self.config,
"custom_prompt": self.custom_prompt,
"prompt_id": self.prompt_id,
"auth_type": self.auth_type,
"auth_credentials": self.auth_credentials, # Careful: may contain sensitive data
"custom_headers": self.custom_headers,
"created_at": self.created_at.isoformat() if self.created_at else None,
"started_at": self.started_at.isoformat() if self.started_at else None,
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
"duration": self.duration,
"error_message": self.error_message,
"total_endpoints": self.total_endpoints,
"total_vulnerabilities": self.total_vulnerabilities,
"critical_count": self.critical_count,
"high_count": self.high_count,
"medium_count": self.medium_count,
"low_count": self.low_count,
"info_count": self.info_count
}

47
backend/models/target.py Executable file
View File

@@ -0,0 +1,47 @@
"""
NeuroSploit v3 - Target Model
"""
from datetime import datetime
from typing import Optional
from sqlalchemy import String, Integer, DateTime, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from backend.db.database import Base
import uuid
class Target(Base):
"""Target URL model"""
__tablename__ = "targets"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
scan_id: Mapped[str] = mapped_column(String(36), ForeignKey("scans.id", ondelete="CASCADE"))
# URL details
url: Mapped[str] = mapped_column(String(2048))
hostname: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
port: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
protocol: Mapped[Optional[str]] = mapped_column(String(10), nullable=True)
path: Mapped[Optional[str]] = mapped_column(String(2048), nullable=True)
# Status
status: Mapped[str] = mapped_column(String(50), default="pending") # pending, scanning, completed, failed
# Timestamps
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
# Relationship
scan: Mapped["Scan"] = relationship("Scan", back_populates="targets")
def to_dict(self) -> dict:
"""Convert to dictionary"""
return {
"id": self.id,
"scan_id": self.scan_id,
"url": self.url,
"hostname": self.hostname,
"port": self.port,
"protocol": self.protocol,
"path": self.path,
"status": self.status,
"created_at": self.created_at.isoformat() if self.created_at else None
}

94
backend/models/vuln_lab.py Executable file
View File

@@ -0,0 +1,94 @@
"""
NeuroSploit v3 - Vulnerability Lab Challenge Model
Tracks isolated vulnerability testing sessions (labs, CTFs, PortSwigger, etc.)
"""
from datetime import datetime
from typing import Optional, List
from sqlalchemy import String, Integer, Float, Boolean, DateTime, Text, JSON, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from backend.db.database import Base
import uuid
class VulnLabChallenge(Base):
"""Individual vulnerability lab/challenge test record"""
__tablename__ = "vuln_lab_challenges"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
# Target info
target_url: Mapped[str] = mapped_column(Text)
challenge_name: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
# Vulnerability scope
vuln_type: Mapped[str] = mapped_column(String(100)) # e.g. xss_reflected, sqli_union
vuln_category: Mapped[Optional[str]] = mapped_column(String(50), nullable=True) # injection, auth, client_side, etc.
# Authentication
auth_type: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) # cookie, bearer, basic, header
auth_value: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Execution state
status: Mapped[str] = mapped_column(String(20), default="pending") # pending, running, completed, failed, stopped
result: Mapped[Optional[str]] = mapped_column(String(20), nullable=True) # detected, not_detected, error
# Agent linkage
agent_id: Mapped[Optional[str]] = mapped_column(String(36), nullable=True)
scan_id: Mapped[Optional[str]] = mapped_column(String(36), nullable=True)
# Results
findings_count: Mapped[int] = mapped_column(Integer, default=0)
critical_count: Mapped[int] = mapped_column(Integer, default=0)
high_count: Mapped[int] = mapped_column(Integer, default=0)
medium_count: Mapped[int] = mapped_column(Integer, default=0)
low_count: Mapped[int] = mapped_column(Integer, default=0)
info_count: Mapped[int] = mapped_column(Integer, default=0)
# Findings detail (JSON list of finding summaries)
findings_detail: Mapped[List] = mapped_column(JSON, default=list)
# Timing
started_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
completed_at: Mapped[Optional[datetime]] = mapped_column(DateTime, nullable=True)
duration: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) # seconds
# Notes
notes: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Logs (JSON list of log entries persisted after completion)
logs: Mapped[List] = mapped_column(JSON, default=list)
# Endpoints discovered count
endpoints_count: Mapped[int] = mapped_column(Integer, default=0)
# Timestamps
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
def to_dict(self) -> dict:
return {
"id": self.id,
"target_url": self.target_url,
"challenge_name": self.challenge_name,
"vuln_type": self.vuln_type,
"vuln_category": self.vuln_category,
"auth_type": self.auth_type,
"status": self.status,
"result": self.result,
"agent_id": self.agent_id,
"scan_id": self.scan_id,
"findings_count": self.findings_count,
"critical_count": self.critical_count,
"high_count": self.high_count,
"medium_count": self.medium_count,
"low_count": self.low_count,
"info_count": self.info_count,
"findings_detail": self.findings_detail or [],
"started_at": self.started_at.isoformat() if self.started_at else None,
"completed_at": self.completed_at.isoformat() if self.completed_at else None,
"duration": self.duration,
"notes": self.notes,
"logs": self.logs or [],
"endpoints_count": self.endpoints_count,
"created_at": self.created_at.isoformat() if self.created_at else None,
}

149
backend/models/vulnerability.py Executable file
View File

@@ -0,0 +1,149 @@
"""
NeuroSploit v3 - Vulnerability Models
"""
from datetime import datetime
from typing import Optional, List
from sqlalchemy import String, Integer, Float, Boolean, DateTime, Text, JSON, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from backend.db.database import Base
import uuid
class VulnerabilityTest(Base):
"""Individual vulnerability test record"""
__tablename__ = "vulnerability_tests"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
scan_id: Mapped[str] = mapped_column(String(36), ForeignKey("scans.id", ondelete="CASCADE"))
endpoint_id: Mapped[Optional[str]] = mapped_column(String(36), ForeignKey("endpoints.id", ondelete="SET NULL"), nullable=True)
# Test details
vulnerability_type: Mapped[str] = mapped_column(String(100)) # xss_reflected, sqli_union, etc.
payload: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Request/Response
request_data: Mapped[dict] = mapped_column(JSON, default=dict)
response_data: Mapped[dict] = mapped_column(JSON, default=dict)
# Result
is_vulnerable: Mapped[bool] = mapped_column(Boolean, default=False)
confidence: Mapped[Optional[float]] = mapped_column(Float, nullable=True) # 0.0 to 1.0
evidence: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Timestamps
tested_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
def to_dict(self) -> dict:
"""Convert to dictionary"""
return {
"id": self.id,
"scan_id": self.scan_id,
"endpoint_id": self.endpoint_id,
"vulnerability_type": self.vulnerability_type,
"payload": self.payload,
"request_data": self.request_data,
"response_data": self.response_data,
"is_vulnerable": self.is_vulnerable,
"confidence": self.confidence,
"evidence": self.evidence,
"tested_at": self.tested_at.isoformat() if self.tested_at else None
}
class Vulnerability(Base):
"""Confirmed vulnerability model"""
__tablename__ = "vulnerabilities"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
scan_id: Mapped[str] = mapped_column(String(36), ForeignKey("scans.id", ondelete="CASCADE"))
test_id: Mapped[Optional[str]] = mapped_column(String(36), ForeignKey("vulnerability_tests.id", ondelete="SET NULL"), nullable=True)
# Vulnerability details
title: Mapped[str] = mapped_column(String(500))
vulnerability_type: Mapped[str] = mapped_column(String(100))
severity: Mapped[str] = mapped_column(String(20)) # critical, high, medium, low, info
# Scoring
cvss_score: Mapped[Optional[float]] = mapped_column(Float, nullable=True)
cvss_vector: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
cwe_id: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
# Details
description: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
affected_endpoint: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Proof of Concept
poc_request: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
poc_response: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
poc_payload: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
poc_parameter: Mapped[Optional[str]] = mapped_column(String(500), nullable=True) # Vulnerable parameter
poc_evidence: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # Evidence of vulnerability
# Remediation
impact: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
remediation: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
references: Mapped[List] = mapped_column(JSON, default=list)
# AI Analysis
ai_analysis: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# PoC Code (executable proof-of-concept: HTML, Python, curl, etc.)
poc_code: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Screenshots (list of base64 data URIs or filesystem paths)
screenshots: Mapped[List] = mapped_column(JSON, default=list)
# Source URL and parameter (for finding_id reconstruction)
url: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
parameter: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
# Confidence & Proof (from ValidationJudge pipeline)
confidence_score: Mapped[Optional[int]] = mapped_column(Integer, nullable=True) # 0-100
confidence_breakdown: Mapped[dict] = mapped_column(JSON, default=dict) # {proof: X, impact: Y, controls: Z}
proof_of_execution: Mapped[Optional[str]] = mapped_column(Text, nullable=True) # Proof type + detail
# Validation status (manual review workflow)
validation_status: Mapped[str] = mapped_column(String(20), default="ai_confirmed")
# Values: "ai_confirmed" | "ai_rejected" | "validated" | "false_positive" | "pending_review"
ai_rejection_reason: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
# Timestamps
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
# Relationships
scan: Mapped["Scan"] = relationship("Scan", back_populates="vulnerabilities")
def to_dict(self) -> dict:
"""Convert to dictionary"""
return {
"id": self.id,
"scan_id": self.scan_id,
"test_id": self.test_id,
"title": self.title,
"vulnerability_type": self.vulnerability_type,
"severity": self.severity,
"cvss_score": self.cvss_score,
"cvss_vector": self.cvss_vector,
"cwe_id": self.cwe_id,
"description": self.description,
"affected_endpoint": self.affected_endpoint,
"poc_request": self.poc_request,
"poc_response": self.poc_response,
"poc_payload": self.poc_payload,
"poc_parameter": self.poc_parameter,
"poc_evidence": self.poc_evidence,
"impact": self.impact,
"remediation": self.remediation,
"references": self.references,
"ai_analysis": self.ai_analysis,
"poc_code": self.poc_code,
"screenshots": self.screenshots or [],
"url": self.url,
"parameter": self.parameter,
"confidence_score": self.confidence_score,
"confidence_breakdown": self.confidence_breakdown or {},
"proof_of_execution": self.proof_of_execution,
"validation_status": self.validation_status or "ai_confirmed",
"ai_rejection_reason": self.ai_rejection_reason,
"created_at": self.created_at.isoformat() if self.created_at else None
}