feat: Add IP filtering to attack types pagination and detail views

This commit is contained in:
Lorenzo Venerandi
2026-03-01 18:01:19 +01:00
parent 6ec4e49d10
commit ed4fe0dcfb
4 changed files with 35 additions and 8 deletions

View File

@@ -1955,6 +1955,7 @@ class DatabaseManager:
page_size: int = 5,
sort_by: str = "timestamp",
sort_order: str = "desc",
ip_filter: Optional[str] = None,
) -> Dict[str, Any]:
"""
Retrieve paginated list of detected attack types with access logs.
@@ -1964,6 +1965,7 @@ class DatabaseManager:
page_size: Number of results per page
sort_by: Field to sort by (timestamp, ip, attack_type)
sort_order: Sort order (asc or desc)
ip_filter: Optional IP address to filter results
Returns:
Dictionary with attacks list and pagination info
@@ -1979,18 +1981,27 @@ class DatabaseManager:
sort_order.lower() if sort_order.lower() in {"asc", "desc"} else "desc"
)
# Base query filter
base_filters = []
if ip_filter:
base_filters.append(AccessLog.ip == ip_filter)
# Count total unique access logs with attack detections
total_attacks = (
count_query = (
session.query(AccessLog)
.join(AttackDetection)
.distinct(AccessLog.id)
.count()
)
if base_filters:
count_query = count_query.filter(*base_filters)
total_attacks = count_query.distinct(AccessLog.id).count()
# Get paginated access logs with attack detections
query = (
session.query(AccessLog).join(AttackDetection).distinct(AccessLog.id)
session.query(AccessLog).join(AttackDetection)
)
if base_filters:
query = query.filter(*base_filters)
query = query.distinct(AccessLog.id)
if sort_by == "timestamp":
query = query.order_by(

View File

@@ -241,10 +241,12 @@ async def htmx_attacks(
page: int = Query(1),
sort_by: str = Query("timestamp"),
sort_order: str = Query("desc"),
ip_filter: str = Query(None),
):
db = get_db()
result = db.get_attack_types_paginated(
page=max(1, page), page_size=5, sort_by=sort_by, sort_order=sort_order
page=max(1, page), page_size=5, sort_by=sort_by, sort_order=sort_order,
ip_filter=ip_filter,
)
# Transform attack data for template (join attack_types list, map id to log_id)
@@ -271,6 +273,7 @@ async def htmx_attacks(
"pagination": result["pagination"],
"sort_by": sort_by,
"sort_order": sort_order,
"ip_filter": ip_filter or "",
},
)

View File

@@ -185,6 +185,19 @@
</div>
{% endif %}
{# Detected Attack Types table only for attackers #}
{% if stats.category and stats.category | lower == 'attacker' %}
<div class="table-container alert-section" style="margin-top: 20px;">
<h2>Detected Attack Types</h2>
<div class="htmx-container"
hx-get="{{ dashboard_path }}/htmx/attacks?page=1&ip_filter={{ ip_address }}"
hx-trigger="revealed"
hx-swap="innerHTML">
<div class="htmx-indicator">Loading...</div>
</div>
</div>
{% endif %}
{# Access History table #}
<div class="table-container alert-section" style="margin-top: 20px;">
<h2>Access History</h2>

View File

@@ -3,12 +3,12 @@
<span class="pagination-info">Page {{ pagination.page }}/{{ pagination.total_pages }} &mdash; {{ pagination.total }} total</span>
<div style="display: flex; gap: 8px;">
<button class="pagination-btn"
hx-get="{{ dashboard_path }}/htmx/attacks?page={{ pagination.page - 1 }}&sort_by={{ sort_by }}&sort_order={{ sort_order }}"
hx-get="{{ dashboard_path }}/htmx/attacks?page={{ pagination.page - 1 }}&sort_by={{ sort_by }}&sort_order={{ sort_order }}{% if ip_filter %}&ip_filter={{ ip_filter }}{% endif %}"
hx-target="closest .htmx-container"
hx-swap="innerHTML"
{% if pagination.page <= 1 %}disabled{% endif %}>Prev</button>
<button class="pagination-btn"
hx-get="{{ dashboard_path }}/htmx/attacks?page={{ pagination.page + 1 }}&sort_by={{ sort_by }}&sort_order={{ sort_order }}"
hx-get="{{ dashboard_path }}/htmx/attacks?page={{ pagination.page + 1 }}&sort_by={{ sort_by }}&sort_order={{ sort_order }}{% if ip_filter %}&ip_filter={{ ip_filter }}{% endif %}"
hx-target="closest .htmx-container"
hx-swap="innerHTML"
{% if pagination.page >= pagination.total_pages %}disabled{% endif %}>Next</button>
@@ -23,7 +23,7 @@
<th>Attack Types</th>
<th>User-Agent</th>
<th class="sortable {% if sort_by == 'timestamp' %}{{ sort_order }}{% endif %}"
hx-get="{{ dashboard_path }}/htmx/attacks?page=1&sort_by=timestamp&sort_order={% if sort_by == 'timestamp' and sort_order == 'desc' %}asc{% else %}desc{% endif %}"
hx-get="{{ dashboard_path }}/htmx/attacks?page=1&sort_by=timestamp&sort_order={% if sort_by == 'timestamp' and sort_order == 'desc' %}asc{% else %}desc{% endif %}{% if ip_filter %}&ip_filter={{ ip_filter }}{% endif %}"
hx-target="closest .htmx-container"
hx-swap="innerHTML">
Time