#!/usr/bin/env python3
"""
Dashboard template for viewing honeypot statistics.
Customize this template to change the dashboard appearance.
"""
import html
from datetime import datetime
from zoneinfo import ZoneInfo
# imports for the __init_subclass__ method, do not remove pls
from firewall import fwtype
def _escape(value) -> str:
"""Escape HTML special characters to prevent XSS attacks."""
if value is None:
return ""
return html.escape(str(value))
def format_timestamp(iso_timestamp: str, time_only: bool = False) -> str:
"""Format ISO timestamp for display with timezone conversion
Args:
iso_timestamp: ISO format timestamp string (UTC)
time_only: If True, return only HH:MM:SS, otherwise full datetime
"""
try:
# Parse UTC timestamp
dt = datetime.fromisoformat(iso_timestamp)
if time_only:
return dt.strftime("%H:%M:%S")
return dt.strftime("%Y-%m-%d %H:%M:%S")
except Exception:
# Fallback for old format
return (
iso_timestamp.split("T")[1][:8] if "T" in iso_timestamp else iso_timestamp
)
def generate_dashboard(stats: dict, dashboard_path: str = "") -> str:
"""Generate dashboard HTML with access statistics
Args:
stats: Statistics dictionary
dashboard_path: The secret dashboard path for generating API URLs
"""
# Generate comprehensive suspicious activity rows combining all suspicious events
suspicious_activities = []
# Add recent suspicious accesses (attacks)
for log in stats.get("recent_suspicious", [])[-20:]:
suspicious_activities.append({
"type": "Attack",
"ip": log["ip"],
"path": log["path"],
"user_agent": log["user_agent"][:60],
"timestamp": log["timestamp"],
"details": ", ".join(log.get("attack_types", [])) if log.get("attack_types") else "Suspicious behavior"
})
# Add credential attempts
for cred in stats.get("credential_attempts", [])[-20:]:
suspicious_activities.append({
"type": "Credentials",
"ip": cred["ip"],
"path": cred["path"],
"user_agent": "",
"timestamp": cred["timestamp"],
"details": f"User: {cred.get('username', 'N/A')}"
})
# Add honeypot triggers
for honeypot in stats.get("honeypot_triggered_ips", [])[-20:]:
# honeypot is a tuple (ip, paths)
ip = honeypot[0]
paths = honeypot[1] if isinstance(honeypot[1], list) else []
suspicious_activities.append({
"type": "Honeypot",
"ip": ip,
"path": paths[0] if paths else "Multiple",
"user_agent": "",
"timestamp": "", # Tuples don't have timestamp
"details": f"{len(paths)} trap(s) triggered"
})
# Sort by timestamp (most recent first) and take last 20
# Put entries with empty timestamps at the end
try:
suspicious_activities.sort(key=lambda x: (x["timestamp"] == "", x["timestamp"]), reverse=True)
except:
pass
suspicious_activities = suspicious_activities[:20]
# Generate table rows
suspicious_rows = (
"\n".join([f"""
| {_escape(activity["ip"])} |
{_escape(activity["type"])} |
{_escape(activity["path"])} |
{_escape(activity["details"])} |
{format_timestamp(activity["timestamp"], time_only=True)} |
|
|
""" for activity in suspicious_activities])
or '| No suspicious activity detected |
'
)
return f"""
Krawl Dashboard
BlessedRebuS/Krawl
Krawl Dashboard
{stats['total_accesses']}
Total Accesses
{stats['unique_ips']}
Unique IPs
{stats['unique_paths']}
Unique Paths
{stats['suspicious_accesses']}
Suspicious Accesses
{stats.get('honeypot_ips', 0)}
Honeypot Caught
{len(stats.get('credential_attempts', []))}
Credentials Captured
{stats.get('unique_attackers', 0)}
Unique Attackers
Recent Suspicious Activity
| IP Address |
Type |
Path |
Details |
Time |
{suspicious_rows}
Honeypot Triggers by IP
| # |
IP Address |
Accessed Paths |
Count |
| Loading... |
Top IP Addresses
| # |
IP Address |
Access Count |
| Loading... |
Top User-Agents
| # |
User-Agent |
Count |
| Loading... |
Attackers by Total Requests
| # |
IP Address |
Total Requests |
First Seen |
Last Seen |
Location |
Captured Credentials
| # |
IP Address |
Username |
Password |
Path |
Time |
| Loading... |
Detected Attack Types
| # |
IP Address |
Path |
Attack Types |
User-Agent |
Time |
Actions |
| Loading... |
Most Recurring Attack Types
Top 10
Most Recurring Attack Patterns
| # |
Attack Pattern |
Attack Type |
Frequency |
IPs |
| Loading... |
"""