Refactor ASN scoring logic and entity routes; add new frontend utilities

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
SOC Analyst
2026-03-20 10:00:06 +01:00
parent bd33fbad01
commit 799e8f1c1e
16 changed files with 161 additions and 126 deletions

View File

@ -8,6 +8,19 @@ from ..models import DetectionsListResponse, Detection
router = APIRouter(prefix="/api/detections", tags=["detections"])
# Mapping label ASN → score float (0 = très suspect, 1 = légitime)
_ASN_LABEL_SCORES: dict[str, float] = {
'human': 0.9, 'bot': 0.05, 'proxy': 0.25, 'vpn': 0.3,
'tor': 0.1, 'datacenter': 0.4, 'scanner': 0.05, 'malicious': 0.05,
}
def _label_to_score(label: str) -> float | None:
"""Convertit un label de réputation ASN en score numérique."""
if not label:
return None
return _ASN_LABEL_SCORES.get(label.lower(), 0.5)
@router.get("", response_model=DetectionsListResponse)
async def get_detections(
@ -154,12 +167,6 @@ async def get_detections(
params["offset"] = offset
gresult = db.query(grouped_query, params)
def _label_to_score(label: str) -> float | None:
if not label: return None
mapping = {'human': 0.9, 'bot': 0.05, 'proxy': 0.25, 'vpn': 0.3,
'tor': 0.1, 'datacenter': 0.4, 'scanner': 0.05, 'malicious': 0.05}
return mapping.get(label.lower(), 0.5)
detections = []
for row in gresult.result_rows:
# row: src_ip, first_seen, last_seen, detection_count, unique_ja4s, unique_hosts,
@ -252,21 +259,6 @@ async def get_detections(
params["offset"] = offset
result = db.query(main_query, params)
def _label_to_score(label: str) -> float | None:
if not label:
return None
mapping = {
'human': 0.9,
'bot': 0.05,
'proxy': 0.25,
'vpn': 0.3,
'tor': 0.1,
'datacenter': 0.4,
'scanner': 0.05,
'malicious': 0.05,
}
return mapping.get(label.lower(), 0.5)
detections = [
Detection(

View File

@ -39,7 +39,7 @@ def get_entity_stats(entity_type: str, entity_value: str, hours: int = 24) -> Op
GROUP BY entity_type, entity_value
"""
result = db.connect().query(query, {
result = db.query(query, {
'entity_type': entity_type,
'entity_value': entity_value,
'hours': hours
@ -73,7 +73,7 @@ def get_related_attributes(entity_type: str, entity_value: str, hours: int = 24)
(SELECT groupUniqArrayArray(countries) FROM mabase_prod.view_dashboard_entities WHERE entity_type = %(entity_type)s AND entity_value = %(entity_value)s AND log_date >= toDate(now() - INTERVAL %(hours)s HOUR) AND notEmpty(countries)) as countries
"""
result = db.connect().query(query, {
result = db.query(query, {
'entity_type': entity_type,
'entity_value': entity_value,
'hours': hours
@ -120,7 +120,7 @@ def get_array_values(entity_type: str, entity_value: str, array_field: str, hour
ORDER BY count DESC
"""
result = db.connect().query(query, {
result = db.query(query, {
'entity_type': entity_type,
'entity_value': entity_value,
'hours': hours
@ -318,6 +318,26 @@ async def get_subnet_investigation(
raise HTTPException(status_code=500, detail=f"Erreur: {str(e)}")
@router.get("/types")
async def get_entity_types():
"""
Retourne la liste des types d'entités supportés.
NOTE: Cette route DOIT être déclarée avant /{entity_type}/... pour ne pas être masquée.
"""
return {
"entity_types": sorted(VALID_ENTITY_TYPES),
"descriptions": {
"ip": "Adresse IP source",
"ja4": "Fingerprint JA4 TLS",
"user_agent": "User-Agent HTTP",
"client_header": "Client Header",
"host": "Host HTTP",
"path": "Path URL",
"query_param": "Query Param"
}
}
@router.get("/{entity_type}/{entity_value:path}", response_model=EntityInvestigation)
async def get_entity_investigation(
entity_type: str,
@ -340,10 +360,10 @@ async def get_entity_investigation(
- Query-Params
"""
# Valider le type d'entité
if entity_type not in ENTITY_TYPES:
if entity_type not in VALID_ENTITY_TYPES:
raise HTTPException(
status_code=400,
detail=f"Type d'entité invalide. Types supportés: {', '.join(ENTITY_TYPES.keys())}"
detail=f"Type d'entité invalide. Types supportés: {', '.join(VALID_ENTITY_TYPES)}"
)
# Stats générales
@ -385,10 +405,10 @@ async def get_entity_related(
"""
Récupère uniquement les attributs associés à une entité
"""
if entity_type not in ENTITY_TYPES:
if entity_type not in VALID_ENTITY_TYPES:
raise HTTPException(
status_code=400,
detail=f"Type d'entité invalide. Types supportés: {', '.join(ENTITY_TYPES.keys())}"
detail=f"Type d'entité invalide. Types supportés: {', '.join(VALID_ENTITY_TYPES)}"
)
related = get_related_attributes(entity_type, entity_value, hours)
@ -410,7 +430,7 @@ async def get_entity_user_agents(
"""
Récupère les User-Agents associés à une entité
"""
if entity_type not in ENTITY_TYPES:
if entity_type not in VALID_ENTITY_TYPES:
raise HTTPException(status_code=400, detail="Type d'entité invalide")
user_agents = get_array_values(entity_type, entity_value, 'user_agents', hours)
@ -432,7 +452,7 @@ async def get_entity_client_headers(
"""
Récupère les Client-Headers associés à une entité
"""
if entity_type not in ENTITY_TYPES:
if entity_type not in VALID_ENTITY_TYPES:
raise HTTPException(status_code=400, detail="Type d'entité invalide")
client_headers = get_array_values(entity_type, entity_value, 'client_headers', hours)
@ -454,7 +474,7 @@ async def get_entity_paths(
"""
Récupère les Paths associés à une entité
"""
if entity_type not in ENTITY_TYPES:
if entity_type not in VALID_ENTITY_TYPES:
raise HTTPException(status_code=400, detail="Type d'entité invalide")
paths = get_array_values(entity_type, entity_value, 'paths', hours)
@ -476,7 +496,7 @@ async def get_entity_query_params(
"""
Récupère les Query-Params associés à une entité
"""
if entity_type not in ENTITY_TYPES:
if entity_type not in VALID_ENTITY_TYPES:
raise HTTPException(status_code=400, detail="Type d'entité invalide")
query_params = get_array_values(entity_type, entity_value, 'query_params', hours)
@ -487,22 +507,3 @@ async def get_entity_query_params(
"query_params": query_params,
"total": len(query_params)
}
@router.get("/types")
async def get_entity_types():
"""
Retourne la liste des types d'entités supportés
"""
return {
"entity_types": list(ENTITY_TYPES.values()),
"descriptions": {
"ip": "Adresse IP source",
"ja4": "Fingerprint JA4 TLS",
"user_agent": "User-Agent HTTP",
"client_header": "Client Header",
"host": "Host HTTP",
"path": "Path URL",
"query_param": "Query Param"
}
}