start project
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
"""Severity scoring (Chapter 10): a simple, transparent rank-escalation
|
||||
model — no ML, easy to explain to a non-expert user (Ch10's own requirement).
|
||||
|
||||
Chapter 07 assigns a PROVISIONAL severity per rule type at parse time.
|
||||
This module computes an EFFECTIVE severity by escalating that base rank
|
||||
for repeated/rapid hits from the same IP — the exact rule Ch10 names.
|
||||
|
||||
Escalation is rank-based and strictly non-decreasing: it starts at the
|
||||
base severity's rank and only ever moves up (repeat-count / hit-rate
|
||||
bonuses), clamped at "high". An earlier weighted-score-vs-fixed-threshold
|
||||
version could silently *downgrade* an isolated high-severity event (e.g.
|
||||
a single sqlmap hit) to "medium" purely because the thresholds weren't
|
||||
calibrated to the base weights — caught by running the pipeline against
|
||||
real sample data. A single dangerous event must never end up rated below
|
||||
its own base severity; only repetition/rate should push it higher.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
_SEVERITY_RANKS = ["low", "medium", "high"]
|
||||
_RANK_BY_SEVERITY = {name: rank for rank, name in enumerate(_SEVERITY_RANKS)}
|
||||
|
||||
_REPEAT_COUNT_HIGH = 20
|
||||
_REPEAT_COUNT_MEDIUM = 5
|
||||
_RAPID_AVG_INTERVAL_SECONDS = 60 # >1 event/minute from one IP suggests automation
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SeverityInputs:
|
||||
base_severity: str
|
||||
ip_event_count: int
|
||||
avg_interval_seconds: float | None # None if this IP has < 2 events in the window
|
||||
|
||||
|
||||
def compute_effective_severity(inputs: SeverityInputs) -> str:
|
||||
"""Two named, inspectable escalation rules: repeat-count and hit-rate."""
|
||||
rank = _RANK_BY_SEVERITY.get(inputs.base_severity, 0)
|
||||
|
||||
if inputs.ip_event_count >= _REPEAT_COUNT_HIGH:
|
||||
rank += 2
|
||||
elif inputs.ip_event_count >= _REPEAT_COUNT_MEDIUM:
|
||||
rank += 1
|
||||
|
||||
if inputs.avg_interval_seconds is not None and inputs.avg_interval_seconds < _RAPID_AVG_INTERVAL_SECONDS:
|
||||
rank += 1
|
||||
|
||||
rank = min(rank, len(_SEVERITY_RANKS) - 1)
|
||||
return _SEVERITY_RANKS[rank]
|
||||
Reference in New Issue
Block a user