"""Declared malware-scan posture for the upload safety gate (ADR-0011). The scan layer auto-detects ClamAV, which made the deployed posture *implicit*: whether an upload was genuinely malware-scanned depended on whatever happened to be on the host's PATH. A dev Mac with ClamAV recorded ``clean``; the HuggingFace Space, which has no AV binary, recorded ``skipped`` — so local testing masked production behaviour. ``deploy/scan_posture.yaml`` makes the posture an explicit, reviewable config value. This module reads it; :mod:`src.uploads.scanning` acts on it. """ from __future__ import annotations import os from pathlib import Path POSTURE_STRUCTURAL_ONLY = "structural_only" POSTURE_AV_REQUIRED = "av_required" VALID_POSTURES = frozenset({POSTURE_STRUCTURAL_ONLY, POSTURE_AV_REQUIRED}) # repo_root/deploy/scan_posture.yaml (src/uploads/posture.py -> up 3). POSTURE_FILE = Path(__file__).resolve().parents[2] / "deploy" / "scan_posture.yaml" _TRUTHY = {"1", "true", "yes", "on"} def declared_posture() -> str: """Return the deployment's declared posture. ``UPLOAD_SCAN_POSTURE`` overrides the file (local experiments / CI). An unreadable, missing, or unrecognised value degrades to ``structural_only`` — the honest default, since claiming AV coverage we cannot prove is the failure mode this whole module exists to prevent. """ env = os.environ.get("UPLOAD_SCAN_POSTURE", "").strip().lower() if env in VALID_POSTURES: return env try: import yaml loaded = yaml.safe_load(POSTURE_FILE.read_text(encoding="utf-8")) or {} value = str(loaded.get("posture", "")).strip().lower() except Exception: # noqa: BLE001 — missing/malformed config must not break uploads return POSTURE_STRUCTURAL_ONLY return value if value in VALID_POSTURES else POSTURE_STRUCTURAL_ONLY def av_required() -> bool: """True when a missing/failed AV pass must fail closed. ``UPLOAD_SCAN_REQUIRED`` (the pre-existing env knob) still forces this on regardless of the declared posture. """ if os.environ.get("UPLOAD_SCAN_REQUIRED", "").strip().lower() in _TRUTHY: return True return declared_posture() == POSTURE_AV_REQUIRED