start project
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""Blocklist-suggestion generation (Chapter 10 follow-up): no earlier
|
||||
chapter assigned ownership of populating blocklist_suggestions or setting
|
||||
ip_registry.is_flagged. Runs in the background aggregator pass (batch, not
|
||||
request-time, per Ch02/03), reusing severity_scoring.py so there's exactly
|
||||
one scoring model between the dashboard display and the flagging decision.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import defaultdict
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
from app.extensions import db
|
||||
from app.models.blocklist_suggestion import BlocklistSuggestion
|
||||
from app.models.ip_registry import IPRegistry
|
||||
from app.models.suspicious_event import SuspiciousEvent
|
||||
from app.services.severity_scoring import SeverityInputs, compute_effective_severity
|
||||
|
||||
_RANK = {"low": 0, "medium": 1, "high": 2}
|
||||
|
||||
|
||||
def refresh_blocklist_suggestions(day: date) -> None:
|
||||
"""Flag an IP (is_flagged + a suggestion row) if its escalated severity
|
||||
for `day` reaches 'high'. Idempotent — skips IPs already suggested.
|
||||
"""
|
||||
start = datetime.combine(day, datetime.min.time())
|
||||
end = start + timedelta(days=1)
|
||||
|
||||
events = (
|
||||
db.session.query(SuspiciousEvent.ip, SuspiciousEvent.timestamp, SuspiciousEvent.severity)
|
||||
.filter(SuspiciousEvent.timestamp >= start, SuspiciousEvent.timestamp < end)
|
||||
.all()
|
||||
)
|
||||
if not events:
|
||||
return
|
||||
|
||||
by_ip: dict[str, list] = defaultdict(list)
|
||||
for ip, ts, sev in events:
|
||||
by_ip[ip].append((ts, sev))
|
||||
|
||||
already_suggested = {ip for (ip,) in db.session.query(BlocklistSuggestion.ip).distinct().all()}
|
||||
|
||||
for ip, ip_events in by_ip.items():
|
||||
if ip in already_suggested:
|
||||
continue
|
||||
timestamps = sorted(ts for ts, _ in ip_events)
|
||||
avg_interval = (
|
||||
(timestamps[-1] - timestamps[0]).total_seconds() / (len(timestamps) - 1)
|
||||
if len(timestamps) > 1 else None
|
||||
)
|
||||
worst_base = max((sev for _, sev in ip_events), key=lambda s: _RANK.get(s, 0))
|
||||
effective = compute_effective_severity(
|
||||
SeverityInputs(base_severity=worst_base, ip_event_count=len(ip_events), avg_interval_seconds=avg_interval)
|
||||
)
|
||||
if effective != "high":
|
||||
continue
|
||||
|
||||
db.session.add(BlocklistSuggestion(
|
||||
ip=ip,
|
||||
reason=f"{len(ip_events)} suspicious event(s) on {day.isoformat()}, escalated to high severity",
|
||||
created_at=datetime.utcnow(),
|
||||
exported=False,
|
||||
))
|
||||
ip_row = db.session.get(IPRegistry, ip)
|
||||
if ip_row is not None:
|
||||
ip_row.is_flagged = True
|
||||
|
||||
db.session.commit()
|
||||
Reference in New Issue
Block a user