feat: add need_reevaluation column to IpStats and update related logic
This commit is contained in:
@@ -261,7 +261,7 @@ class DatabaseManager:
|
|||||||
session.add(detection)
|
session.add(detection)
|
||||||
|
|
||||||
# Update IP stats
|
# Update IP stats
|
||||||
self._update_ip_stats(session, ip)
|
self._update_ip_stats(session, ip, is_suspicious)
|
||||||
|
|
||||||
session.commit()
|
session.commit()
|
||||||
return access_log.id
|
return access_log.id
|
||||||
@@ -313,13 +313,16 @@ class DatabaseManager:
|
|||||||
finally:
|
finally:
|
||||||
self.close_session()
|
self.close_session()
|
||||||
|
|
||||||
def _update_ip_stats(self, session: Session, ip: str) -> None:
|
def _update_ip_stats(
|
||||||
|
self, session: Session, ip: str, is_suspicious: bool = False
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Update IP statistics (upsert pattern).
|
Update IP statistics (upsert pattern).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
session: Active database session
|
session: Active database session
|
||||||
ip: IP address to update
|
ip: IP address to update
|
||||||
|
is_suspicious: Whether the request was flagged as suspicious
|
||||||
"""
|
"""
|
||||||
sanitized_ip = sanitize_ip(ip)
|
sanitized_ip = sanitize_ip(ip)
|
||||||
now = datetime.now()
|
now = datetime.now()
|
||||||
@@ -329,9 +332,15 @@ class DatabaseManager:
|
|||||||
if ip_stats:
|
if ip_stats:
|
||||||
ip_stats.total_requests += 1
|
ip_stats.total_requests += 1
|
||||||
ip_stats.last_seen = now
|
ip_stats.last_seen = now
|
||||||
|
if is_suspicious:
|
||||||
|
ip_stats.need_reevaluation = True
|
||||||
else:
|
else:
|
||||||
ip_stats = IpStats(
|
ip_stats = IpStats(
|
||||||
ip=sanitized_ip, total_requests=1, first_seen=now, last_seen=now
|
ip=sanitized_ip,
|
||||||
|
total_requests=1,
|
||||||
|
first_seen=now,
|
||||||
|
last_seen=now,
|
||||||
|
need_reevaluation=is_suspicious,
|
||||||
)
|
)
|
||||||
session.add(ip_stats)
|
session.add(ip_stats)
|
||||||
|
|
||||||
@@ -385,6 +394,7 @@ class DatabaseManager:
|
|||||||
ip_stats.category = category
|
ip_stats.category = category
|
||||||
ip_stats.category_scores = category_scores
|
ip_stats.category_scores = category_scores
|
||||||
ip_stats.last_analysis = last_analysis
|
ip_stats.last_analysis = last_analysis
|
||||||
|
ip_stats.need_reevaluation = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
session.commit()
|
session.commit()
|
||||||
@@ -637,6 +647,24 @@ class DatabaseManager:
|
|||||||
finally:
|
finally:
|
||||||
self.close_session()
|
self.close_session()
|
||||||
|
|
||||||
|
def get_ips_needing_reevaluation(self) -> List[str]:
|
||||||
|
"""
|
||||||
|
Get all IP addresses that have been flagged for reevaluation.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List of IP addresses where need_reevaluation is True
|
||||||
|
"""
|
||||||
|
session = self.session
|
||||||
|
try:
|
||||||
|
ips = (
|
||||||
|
session.query(IpStats.ip)
|
||||||
|
.filter(IpStats.need_reevaluation == True)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
return [ip[0] for ip in ips]
|
||||||
|
finally:
|
||||||
|
self.close_session()
|
||||||
|
|
||||||
def get_access_logs(
|
def get_access_logs(
|
||||||
self,
|
self,
|
||||||
limit: int = 100,
|
limit: int = 100,
|
||||||
|
|||||||
@@ -38,6 +38,16 @@ def _migrate_raw_request_column(cursor) -> bool:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _migrate_need_reevaluation_column(cursor) -> bool:
|
||||||
|
"""Add need_reevaluation column to ip_stats if missing."""
|
||||||
|
if _column_exists(cursor, "ip_stats", "need_reevaluation"):
|
||||||
|
return False
|
||||||
|
cursor.execute(
|
||||||
|
"ALTER TABLE ip_stats ADD COLUMN need_reevaluation BOOLEAN DEFAULT 0"
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _migrate_performance_indexes(cursor) -> List[str]:
|
def _migrate_performance_indexes(cursor) -> List[str]:
|
||||||
"""Add performance indexes to attack_detections if missing."""
|
"""Add performance indexes to attack_detections if missing."""
|
||||||
added = []
|
added = []
|
||||||
@@ -77,6 +87,9 @@ def run_migrations(database_path: str) -> None:
|
|||||||
if _migrate_raw_request_column(cursor):
|
if _migrate_raw_request_column(cursor):
|
||||||
applied.append("add raw_request column to access_logs")
|
applied.append("add raw_request column to access_logs")
|
||||||
|
|
||||||
|
if _migrate_need_reevaluation_column(cursor):
|
||||||
|
applied.append("add need_reevaluation column to ip_stats")
|
||||||
|
|
||||||
idx_added = _migrate_performance_indexes(cursor)
|
idx_added = _migrate_performance_indexes(cursor)
|
||||||
for idx in idx_added:
|
for idx in idx_added:
|
||||||
applied.append(f"add index {idx}")
|
applied.append(f"add index {idx}")
|
||||||
|
|||||||
@@ -200,6 +200,9 @@ class IpStats(Base):
|
|||||||
category_scores: Mapped[Dict[str, int]] = mapped_column(JSON, nullable=True)
|
category_scores: Mapped[Dict[str, int]] = mapped_column(JSON, nullable=True)
|
||||||
manual_category: Mapped[bool] = mapped_column(Boolean, default=False, nullable=True)
|
manual_category: Mapped[bool] = mapped_column(Boolean, default=False, nullable=True)
|
||||||
last_analysis: Mapped[datetime] = mapped_column(DateTime, nullable=True)
|
last_analysis: Mapped[datetime] = mapped_column(DateTime, nullable=True)
|
||||||
|
need_reevaluation: Mapped[bool] = mapped_column(
|
||||||
|
Boolean, default=False, nullable=True
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<IpStats(ip='{self.ip}', total_requests={self.total_requests})>"
|
return f"<IpStats(ip='{self.ip}', total_requests={self.total_requests})>"
|
||||||
|
|||||||
@@ -94,12 +94,11 @@ def main():
|
|||||||
"attack_url": 0,
|
"attack_url": 0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
# Get IPs with recent activity (last minute to match cron schedule)
|
# Get IPs flagged for reevaluation (set when a suspicious request arrives)
|
||||||
recent_accesses = db_manager.get_access_logs(limit=999999999, since_minutes=1)
|
ips_to_analyze = set(db_manager.get_ips_needing_reevaluation())
|
||||||
ips_to_analyze = {item["ip"] for item in recent_accesses}
|
|
||||||
|
|
||||||
if not ips_to_analyze:
|
if not ips_to_analyze:
|
||||||
app_logger.debug("[Background Task] analyze-ips: No recent activity, skipping")
|
app_logger.debug("[Background Task] analyze-ips: No IPs need reevaluation, skipping")
|
||||||
return
|
return
|
||||||
|
|
||||||
for ip in ips_to_analyze:
|
for ip in ips_to_analyze:
|
||||||
|
|||||||
Reference in New Issue
Block a user