Compare commits

..

No commits in common. "master" and "v2026.05.13.9107009" have entirely different histories.

View file

@ -41,29 +41,6 @@ def _fetch_cve_html(cve_id: str) -> str:
return "" return ""
def _enrich_cve(cve_id: str) -> dict[str, str]:
"""Récupère la sévérité et le vecteur d'attaque depuis le HTML Debian Security Tracker."""
html = _fetch_cve_html(cve_id)
if not html:
return {"severity": "?", "vector": "?"}
result = {"severity": "?", "vector": "?"}
# Extraction de la sévérité depuis le bloc "Vulnerable and fixed packages"
# Recherche du motif: <td>remote</td> ou <td>local</td>
vector_match = re.search(r'<td[^>]*>\s*(remote|local)\s*</td>', html, re.IGNORECASE)
if vector_match:
result["vector"] = vector_match.group(1).lower()
# Extraction de la sévérité depuis les notes ou le tableau
# Format courant: [low], [medium], [high], [critical] dans les notes
severity_match = re.search(r'\b(unimportant|low|medium|high|critical)\b', html, re.IGNORECASE)
if severity_match:
result["severity"] = severity_match.group(1).lower()
return result
def _is_cve_actionable(cve_id: str, suite: str = "bookworm") -> bool: def _is_cve_actionable(cve_id: str, suite: str = "bookworm") -> bool:
"""Retourne True si la CVE est marquée 'fixed' pour le suite donné dans le HTML.""" """Retourne True si la CVE est marquée 'fixed' pour le suite donné dans le HTML."""
html = _fetch_cve_html(cve_id) html = _fetch_cve_html(cve_id)
@ -77,20 +54,16 @@ def _is_cve_actionable(cve_id: str, suite: str = "bookworm") -> bool:
return bool(pattern.search(html)) return bool(pattern.search(html))
def enrich_cves(cves: list[dict]) -> tuple[list[dict], int]: def filter_actionable_cves(cves: list[dict]) -> tuple[list[dict], int]:
"""Enrichit les CVEs avec fixable, severity, vector via l'API Debian. """Filtre la liste des CVE pour ajouter un flag 'fixable'.
Retourne (cve_enrichies, nombre_actionnables).""" Retourne (cve_avec_flag, nombre_actionnables)."""
if not cves: if not cves:
return [], 0 return [], 0
def check(cve: dict) -> dict: def check(cve: dict) -> dict:
try: try:
# Vérifie si corrigeable is_fixable = _is_cve_actionable(cve["id"])
cve["fixable"] = _is_cve_actionable(cve["id"]) cve["fixable"] = is_fixable
# Enrichit avec severity et vector
enriched = _enrich_cve(cve["id"])
cve["severity"] = enriched["severity"]
cve["vector"] = enriched["vector"]
return cve return cve
except Exception: except Exception:
cve["fixable"] = False cve["fixable"] = False
@ -246,21 +219,30 @@ def scan_apt(target: Target) -> tuple[bool, list[dict[str, str]], str]:
def scan_cve(target: Target) -> tuple[bool, list[dict[str, str]], str]: def scan_cve(target: Target) -> tuple[bool, list[dict[str, str]], str]:
# Format par défaut (pas de --format) retourne: CVE-ID package cmd = ["debsecan", "--format", "report", "--suite", "bookworm"] if target.is_host else ["pct", "exec", target.target_id, "--", "debsecan", "--format", "report", "--suite", "bookworm"]
cmd = ["debsecan", "--suite", "bookworm"] if target.is_host else ["pct", "exec", target.target_id, "--", "debsecan", "--suite", "bookworm"]
ok, stdout, stderr = run_cmd(cmd, timeout=120) ok, stdout, stderr = run_cmd(cmd, timeout=120)
if not ok: if not ok:
return False, [], stderr or stdout return False, [], stderr or stdout
cves = [] cves = []
for line in stdout.splitlines(): for line in stdout.splitlines():
# Format: CVE-XXXX-XXXX package [remote|local] [severity] - description
m = re.match(r"(CVE-\d{4}-\d+)\s+(\S+)", line) m = re.match(r"(CVE-\d{4}-\d+)\s+(\S+)", line)
if m: if m:
# Extraire tous les flags entre crochets
flags = re.findall(r"\[(\w+)\]", line)
vector = "?"
severity = "?"
for f in flags:
if f in ("remote", "local"):
vector = f
elif f in ("unimportant", "low", "medium", "high", "critical"):
severity = f
cves.append({ cves.append({
"id": m.group(1), "id": m.group(1),
"package": m.group(2), "package": m.group(2),
"vector": "?", "vector": vector,
"severity": "?", "severity": severity,
"url": f"https://security-tracker.debian.org/tracker/{m.group(1)}" "url": f"https://security-tracker.debian.org/tracker/{m.group(1)}"
}) })
return True, cves, "" return True, cves, ""
@ -291,9 +273,9 @@ def scan_target(target: Target, progress_cb: Callable) -> ScanResult:
if not cve_ok: if not cve_ok:
result.error = cve_err result.error = cve_err
# Enrichir les CVE via l'API Debian (severity, vector, fixable) # Filtrer les CVE actionnables via l'API Debian
if cve_list: if cve_list:
all_cves, actionable_count = enrich_cves(cve_list) all_cves, actionable_count = filter_actionable_cves(cve_list)
result.cve_list = all_cves result.cve_list = all_cves
result.cve_count = actionable_count result.cve_count = actionable_count
result.cve_total = len(all_cves) result.cve_total = len(all_cves)