feat: Add HackGpt Enterprise features

- 6-Phase pentest methodology UI (Recon, Scanning, Vuln, Exploit, Report, Retest)
- Phase-aware AI prompts with context from current phase
- Attack chain analysis and visualization
- CVSS-style severity badges (CRITICAL/HIGH/MEDIUM/LOW)
- Findings sidebar with severity counts
- Phase-specific tools and quick actions
This commit is contained in:
2025-11-28 10:54:25 -05:00
parent 8b89e27b68
commit b9428df6df
32 changed files with 4641 additions and 1 deletions

View File

@@ -0,0 +1,4 @@
"""
StrikePackageGPT Shared Library
Common models, utilities, and constants used across services.
"""

158
services/shared/models.py Normal file
View File

@@ -0,0 +1,158 @@
"""
Shared Pydantic models for StrikePackageGPT services.
"""
from pydantic import BaseModel, Field
from typing import Optional, Literal, List, Dict, Any
from datetime import datetime
from enum import Enum
class TaskState(str, Enum):
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
class ToolCategory(str, Enum):
RECON = "reconnaissance"
VULN_SCAN = "vulnerability_scanning"
EXPLOITATION = "exploitation"
WEB_TESTING = "web_testing"
PASSWORD = "password_attacks"
WIRELESS = "wireless"
FORENSICS = "forensics"
# ============== Chat Models ==============
class ChatMessage(BaseModel):
role: Literal["system", "user", "assistant"]
content: str
timestamp: Optional[datetime] = None
class ChatRequest(BaseModel):
message: str
session_id: Optional[str] = None
context: Optional[str] = None
provider: str = "ollama"
model: str = "llama3.2"
temperature: float = 0.7
max_tokens: int = 2048
class ChatResponse(BaseModel):
provider: str
model: str
content: str
usage: Optional[Dict[str, int]] = None
session_id: Optional[str] = None
# ============== Command Execution Models ==============
class CommandRequest(BaseModel):
command: str
timeout: int = Field(default=300, ge=1, le=3600)
working_dir: Optional[str] = "/workspace"
env: Optional[Dict[str, str]] = None
class CommandResult(BaseModel):
command: str
exit_code: int
stdout: str
stderr: str
duration_seconds: float
timed_out: bool = False
# ============== Task Models ==============
class Task(BaseModel):
task_id: str
task_type: str
status: TaskState = TaskState.PENDING
created_at: datetime = Field(default_factory=datetime.utcnow)
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
result: Optional[Any] = None
error: Optional[str] = None
progress: int = Field(default=0, ge=0, le=100)
metadata: Dict[str, Any] = Field(default_factory=dict)
# ============== Security Tool Models ==============
class SecurityTool(BaseModel):
name: str
description: str
category: ToolCategory
command_template: str
required_args: List[str] = []
optional_args: List[str] = []
output_parser: Optional[str] = None
class ScanTarget(BaseModel):
target: str # IP, hostname, URL, or CIDR
target_type: Literal["ip", "hostname", "url", "cidr", "auto"] = "auto"
ports: Optional[str] = None # e.g., "22,80,443" or "1-1000"
options: Dict[str, Any] = Field(default_factory=dict)
class ScanRequest(BaseModel):
target: ScanTarget
tool: str
scan_type: Optional[str] = None
options: Dict[str, Any] = Field(default_factory=dict)
class ScanResult(BaseModel):
scan_id: str
tool: str
target: str
status: TaskState
started_at: datetime
completed_at: Optional[datetime] = None
raw_output: Optional[str] = None
parsed_results: Optional[Dict[str, Any]] = None
findings: List[Dict[str, Any]] = []
# ============== Session Models ==============
class Session(BaseModel):
session_id: str
created_at: datetime = Field(default_factory=datetime.utcnow)
last_activity: datetime = Field(default_factory=datetime.utcnow)
messages: List[ChatMessage] = []
context: Dict[str, Any] = Field(default_factory=dict)
active_scans: List[str] = []
# ============== Finding Models ==============
class Severity(str, Enum):
CRITICAL = "critical"
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
INFO = "info"
class Finding(BaseModel):
finding_id: str
title: str
description: str
severity: Severity
category: str
target: str
evidence: Optional[str] = None
remediation: Optional[str] = None
references: List[str] = []
cve_ids: List[str] = []
detected_at: datetime = Field(default_factory=datetime.utcnow)
tool: Optional[str] = None

