47 lines
1.5 KiB
Python
47 lines
1.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Flagging rules for the intrusion-detection system.
|
|
|
|
Detection functions that identify hostile IPs based on aggregated stats.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import bad_patterns
|
|
from models import IPStats
|
|
|
|
|
|
def flag_scanners(stats: dict[str, IPStats], min_errors: int = 5,
|
|
min_ratio: float = 0.5) -> dict[str, IPStats]:
|
|
"""IPs with many errors AND a high error ratio."""
|
|
return {
|
|
ip: s for ip, s in stats.items()
|
|
if s.errors >= min_errors and s.error_ratio >= min_ratio
|
|
}
|
|
|
|
|
|
def flag_denied_only(stats: dict[str, IPStats],
|
|
min_denied: int = 1) -> dict[str, IPStats]:
|
|
"""IPs that only ever hit denied paths and never authenticated."""
|
|
return {
|
|
ip: s for ip, s in stats.items()
|
|
if s.denied >= min_denied and not s.authenticated
|
|
}
|
|
|
|
|
|
def flag_malicious_patterns(stats: dict[str, IPStats],
|
|
min_severity: int = 2,
|
|
min_hits: int = 1) -> dict[str, IPStats]:
|
|
"""IPs with hits against malicious path patterns at or above severity threshold."""
|
|
return {
|
|
ip: s for ip, s in stats.items()
|
|
if s.malicious_hits >= min_hits and any(
|
|
bad_patterns.PATTERNS_BY_NAME[name].severity >= min_severity
|
|
for name in s.malicious_patterns
|
|
)
|
|
}
|
|
|
|
|
|
def by_total(stats: dict[str, IPStats]) -> list[IPStats]:
|
|
"""Stats sorted by request count, descending."""
|
|
return sorted(stats.values(), key=lambda s: (-s.total, s.ip))
|