mirror of
https://github.com/CyberSecurityUP/NeuroSploit.git
synced 2026-06-30 07:15:30 +02:00
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:
Executable
+209
@@ -0,0 +1,209 @@
|
||||
"""
|
||||
NeuroSploit v3 - Container Pool
|
||||
|
||||
Global coordinator for per-scan Kali Linux containers.
|
||||
Tracks all running sandbox containers, enforces max concurrent limits,
|
||||
handles lifecycle management and orphan cleanup.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import docker
|
||||
from docker.errors import NotFound
|
||||
HAS_DOCKER = True
|
||||
except ImportError:
|
||||
HAS_DOCKER = False
|
||||
|
||||
from core.kali_sandbox import KaliSandbox
|
||||
|
||||
|
||||
class ContainerPool:
|
||||
"""Global pool managing per-scan KaliSandbox instances.
|
||||
|
||||
Thread-safe. One pool per process. Enforces resource limits.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
image: str = "neurosploit-kali:latest",
|
||||
max_concurrent: int = 5,
|
||||
memory_limit: str = "2g",
|
||||
cpu_limit: float = 2.0,
|
||||
container_ttl_minutes: int = 60,
|
||||
):
|
||||
self.image = image
|
||||
self.max_concurrent = max_concurrent
|
||||
self.memory_limit = memory_limit
|
||||
self.cpu_limit = cpu_limit
|
||||
self.container_ttl = timedelta(minutes=container_ttl_minutes)
|
||||
|
||||
self._sandboxes: Dict[str, KaliSandbox] = {}
|
||||
self._lock = asyncio.Lock()
|
||||
|
||||
@classmethod
|
||||
def from_config(cls) -> "ContainerPool":
|
||||
"""Create pool from config/config.json sandbox section."""
|
||||
try:
|
||||
with open("config/config.json") as f:
|
||||
cfg = json.load(f)
|
||||
sandbox_cfg = cfg.get("sandbox", {})
|
||||
kali_cfg = sandbox_cfg.get("kali", {})
|
||||
resources = sandbox_cfg.get("resources", {})
|
||||
|
||||
return cls(
|
||||
image=kali_cfg.get("image", "neurosploit-kali:latest"),
|
||||
max_concurrent=kali_cfg.get("max_concurrent", 5),
|
||||
memory_limit=resources.get("memory_limit", "2g"),
|
||||
cpu_limit=resources.get("cpu_limit", 2.0),
|
||||
container_ttl_minutes=kali_cfg.get("container_ttl_minutes", 60),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not load pool config, using defaults: {e}")
|
||||
return cls()
|
||||
|
||||
async def get_or_create(
|
||||
self, scan_id: str, enable_vpn: bool = False,
|
||||
) -> KaliSandbox:
|
||||
"""Get existing sandbox for scan_id, or create a new one.
|
||||
|
||||
Raises RuntimeError if max_concurrent limit reached.
|
||||
"""
|
||||
async with self._lock:
|
||||
# Return existing
|
||||
if scan_id in self._sandboxes:
|
||||
sb = self._sandboxes[scan_id]
|
||||
if sb.is_available:
|
||||
return sb
|
||||
else:
|
||||
del self._sandboxes[scan_id]
|
||||
|
||||
# Check limit
|
||||
active = sum(1 for sb in self._sandboxes.values() if sb.is_available)
|
||||
if active >= self.max_concurrent:
|
||||
raise RuntimeError(
|
||||
f"Max concurrent containers ({self.max_concurrent}) reached. "
|
||||
f"Active scans: {list(self._sandboxes.keys())}"
|
||||
)
|
||||
|
||||
# Create new
|
||||
sb = KaliSandbox(
|
||||
scan_id=scan_id,
|
||||
image=self.image,
|
||||
memory_limit=self.memory_limit,
|
||||
cpu_limit=self.cpu_limit,
|
||||
enable_vpn=enable_vpn,
|
||||
)
|
||||
ok, msg = await sb.initialize()
|
||||
if not ok:
|
||||
raise RuntimeError(f"Failed to create Kali sandbox: {msg}")
|
||||
|
||||
self._sandboxes[scan_id] = sb
|
||||
logger.info(
|
||||
f"Pool: created container for scan {scan_id} "
|
||||
f"({active + 1}/{self.max_concurrent} active)"
|
||||
)
|
||||
return sb
|
||||
|
||||
async def destroy(self, scan_id: str):
|
||||
"""Stop and remove the container for a specific scan."""
|
||||
async with self._lock:
|
||||
sb = self._sandboxes.pop(scan_id, None)
|
||||
if sb:
|
||||
await sb.stop()
|
||||
logger.info(f"Pool: destroyed container for scan {scan_id}")
|
||||
|
||||
async def cleanup_all(self):
|
||||
"""Destroy all managed containers (shutdown hook)."""
|
||||
async with self._lock:
|
||||
scan_ids = list(self._sandboxes.keys())
|
||||
for sid in scan_ids:
|
||||
await self.destroy(sid)
|
||||
logger.info("Pool: all containers destroyed")
|
||||
|
||||
async def cleanup_orphans(self):
|
||||
"""Find and remove neurosploit-* containers not tracked by this pool."""
|
||||
if not HAS_DOCKER:
|
||||
return
|
||||
|
||||
try:
|
||||
client = docker.from_env()
|
||||
containers = client.containers.list(
|
||||
all=True,
|
||||
filters={"label": "neurosploit.type=kali-sandbox"},
|
||||
)
|
||||
async with self._lock:
|
||||
tracked = set(self._sandboxes.keys())
|
||||
|
||||
removed = 0
|
||||
for c in containers:
|
||||
scan_id = c.labels.get("neurosploit.scan_id", "")
|
||||
if scan_id not in tracked:
|
||||
try:
|
||||
c.stop(timeout=5)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
c.remove(force=True)
|
||||
removed += 1
|
||||
logger.info(f"Pool: removed orphan container {c.name}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if removed:
|
||||
logger.info(f"Pool: cleaned up {removed} orphan containers")
|
||||
except Exception as e:
|
||||
logger.warning(f"Pool: orphan cleanup failed: {e}")
|
||||
|
||||
async def cleanup_expired(self):
|
||||
"""Remove containers that have exceeded their TTL."""
|
||||
now = datetime.utcnow()
|
||||
async with self._lock:
|
||||
expired = [
|
||||
sid for sid, sb in self._sandboxes.items()
|
||||
if sb._created_at and (now - sb._created_at) > self.container_ttl
|
||||
]
|
||||
for sid in expired:
|
||||
logger.warning(f"Pool: container for scan {sid} exceeded TTL, destroying")
|
||||
await self.destroy(sid)
|
||||
|
||||
def list_sandboxes(self) -> Dict[str, Dict]:
|
||||
"""List all tracked sandboxes with status."""
|
||||
result = {}
|
||||
for sid, sb in self._sandboxes.items():
|
||||
result[sid] = {
|
||||
"scan_id": sid,
|
||||
"container_name": sb.container_name,
|
||||
"available": sb.is_available,
|
||||
"installed_tools": sorted(sb._installed_tools),
|
||||
"created_at": sb._created_at.isoformat() if sb._created_at else None,
|
||||
}
|
||||
return result
|
||||
|
||||
@property
|
||||
def active_count(self) -> int:
|
||||
return sum(1 for sb in self._sandboxes.values() if sb.is_available)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Global singleton pool
|
||||
# ---------------------------------------------------------------------------
|
||||
_pool: Optional[ContainerPool] = None
|
||||
_pool_lock = threading.Lock()
|
||||
|
||||
|
||||
def get_pool() -> ContainerPool:
|
||||
"""Get or create the global container pool."""
|
||||
global _pool
|
||||
if _pool is None:
|
||||
with _pool_lock:
|
||||
if _pool is None:
|
||||
_pool = ContainerPool.from_config()
|
||||
return _pool
|
||||
Reference in New Issue
Block a user