315
services/shared/parsers.py Normal file
View File

@@ -0,0 +1,315 @@
"""
Output parsers for security tool results.
Converts raw tool output into structured data.
"""
import re
import json
import xml.etree.ElementTree as ET
from typing import Dict, Any, List, Optional
from datetime import datetime
class BaseParser:
"""Base class for tool output parsers."""
def parse(self, output: str) -> Dict[str, Any]:
raise NotImplementedError
class NmapParser(BaseParser):
"""Parser for nmap output."""
def parse(self, output: str) -> Dict[str, Any]:
"""Parse nmap text output."""
results = {
"hosts": [],
"scan_info": {},
"raw": output
}
current_host = None
for line in output.split('\n'):
line = line.strip()
# Parse scan info
if line.startswith('Nmap scan report for'):
if current_host:
results["hosts"].append(current_host)
# Extract hostname and IP
match = re.search(r'for (\S+)(?: \((\d+\.\d+\.\d+\.\d+)\))?', line)
if match:
current_host = {
"hostname": match.group(1),
"ip": match.group(2) or match.group(1),
"ports": [],
"os": None,
"status": "up"
}
# Parse port info
elif current_host and re.match(r'^\d+/(tcp|udp)', line):
parts = line.split()
if len(parts) >= 3:
port_proto = parts[0].split('/')
current_host["ports"].append({
"port": int(port_proto[0]),
"protocol": port_proto[1],
"state": parts[1],
"service": parts[2] if len(parts) > 2 else "unknown",
"version": ' '.join(parts[3:]) if len(parts) > 3 else None
})
# Parse OS detection
elif current_host and 'OS details:' in line:
current_host["os"] = line.replace('OS details:', '').strip()
# Parse timing info
elif 'scanned in' in line.lower():
match = re.search(r'scanned in ([\d.]+) seconds', line)
if match:
results["scan_info"]["duration_seconds"] = float(match.group(1))
if current_host:
results["hosts"].append(current_host)
return results
def parse_xml(self, xml_output: str) -> Dict[str, Any]:
"""Parse nmap XML output for more detailed results."""
try:
root = ET.fromstring(xml_output)
results = {
"hosts": [],
"scan_info": {
"scanner": root.get("scanner"),
"args": root.get("args"),
"start_time": root.get("start"),
}
}
for host in root.findall('.//host'):
host_info = {
"ip": None,
"hostname": None,
"status": host.find('status').get('state') if host.find('status') is not None else "unknown",
"ports": [],
"os": []
}
# Get addresses
for addr in host.findall('.//address'):
if addr.get('addrtype') == 'ipv4':
host_info["ip"] = addr.get('addr')
# Get hostnames
hostname_elem = host.find('.//hostname')
if hostname_elem is not None:
host_info["hostname"] = hostname_elem.get('name')
# Get ports
for port in host.findall('.//port'):
port_info = {
"port": int(port.get('portid')),
"protocol": port.get('protocol'),
"state": port.find('state').get('state') if port.find('state') is not None else "unknown",
}
service = port.find('service')
if service is not None:
port_info["service"] = service.get('name')
port_info["product"] = service.get('product')
port_info["version"] = service.get('version')
host_info["ports"].append(port_info)
results["hosts"].append(host_info)
return results
except ET.ParseError:
return {"error": "Failed to parse XML", "raw": xml_output}
class NiktoParser(BaseParser):
"""Parser for nikto output."""
def parse(self, output: str) -> Dict[str, Any]:
results = {
"target": None,
"findings": [],
"server_info": {},
"raw": output
}
for line in output.split('\n'):
line = line.strip()
# Target info
if '+ Target IP:' in line:
results["target"] = line.split(':')[-1].strip()
elif '+ Target Hostname:' in line:
results["server_info"]["hostname"] = line.split(':')[-1].strip()
elif '+ Target Port:' in line:
results["server_info"]["port"] = line.split(':')[-1].strip()
elif '+ Server:' in line:
results["server_info"]["server"] = line.split(':', 1)[-1].strip()
# Findings (lines starting with +)
elif line.startswith('+') and ':' in line:
# Skip info lines
if any(skip in line for skip in ['Target IP', 'Target Hostname', 'Target Port', 'Server:', 'Start Time', 'End Time']):
continue
finding = {
"raw": line[1:].strip(),
"severity": "info"
}
# Determine severity based on content
if any(word in line.lower() for word in ['vulnerable', 'vulnerability', 'exploit']):
finding["severity"] = "high"
elif any(word in line.lower() for word in ['outdated', 'deprecated', 'insecure']):
finding["severity"] = "medium"
elif any(word in line.lower() for word in ['disclosed', 'information', 'header']):
finding["severity"] = "low"
# Extract OSVDB if present
osvdb_match = re.search(r'OSVDB-(\d+)', line)
if osvdb_match:
finding["osvdb"] = osvdb_match.group(1)
results["findings"].append(finding)
return results
class SQLMapParser(BaseParser):
"""Parser for sqlmap output."""
def parse(self, output: str) -> Dict[str, Any]:
results = {
"target": None,
"parameters": [],
"injections": [],
"databases": [],
"raw": output
}
in_parameter_section = False
for line in output.split('\n'):
line = line.strip()
# Target URL
if 'target URL' in line.lower():
match = re.search(r"'([^']+)'", line)
if match:
results["target"] = match.group(1)
# Injectable parameters
if 'Parameter:' in line:
param_match = re.search(r"Parameter: (\S+)", line)
if param_match:
results["parameters"].append({
"name": param_match.group(1),
"injectable": True
})
# Injection type
if 'Type:' in line and 'injection' in line.lower():
results["injections"].append(line.replace('Type:', '').strip())
# Databases found
if line.startswith('[*]') and 'available databases' not in line.lower():
db_name = line[3:].strip()
if db_name:
results["databases"].append(db_name)
return results
class GobusterParser(BaseParser):
"""Parser for gobuster output."""
def parse(self, output: str) -> Dict[str, Any]:
results = {
"findings": [],
"directories": [],
"files": [],
"raw": output
}
for line in output.split('\n'):
line = line.strip()
# Parse found paths
# Format: /path (Status: 200) [Size: 1234]
match = re.search(r'^(/\S*)\s+\(Status:\s*(\d+)\)(?:\s+\[Size:\s*(\d+)\])?', line)
if match:
finding = {
"path": match.group(1),
"status": int(match.group(2)),
"size": int(match.group(3)) if match.group(3) else None
}
results["findings"].append(finding)
if finding["path"].endswith('/'):
results["directories"].append(finding["path"])
else:
results["files"].append(finding["path"])
return results
class HydraParser(BaseParser):
"""Parser for hydra output."""
def parse(self, output: str) -> Dict[str, Any]:
results = {
"credentials": [],
"target": None,
"service": None,
"raw": output
}
for line in output.split('\n'):
line = line.strip()
# Parse found credentials
# Format: [port][service] host: x login: y password: z
cred_match = re.search(r'\[(\d+)\]\[(\w+)\]\s+host:\s+(\S+)\s+login:\s+(\S+)\s+password:\s+(\S+)', line)
if cred_match:
results["credentials"].append({
"port": int(cred_match.group(1)),
"service": cred_match.group(2),
"host": cred_match.group(3),
"username": cred_match.group(4),
"password": cred_match.group(5)
})
results["target"] = cred_match.group(3)
results["service"] = cred_match.group(2)
return results
# Registry of parsers
PARSERS = {
"nmap": NmapParser(),
"nikto": NiktoParser(),
"sqlmap": SQLMapParser(),
"gobuster": GobusterParser(),
"hydra": HydraParser(),
}
def parse_tool_output(tool: str, output: str) -> Dict[str, Any]:
"""Parse output from a security tool."""
parser = PARSERS.get(tool.lower())
if parser:
try:
return parser.parse(output)
except Exception as e:
return {"error": str(e), "raw": output}
return {"raw": output}

