docs: add standardized comments to all services (Python, Go, Bash)
- Add docs/commenting-standard.md defining per-language comment standards (Go godoc, Python PEP-257, C Doxygen, Bash header blocks, SQL banners) - services/dashboard: 100% docstring coverage (100/100 functions) - All FastAPI route handlers, helpers, classes, and models documented - Language: French (project convention) - services/bot-detector: 100% docstring coverage (53/53 symbols) - bot_detector.py: 14 functions + module docstring - anubis/fetch_rules.py: 9 functions - shared/python/ja4_common: full docstrings on ClickHouseClient (7 methods) and ClickHouseSettings class - services/correlator: 24 godoc comments added across 6 Go files - correlation_service.go: 10 private helpers - unixsocket/source.go: 6 parsing/socket helpers - correlated_log.go: 4 field extraction helpers - orchestrator.go, logger.go, main.go: 4 comments - services/correlator/scripts/audit-architecture.sh: standardized header block Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@ -1 +1 @@
|
||||
# Backend package
|
||||
"""Package principal du backend FastAPI bot-detector."""
|
||||
|
||||
@ -5,6 +5,7 @@ from pydantic_settings import BaseSettings
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
"""Paramètres de configuration de l'application chargés depuis l'environnement."""
|
||||
# ClickHouse
|
||||
CLICKHOUSE_HOST: str = "clickhouse"
|
||||
CLICKHOUSE_PORT: int = 8123
|
||||
@ -22,6 +23,7 @@ class Settings(BaseSettings):
|
||||
CORS_ORIGINS: list = ["http://localhost:3000", "http://127.0.0.1:3000"]
|
||||
|
||||
class Config:
|
||||
"""Configuration Pydantic pour le chargement du fichier .env."""
|
||||
env_file = ".env"
|
||||
case_sensitive = True
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ from enum import Enum
|
||||
|
||||
|
||||
class ThreatLevel(str, Enum):
|
||||
"""Niveaux de menace supportés par le modèle de détection."""
|
||||
CRITICAL = "CRITICAL"
|
||||
HIGH = "HIGH"
|
||||
MEDIUM = "MEDIUM"
|
||||
@ -19,6 +20,7 @@ class ThreatLevel(str, Enum):
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class MetricsSummary(BaseModel):
|
||||
"""Résumé agrégé des métriques sur les dernières 24 heures."""
|
||||
total_detections: int
|
||||
critical_count: int
|
||||
high_count: int
|
||||
@ -30,6 +32,7 @@ class MetricsSummary(BaseModel):
|
||||
|
||||
|
||||
class TimeSeriesPoint(BaseModel):
|
||||
"""Point de série temporelle par heure pour les métriques."""
|
||||
hour: datetime
|
||||
total: int
|
||||
critical: int
|
||||
@ -39,6 +42,7 @@ class TimeSeriesPoint(BaseModel):
|
||||
|
||||
|
||||
class MetricsResponse(BaseModel):
|
||||
"""Réponse complète des métriques du dashboard avec série temporelle."""
|
||||
summary: MetricsSummary
|
||||
timeseries: List[TimeSeriesPoint]
|
||||
threat_distribution: Dict[str, int]
|
||||
@ -49,6 +53,7 @@ class MetricsResponse(BaseModel):
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class Detection(BaseModel):
|
||||
"""Représentation d'une détection d'anomalie émise par le modèle ML."""
|
||||
detected_at: datetime
|
||||
src_ip: str
|
||||
ja4: str
|
||||
@ -82,6 +87,7 @@ class Detection(BaseModel):
|
||||
|
||||
|
||||
class DetectionsListResponse(BaseModel):
|
||||
"""Liste paginée de détections d'anomalies."""
|
||||
items: List[Detection]
|
||||
total: int
|
||||
page: int
|
||||
@ -94,6 +100,7 @@ class DetectionsListResponse(BaseModel):
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class AttributeValue(BaseModel):
|
||||
"""Valeur d'attribut avec comptage, pourcentage et métadonnées temporelles."""
|
||||
value: str
|
||||
count: int
|
||||
percentage: float
|
||||
@ -105,6 +112,7 @@ class AttributeValue(BaseModel):
|
||||
|
||||
|
||||
class VariabilityAttributes(BaseModel):
|
||||
"""Ensemble des attributs de variabilité comportementale pour une entité."""
|
||||
user_agents: List[AttributeValue] = Field(default_factory=list)
|
||||
ja4: List[AttributeValue] = Field(default_factory=list)
|
||||
countries: List[AttributeValue] = Field(default_factory=list)
|
||||
@ -115,11 +123,13 @@ class VariabilityAttributes(BaseModel):
|
||||
|
||||
|
||||
class Insight(BaseModel):
|
||||
"""Message d'analyse contextuelle (alerte, information ou succès)."""
|
||||
type: str # "warning", "info", "success"
|
||||
message: str
|
||||
|
||||
|
||||
class VariabilityResponse(BaseModel):
|
||||
"""Réponse d'analyse de variabilité pour un attribut donné."""
|
||||
type: str
|
||||
value: str
|
||||
total_detections: int
|
||||
@ -134,11 +144,13 @@ class VariabilityResponse(BaseModel):
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class AttributeListItem(BaseModel):
|
||||
"""Élément de la liste des valeurs uniques d'un attribut avec son comptage."""
|
||||
value: str
|
||||
count: int
|
||||
|
||||
|
||||
class AttributeListResponse(BaseModel):
|
||||
"""Réponse de la liste des valeurs uniques pour un type d'attribut."""
|
||||
type: str
|
||||
items: List[AttributeListItem]
|
||||
total: int
|
||||
@ -149,6 +161,7 @@ class AttributeListResponse(BaseModel):
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class UserAgentValue(BaseModel):
|
||||
"""Valeur de User-Agent avec comptage et plage temporelle d'observation."""
|
||||
value: str
|
||||
count: int
|
||||
percentage: float
|
||||
@ -157,6 +170,7 @@ class UserAgentValue(BaseModel):
|
||||
|
||||
|
||||
class UserAgentsResponse(BaseModel):
|
||||
"""Réponse de la liste des User-Agents associés à une entité."""
|
||||
type: str
|
||||
value: str
|
||||
user_agents: List[UserAgentValue]
|
||||
@ -169,12 +183,14 @@ class UserAgentsResponse(BaseModel):
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
class ClassificationLabel(str, Enum):
|
||||
"""Étiquettes de classification SOC pour les IPs et fingerprints JA4."""
|
||||
LEGITIMATE = "legitimate"
|
||||
SUSPICIOUS = "suspicious"
|
||||
MALICIOUS = "malicious"
|
||||
|
||||
|
||||
class ClassificationBase(BaseModel):
|
||||
"""Modèle de base partagé pour les classifications SOC."""
|
||||
ip: Optional[str] = None
|
||||
ja4: Optional[str] = None
|
||||
label: ClassificationLabel
|
||||
@ -198,6 +214,7 @@ class Classification(ClassificationBase):
|
||||
|
||||
|
||||
class ClassificationsListResponse(BaseModel):
|
||||
"""Liste paginée des classifications SOC enregistrées."""
|
||||
items: List[Classification]
|
||||
total: int
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
# Routes package
|
||||
"""Package des routes FastAPI de l'API bot-detector."""
|
||||
|
||||
@ -374,6 +374,7 @@ async def analyze_user_agents(ip: str):
|
||||
|
||||
# Classification des UAs
|
||||
def classify_ua(ua: str) -> str:
|
||||
"""Classe un User-Agent en 'bot', 'script', 'browser' ou 'unknown'."""
|
||||
ua_lower = ua.lower()
|
||||
if any(bot in ua_lower for bot in ['bot', 'crawler', 'spider', 'curl', 'wget', 'python', 'requests', 'scrapy']):
|
||||
return 'bot'
|
||||
|
||||
@ -10,6 +10,7 @@ router = APIRouter(prefix="/api/botnets", tags=["botnets"])
|
||||
|
||||
|
||||
def _botnet_class(unique_countries: int) -> str:
|
||||
"""Classifie un JA4 selon sa dispersion géographique."""
|
||||
if unique_countries > 100:
|
||||
return "global_botnet"
|
||||
if unique_countries > 20:
|
||||
|
||||
@ -222,6 +222,7 @@ def _run_clustering_job(k: int, hours: int, sensitivity: float = 1.0) -> None:
|
||||
continue
|
||||
|
||||
def avg_f(key: str, crows: list[dict] = cluster_rows[j]) -> float:
|
||||
"""Calcule la moyenne flottante d'un champ numérique sur les lignes du cluster."""
|
||||
return float(np.mean([float(r.get(key) or 0) for r in crows]))
|
||||
|
||||
mean_ttl = avg_f("ttl")
|
||||
@ -245,6 +246,7 @@ def _run_clustering_job(k: int, hours: int, sensitivity: float = 1.0) -> None:
|
||||
orgs = [str(r.get("asn_org") or "") for r in cluster_rows[j] if r.get("asn_org")]
|
||||
|
||||
def topk(lst: list[str], n: int = 5) -> list[str]:
|
||||
"""Retourne les n valeurs les plus fréquentes d'une liste (valeurs vides exclues)."""
|
||||
return [v for v, _ in Counter(lst).most_common(n) if v]
|
||||
|
||||
radar = [
|
||||
|
||||
@ -489,6 +489,7 @@ async def get_ua_analysis(
|
||||
|
||||
|
||||
def _build_ua_risk_flags(ua: str, ua_type: str, unique_ja4s: int, ip_count: int) -> list:
|
||||
"""Construit la liste des indicateurs de risque pour un User-Agent."""
|
||||
flags = []
|
||||
if ua_type == "bot":
|
||||
flags.append("ua_bot_signature")
|
||||
|
||||
@ -144,6 +144,7 @@ async def get_metrics_baseline():
|
||||
row = r.result_rows[0] if r.result_rows else None
|
||||
|
||||
def pct_change(today: int, yesterday: int) -> float:
|
||||
"""Calcule la variation en pourcentage entre aujourd'hui et hier. Retourne 100 si hier=0 et aujourd'hui>0."""
|
||||
if yesterday == 0:
|
||||
return 100.0 if today > 0 else 0.0
|
||||
return round((today - yesterday) / yesterday * 100, 1)
|
||||
|
||||
@ -11,6 +11,7 @@ router = APIRouter(prefix="/api/ml", tags=["ml_features"])
|
||||
|
||||
def _attack_type(fuzzing_index: float, hit_velocity: float,
|
||||
is_fake_nav: int, ua_ch_mismatch: int) -> str:
|
||||
"""Déduit le type d'attaque depuis les métriques comportementales."""
|
||||
if fuzzing_index > 50:
|
||||
return "brute_force"
|
||||
if hit_velocity > 1.0:
|
||||
@ -113,6 +114,7 @@ async def get_ip_radar(ip: str):
|
||||
row = result.result_rows[0]
|
||||
|
||||
def _f(v) -> float:
|
||||
"""Convertit une valeur nullable en float (None ou falsy → 0.0)."""
|
||||
return float(v or 0)
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user