Files
Bjorn/db_utils/webenum.py

163 lines
6.4 KiB
Python

# db_utils/webenum.py
# Web enumeration (directory/file discovery) operations
from typing import Any, Dict, List, Optional
import logging
from logger import Logger
logger = Logger(name="db_utils.webenum", level=logging.DEBUG)
class WebEnumOps:
"""Web directory and file enumeration tracking operations"""
def __init__(self, base):
self.base = base
def create_tables(self):
"""Create web enumeration table"""
self.base.execute("""
CREATE TABLE IF NOT EXISTS webenum (
id INTEGER PRIMARY KEY AUTOINCREMENT,
mac_address TEXT NOT NULL,
ip TEXT NOT NULL,
hostname TEXT,
port INTEGER NOT NULL,
directory TEXT NOT NULL,
status INTEGER NOT NULL,
size INTEGER DEFAULT 0,
response_time INTEGER DEFAULT 0,
content_type TEXT,
scan_date TEXT DEFAULT CURRENT_TIMESTAMP,
tool TEXT DEFAULT 'gobuster',
method TEXT DEFAULT 'GET',
user_agent TEXT,
headers TEXT,
first_seen TEXT DEFAULT CURRENT_TIMESTAMP,
last_seen TEXT DEFAULT CURRENT_TIMESTAMP,
is_active INTEGER DEFAULT 1,
UNIQUE(mac_address, ip, port, directory)
);
""")
# Indexes for frequent queries
self.base.execute("CREATE INDEX IF NOT EXISTS idx_webenum_host_port ON webenum(mac_address, port);")
self.base.execute("CREATE INDEX IF NOT EXISTS idx_webenum_status ON webenum(status);")
self.base.execute("CREATE INDEX IF NOT EXISTS idx_webenum_scan_date ON webenum(scan_date);")
self.base.execute("CREATE INDEX IF NOT EXISTS idx_webenum_active ON webenum(is_active) WHERE is_active=1;")
logger.debug("WebEnum table created/verified")
# =========================================================================
# WEB ENUMERATION OPERATIONS
# =========================================================================
def add_webenum_result(
self,
mac_address: str,
ip: str,
port: int,
directory: str,
status: int,
*,
hostname: Optional[str] = None,
size: int = 0,
response_time: int = 0,
content_type: Optional[str] = None,
tool: str = "gobuster",
method: str = "GET",
user_agent: Optional[str] = None,
headers: Optional[str] = None
):
"""Add or update a web enumeration result"""
self.base.execute("""
INSERT INTO webenum (
mac_address, ip, hostname, port, directory, status,
size, response_time, content_type, tool, method,
user_agent, headers, is_active
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1)
ON CONFLICT(mac_address, ip, port, directory) DO UPDATE SET
status = excluded.status,
size = excluded.size,
response_time = excluded.response_time,
content_type = excluded.content_type,
hostname = COALESCE(excluded.hostname, webenum.hostname),
last_seen = CURRENT_TIMESTAMP,
is_active = 1
""", (
mac_address, ip, hostname, port, directory, status,
size, response_time, content_type, tool, method,
user_agent, headers
))
def get_webenum_for_host(self, mac_address: str, port: Optional[int] = None) -> List[Dict[str, Any]]:
"""Get all web enumeration results for a host (optionally filtered by port)"""
if port is not None:
return self.base.query("""
SELECT * FROM webenum
WHERE mac_address = ? AND port = ? AND is_active = 1
ORDER BY status, directory
""", (mac_address, port))
else:
return self.base.query("""
SELECT * FROM webenum
WHERE mac_address = ? AND is_active = 1
ORDER BY port, status, directory
""", (mac_address,))
def get_webenum_by_status(self, status: int) -> List[Dict[str, Any]]:
"""Get all enumeration results with a specific HTTP status code"""
return self.base.query("""
SELECT * FROM webenum
WHERE status = ? AND is_active = 1
ORDER BY mac_address, port, directory
""", (status,))
def mark_webenum_inactive(self, mac_address: str, port: int, directories: List[str]):
"""Mark enumeration results as inactive (e.g., after a rescan)"""
if not directories:
return
placeholders = ",".join("?" for _ in directories)
self.base.execute(f"""
UPDATE webenum
SET is_active = 0, last_seen = CURRENT_TIMESTAMP
WHERE mac_address = ? AND port = ? AND directory IN ({placeholders})
""", (mac_address, port, *directories))
def delete_webenum_for_host(self, mac_address: str, port: Optional[int] = None):
"""Delete all enumeration results for a host (optionally filtered by port)"""
if port is not None:
self.base.execute("""
DELETE FROM webenum
WHERE mac_address = ? AND port = ?
""", (mac_address, port))
else:
self.base.execute("""
DELETE FROM webenum
WHERE mac_address = ?
""", (mac_address,))
def count_webenum_results(self, mac_address: Optional[str] = None,
active_only: bool = True) -> int:
"""Count enumeration results (optionally for a specific host and/or active only)"""
where_clauses = []
params = []
if mac_address:
where_clauses.append("mac_address = ?")
params.append(mac_address)
if active_only:
where_clauses.append("is_active = 1")
where_sql = " AND ".join(where_clauses) if where_clauses else "1=1"
row = self.base.query_one(f"""
SELECT COUNT(*) as cnt FROM webenum
WHERE {where_sql}
""", tuple(params))
return int(row["cnt"]) if row else 0