263
services/shared/tools.py Normal file
View File

@@ -0,0 +1,263 @@
"""
Security tool definitions and command builders.
"""
from typing import Dict, List, Optional, Any
SECURITY_TOOLS = {
# ============== Reconnaissance ==============
"nmap": {
"name": "nmap",
"description": "Network scanner and security auditing tool",
"category": "reconnaissance",
"templates": {
"quick": "nmap -T4 -F {target}",
"full": "nmap -sV -sC -O -p- {target}",
"stealth": "nmap -sS -T2 -f {target}",
"udp": "nmap -sU --top-ports 100 {target}",
"vuln": "nmap --script vuln {target}",
"version": "nmap -sV -p {ports} {target}",
"os": "nmap -O --osscan-guess {target}",
},
"default_template": "quick",
"output_parser": "nmap"
},
"masscan": {
"name": "masscan",
"description": "Fast TCP port scanner",
"category": "reconnaissance",
"templates": {
"quick": "masscan {target} --ports 0-1000 --rate 1000",
"full": "masscan {target} --ports 0-65535 --rate 10000",
"top100": "masscan {target} --top-ports 100 --rate 1000",
},
"default_template": "quick",
},
"amass": {
"name": "amass",
"description": "Subdomain enumeration tool",
"category": "reconnaissance",
"templates": {
"passive": "amass enum -passive -d {target}",
"active": "amass enum -active -d {target}",
"intel": "amass intel -d {target}",
},
"default_template": "passive",
},
"theharvester": {
"name": "theHarvester",
"description": "OSINT tool for gathering emails, names, subdomains",
"category": "reconnaissance",
"templates": {
"all": "theHarvester -d {target} -b all",
"google": "theHarvester -d {target} -b google",
"linkedin": "theHarvester -d {target} -b linkedin",
},
"default_template": "all",
},
"whatweb": {
"name": "whatweb",
"description": "Web technology fingerprinting",
"category": "reconnaissance",
"templates": {
"default": "whatweb {target}",
"aggressive": "whatweb -a 3 {target}",
"verbose": "whatweb -v {target}",
},
"default_template": "default",
},
"dnsrecon": {
"name": "dnsrecon",
"description": "DNS enumeration tool",
"category": "reconnaissance",
"templates": {
"standard": "dnsrecon -d {target}",
"zone": "dnsrecon -d {target} -t axfr",
"brute": "dnsrecon -d {target} -t brt",
},
"default_template": "standard",
},
# ============== Vulnerability Scanning ==============
"nikto": {
"name": "nikto",
"description": "Web server vulnerability scanner",
"category": "vulnerability_scanning",
"templates": {
"default": "nikto -h {target}",
"ssl": "nikto -h {target} -ssl",
"tuning": "nikto -h {target} -Tuning x",
"full": "nikto -h {target} -C all",
},
"default_template": "default",
"output_parser": "nikto"
},
"sqlmap": {
"name": "sqlmap",
"description": "SQL injection detection and exploitation",
"category": "vulnerability_scanning",
"templates": {
"test": "sqlmap -u '{target}' --batch",
"dbs": "sqlmap -u '{target}' --batch --dbs",
"tables": "sqlmap -u '{target}' --batch -D {database} --tables",
"dump": "sqlmap -u '{target}' --batch -D {database} -T {table} --dump",
"forms": "sqlmap -u '{target}' --batch --forms",
},
"default_template": "test",
"output_parser": "sqlmap"
},
"wpscan": {
"name": "wpscan",
"description": "WordPress vulnerability scanner",
"category": "vulnerability_scanning",
"templates": {
"default": "wpscan --url {target}",
"enumerate": "wpscan --url {target} -e vp,vt,u",
"aggressive": "wpscan --url {target} -e ap,at,u --plugins-detection aggressive",
},
"default_template": "default",
},
# ============== Web Testing ==============
"gobuster": {
"name": "gobuster",
"description": "Directory/file brute-forcing",
"category": "web_testing",
"templates": {
"dir": "gobuster dir -u {target} -w /usr/share/wordlists/dirb/common.txt",
"big": "gobuster dir -u {target} -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt",
"dns": "gobuster dns -d {target} -w /usr/share/wordlists/dns/subdomains-top1million-5000.txt",
"vhost": "gobuster vhost -u {target} -w /usr/share/wordlists/dns/subdomains-top1million-5000.txt",
},
"default_template": "dir",
"output_parser": "gobuster"
},
"ffuf": {
"name": "ffuf",
"description": "Fast web fuzzer",
"category": "web_testing",
"templates": {
"dir": "ffuf -u {target}/FUZZ -w /usr/share/wordlists/dirb/common.txt",
"vhost": "ffuf -u {target} -H 'Host: FUZZ.{domain}' -w /usr/share/wordlists/dns/subdomains-top1million-5000.txt",
"param": "ffuf -u '{target}?FUZZ=test' -w /usr/share/wordlists/dirb/common.txt",
},
"default_template": "dir",
},
"dirb": {
"name": "dirb",
"description": "Web content scanner",
"category": "web_testing",
"templates": {
"default": "dirb {target}",
"small": "dirb {target} /usr/share/wordlists/dirb/small.txt",
"big": "dirb {target} /usr/share/wordlists/dirb/big.txt",
},
"default_template": "default",
},
# ============== Exploitation ==============
"searchsploit": {
"name": "searchsploit",
"description": "Exploit database search tool",
"category": "exploitation",
"templates": {
"search": "searchsploit {query}",
"exact": "searchsploit -e {query}",
"json": "searchsploit -j {query}",
"path": "searchsploit -p {exploit_id}",
},
"default_template": "search",
},
"hydra": {
"name": "hydra",
"description": "Network login cracker",
"category": "password_attacks",
"templates": {
"ssh": "hydra -l {user} -P /usr/share/wordlists/rockyou.txt {target} ssh",
"ftp": "hydra -l {user} -P /usr/share/wordlists/rockyou.txt {target} ftp",
"http_post": "hydra -l {user} -P /usr/share/wordlists/rockyou.txt {target} http-post-form '{form}'",
"smb": "hydra -l {user} -P /usr/share/wordlists/rockyou.txt {target} smb",
},
"default_template": "ssh",
"output_parser": "hydra"
},
# ============== Network Tools ==============
"netcat": {
"name": "nc",
"description": "Network utility for TCP/UDP connections",
"category": "network",
"templates": {
"listen": "nc -lvnp {port}",
"connect": "nc -v {target} {port}",
"scan": "nc -zv {target} {port_range}",
},
"default_template": "scan",
},
"curl": {
"name": "curl",
"description": "HTTP client",
"category": "web_testing",
"templates": {
"get": "curl -v {target}",
"headers": "curl -I {target}",
"post": "curl -X POST -d '{data}' {target}",
"follow": "curl -L -v {target}",
},
"default_template": "get",
},
}
def get_tool(name: str) -> Optional[Dict[str, Any]]:
"""Get tool definition by name."""
return SECURITY_TOOLS.get(name.lower())
def get_tools_by_category(category: str) -> List[Dict[str, Any]]:
"""Get all tools in a category."""
return [tool for tool in SECURITY_TOOLS.values() if tool.get("category") == category]
def build_command(tool_name: str, template_name: str = None, **kwargs) -> Optional[str]:
"""Build a command from a tool template."""
tool = get_tool(tool_name)
if not tool:
return None
template_name = template_name or tool.get("default_template")
template = tool.get("templates", {}).get(template_name)
if not template:
return None
try:
return template.format(**kwargs)
except KeyError as e:
return None
def list_all_tools() -> Dict[str, List[Dict[str, str]]]:
"""List all available tools grouped by category."""
result = {}
for tool in SECURITY_TOOLS.values():
category = tool.get("category", "other")
if category not in result:
result[category] = []
result[category].append({
"name": tool["name"],
"description": tool["description"],
"templates": list(tool.get("templates", {}).keys())
})
return result