39 lines
1.5 KiB
Python
39 lines
1.5 KiB
Python
"""
|
|
AV-Scan-Hook fuer Datei-Uploads (Konzept 4.3/6.6).
|
|
|
|
Bewusst als duenner Wrapper um ein optionales ClamAV (clamd) gehalten: ist
|
|
kein Scanner konfiguriert/erreichbar, wird das Ergebnis "skipped" vermerkt
|
|
statt die Datei stillschweigend als "sauber" zu markieren -- Admins sehen im
|
|
Audit-/Filetransfer-Log damit ehrlich, ob wirklich gescannt wurde.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import subprocess # nosec B404 -- benoetigt fuer den optionalen ClamAV-Aufruf, siehe unten
|
|
|
|
|
|
def scan_bytes(data: bytes) -> str:
|
|
"""Rueckgabe: 'clean', 'infected:<signature>' oder 'skipped:<grund>'."""
|
|
clamdscan = shutil.which("clamdscan")
|
|
if not clamdscan:
|
|
return "skipped:clamdscan_not_installed"
|
|
try:
|
|
# Argumentliste ist vollstaendig fest (kein shell=True, keine
|
|
# Nutzereingabe im Kommando selbst); die hochgeladenen Datei-Bytes
|
|
# werden ausschliesslich ueber stdin (input=data) uebergeben, nie als
|
|
# Kommandozeilen-/Pfadargument -- Command-Injection ueber Dateinamen
|
|
# o.ae. ist damit ausgeschlossen.
|
|
proc = subprocess.run( # nosec B603
|
|
[clamdscan, "--stdout", "--no-summary", "-"],
|
|
input=data, capture_output=True, timeout=30,
|
|
)
|
|
except (subprocess.TimeoutExpired, OSError):
|
|
return "skipped:scan_error"
|
|
|
|
output = proc.stdout.decode(errors="replace")
|
|
if proc.returncode == 0:
|
|
return "clean"
|
|
if proc.returncode == 1:
|
|
return f"infected:{output.strip()}"
|
|
return "skipped:scan_error"
|