Linted code iwht black tool
This commit is contained in:
@@ -8,8 +8,8 @@ from .template_loader import load_template, clear_cache, TemplateNotFoundError
|
||||
from . import html_templates
|
||||
|
||||
__all__ = [
|
||||
'load_template',
|
||||
'clear_cache',
|
||||
'TemplateNotFoundError',
|
||||
'html_templates',
|
||||
"load_template",
|
||||
"clear_cache",
|
||||
"TemplateNotFoundError",
|
||||
"html_templates",
|
||||
]
|
||||
|
||||
@@ -9,12 +9,14 @@ import html
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
||||
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
|
||||
|
||||
@@ -30,10 +32,12 @@ def format_timestamp(iso_timestamp: str, time_only: bool = False) -> str:
|
||||
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
|
||||
return (
|
||||
iso_timestamp.split("T")[1][:8] if "T" in iso_timestamp else iso_timestamp
|
||||
)
|
||||
|
||||
|
||||
def generate_dashboard(stats: dict, dashboard_path: str = '') -> str:
|
||||
def generate_dashboard(stats: dict, dashboard_path: str = "") -> str:
|
||||
"""Generate dashboard HTML with access statistics
|
||||
|
||||
Args:
|
||||
@@ -42,8 +46,8 @@ def generate_dashboard(stats: dict, dashboard_path: str = '') -> str:
|
||||
"""
|
||||
|
||||
# Generate IP rows with clickable functionality for dropdown stats
|
||||
top_ips_rows = '\n'.join([
|
||||
f'''<tr class="ip-row" data-ip="{_escape(ip)}">
|
||||
top_ips_rows = (
|
||||
"\n".join([f"""<tr class="ip-row" data-ip="{_escape(ip)}">
|
||||
<td class="rank">{i+1}</td>
|
||||
<td class="ip-clickable">{_escape(ip)}</td>
|
||||
<td>{count}</td>
|
||||
@@ -54,25 +58,35 @@ def generate_dashboard(stats: dict, dashboard_path: str = '') -> str:
|
||||
<div class="loading">Loading stats...</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>'''
|
||||
for i, (ip, count) in enumerate(stats['top_ips'])
|
||||
]) or '<tr><td colspan="3" style="text-align:center;">No data</td></tr>'
|
||||
</tr>""" for i, (ip, count) in enumerate(stats["top_ips"])])
|
||||
or '<tr><td colspan="3" style="text-align:center;">No data</td></tr>'
|
||||
)
|
||||
|
||||
# Generate paths rows (CRITICAL: paths can contain XSS payloads)
|
||||
top_paths_rows = '\n'.join([
|
||||
f'<tr><td class="rank">{i+1}</td><td>{_escape(path)}</td><td>{count}</td></tr>'
|
||||
for i, (path, count) in enumerate(stats['top_paths'])
|
||||
]) or '<tr><td colspan="3" style="text-align:center;">No data</td></tr>'
|
||||
top_paths_rows = (
|
||||
"\n".join(
|
||||
[
|
||||
f'<tr><td class="rank">{i+1}</td><td>{_escape(path)}</td><td>{count}</td></tr>'
|
||||
for i, (path, count) in enumerate(stats["top_paths"])
|
||||
]
|
||||
)
|
||||
or '<tr><td colspan="3" style="text-align:center;">No data</td></tr>'
|
||||
)
|
||||
|
||||
# Generate User-Agent rows (CRITICAL: user agents can contain XSS payloads)
|
||||
top_ua_rows = '\n'.join([
|
||||
f'<tr><td class="rank">{i+1}</td><td style="word-break: break-all;">{_escape(ua[:80])}</td><td>{count}</td></tr>'
|
||||
for i, (ua, count) in enumerate(stats['top_user_agents'])
|
||||
]) or '<tr><td colspan="3" style="text-align:center;">No data</td></tr>'
|
||||
top_ua_rows = (
|
||||
"\n".join(
|
||||
[
|
||||
f'<tr><td class="rank">{i+1}</td><td style="word-break: break-all;">{_escape(ua[:80])}</td><td>{count}</td></tr>'
|
||||
for i, (ua, count) in enumerate(stats["top_user_agents"])
|
||||
]
|
||||
)
|
||||
or '<tr><td colspan="3" style="text-align:center;">No data</td></tr>'
|
||||
)
|
||||
|
||||
# Generate suspicious accesses rows with clickable IPs
|
||||
suspicious_rows = '\n'.join([
|
||||
f'''<tr class="ip-row" data-ip="{_escape(log["ip"])}">
|
||||
suspicious_rows = (
|
||||
"\n".join([f"""<tr class="ip-row" data-ip="{_escape(log["ip"])}">
|
||||
<td class="ip-clickable">{_escape(log["ip"])}</td>
|
||||
<td>{_escape(log["path"])}</td>
|
||||
<td style="word-break: break-all;">{_escape(log["user_agent"][:60])}</td>
|
||||
@@ -84,13 +98,13 @@ def generate_dashboard(stats: dict, dashboard_path: str = '') -> str:
|
||||
<div class="loading">Loading stats...</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>'''
|
||||
for log in stats['recent_suspicious'][-10:]
|
||||
]) or '<tr><td colspan="4" style="text-align:center;">No suspicious activity detected</td></tr>'
|
||||
</tr>""" for log in stats["recent_suspicious"][-10:]])
|
||||
or '<tr><td colspan="4" style="text-align:center;">No suspicious activity detected</td></tr>'
|
||||
)
|
||||
|
||||
# Generate honeypot triggered IPs rows with clickable IPs
|
||||
honeypot_rows = '\n'.join([
|
||||
f'''<tr class="ip-row" data-ip="{_escape(ip)}">
|
||||
honeypot_rows = (
|
||||
"\n".join([f"""<tr class="ip-row" data-ip="{_escape(ip)}">
|
||||
<td class="ip-clickable">{_escape(ip)}</td>
|
||||
<td style="word-break: break-all;">{_escape(", ".join(paths))}</td>
|
||||
<td>{len(paths)}</td>
|
||||
@@ -101,13 +115,13 @@ def generate_dashboard(stats: dict, dashboard_path: str = '') -> str:
|
||||
<div class="loading">Loading stats...</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>'''
|
||||
for ip, paths in stats.get('honeypot_triggered_ips', [])
|
||||
]) or '<tr><td colspan="3" style="text-align:center;">No honeypot triggers yet</td></tr>'
|
||||
</tr>""" for ip, paths in stats.get("honeypot_triggered_ips", [])])
|
||||
or '<tr><td colspan="3" style="text-align:center;">No honeypot triggers yet</td></tr>'
|
||||
)
|
||||
|
||||
# Generate attack types rows with clickable IPs
|
||||
attack_type_rows = '\n'.join([
|
||||
f'''<tr class="ip-row" data-ip="{_escape(log["ip"])}">
|
||||
attack_type_rows = (
|
||||
"\n".join([f"""<tr class="ip-row" data-ip="{_escape(log["ip"])}">
|
||||
<td class="ip-clickable">{_escape(log["ip"])}</td>
|
||||
<td>{_escape(log["path"])}</td>
|
||||
<td>{_escape(", ".join(log["attack_types"]))}</td>
|
||||
@@ -120,13 +134,13 @@ def generate_dashboard(stats: dict, dashboard_path: str = '') -> str:
|
||||
<div class="loading">Loading stats...</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>'''
|
||||
for log in stats.get('attack_types', [])[-10:]
|
||||
]) or '<tr><td colspan="4" style="text-align:center;">No attacks detected</td></tr>'
|
||||
</tr>""" for log in stats.get("attack_types", [])[-10:]])
|
||||
or '<tr><td colspan="4" style="text-align:center;">No attacks detected</td></tr>'
|
||||
)
|
||||
|
||||
# Generate credential attempts rows with clickable IPs
|
||||
credential_rows = '\n'.join([
|
||||
f'''<tr class="ip-row" data-ip="{_escape(log["ip"])}">
|
||||
credential_rows = (
|
||||
"\n".join([f"""<tr class="ip-row" data-ip="{_escape(log["ip"])}">
|
||||
<td class="ip-clickable">{_escape(log["ip"])}</td>
|
||||
<td>{_escape(log["username"])}</td>
|
||||
<td>{_escape(log["password"])}</td>
|
||||
@@ -139,9 +153,9 @@ def generate_dashboard(stats: dict, dashboard_path: str = '') -> str:
|
||||
<div class="loading">Loading stats...</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>'''
|
||||
for log in stats.get('credential_attempts', [])[-20:]
|
||||
]) or '<tr><td colspan="5" style="text-align:center;">No credentials captured yet</td></tr>'
|
||||
</tr>""" for log in stats.get("credential_attempts", [])[-20:]])
|
||||
or '<tr><td colspan="5" style="text-align:center;">No credentials captured yet</td></tr>'
|
||||
)
|
||||
|
||||
return f"""<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
@@ -11,6 +11,7 @@ from typing import Dict
|
||||
|
||||
class TemplateNotFoundError(Exception):
|
||||
"""Raised when a template file cannot be found."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@@ -42,11 +43,11 @@ def load_template(name: str, **kwargs) -> str:
|
||||
"""
|
||||
# debug
|
||||
# print(f"Loading Template: {name}")
|
||||
|
||||
|
||||
# Check cache first
|
||||
if name not in _template_cache:
|
||||
# Determine file path based on whether name has an extension
|
||||
if '.' in name:
|
||||
if "." in name:
|
||||
file_path = _TEMPLATE_DIR / name
|
||||
else:
|
||||
file_path = _TEMPLATE_DIR / f"{name}.html"
|
||||
@@ -54,7 +55,7 @@ def load_template(name: str, **kwargs) -> str:
|
||||
if not file_path.exists():
|
||||
raise TemplateNotFoundError(f"Template '{name}' not found at {file_path}")
|
||||
|
||||
_template_cache[name] = file_path.read_text(encoding='utf-8')
|
||||
_template_cache[name] = file_path.read_text(encoding="utf-8")
|
||||
|
||||
template = _template_cache[name]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user