Files
anonymisation/docs/beta/2026-06-25_audit-pret-beta-gui-v6.md
Domi31tls 6554a6d590 docs(beta): audit prêt-bêta GUI V6 + design de mise en prod bêta testeur
Audit prêt-pour-bêta de GUI V6 (3 axes : parcours/UX/PII, moteurs/OnnxTR/frozen,
licence/télémétrie/diffusion) → punch-list P0=7 / P1=11 / P2=6, findings vérifiés.

Design validé Dom : périmètre « A + chaîne prod », P0 + P1 honnêteté UI, toggles
« Données à détecter » câblés au moteur (gating par type + couplage rescan, pas de
plancher dur), licence souple + binding poste (D-20.4), −2 Go via excludes PyInstaller,
Linux-first TDD + un seul build Windows. Plus 3 exigences prod intégrées : diagnostics
logs auto-upload scrubbé (liste-blanche), mise à jour propre (installeur), modèles
embarqués hors-ligne. 6 chantiers (A-F), 2 repos. Diffusion = gate Dom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 16:43:04 +02:00

10 KiB
Raw Blame History

Audit prêt-pour-bêta — GUI V6 (HEAD 4b7c8db, 2026-06-25)

Périmètre : GUI V6 (gui_v6/) + chaîne prod (licence, télémétrie, diffusion) pour une mise en bêta testeur. Cible : un testeur installe l'EXE Windows → active sa licence → traite des PDF/images médicaux → ses stats remontent au portail.

Audit en 3 axes (parcours/UX/PII, moteurs/OnnxTR/frozen, licence/télémétrie/diffusion). Findings vérifiés dans le code (lignes citées) ; les 2 plus lourds recontrôlés par Claude.

À FAIRE par Dom : valider / élaguer cette liste avant tout code (cocher / barrer).


P0 — Bloqueurs bêta (à corriger avant toute diffusion)

P0-1 — Fuite PII silencieuse : la GUI ne fait pas fail-close si le NER obligatoire échoue

  • gui_v6/engine_bridge.py:222-231 appelle ensure_loaded() sans vérifier l'état ; si CamemBERT-bio ne charge pas (:173-175UNAVAILABLE, camembert=None), le traitement continue en regex + gazetteers seuls et le document sort compté « Réussi ». Le rescan résiduel ne couvre PAS les noms de personnes.
  • Le CLI de prod fait l'inverse : scripts/anonymize_cli.py:184-198 fail-close (code 3, aucune sortie) si le modèle obligatoire manque.
  • Impact : modèle ONNX corrompu/absent (antivirus, ressource manquante) → PDF qui paraît traité, statut OK, mais noms patients potentiellement en clair. Risque #1.
  • Fix : dans process_fn, si use_local_ner et état UNAVAILABLE → lever une exception explicite (refus de traitement), le runner la remonte en échec. Aligne la GUI sur la garantie code-3 du CLI.

P0-2 — Chaîne prod cassée : la GUI pointe http://localhost, jamais le portail réel

  • Pseudonymisation_Gui_V6.py:57AnonymisationApp() sans client → gui_v6/app.py:41 LicenseClient("http://localhost"). La télémétrie réutilise cette URL (app.py:199). Aucune env var / config / injection build ne fixe l'URL portail (grep exhaustif = 0).
  • Impact : sur un poste testeur, activation licence ET télémétrie POSTent sur localhost → rien n'écoute → activation impossible, stats jamais remontées. Toute la « chaîne prod » demandée est non fonctionnelle. Le portail tourne sur app.aivanov.eu (vérifié live, HTTPS 200, artefact servi).
  • Fix : injecter l'URL réelle (DEFAULT_PORTAL_URL="https://app.aivanov.eu" ou env var), localhost devient le secours dev. Impose un rebuild (l'EXE déjà publié est inutilisable pour l'activation).

P0-3 — Gain 2 Go non réalisé : optimum réintroduit torch en dur

  • requirements.txt:3 = optimum[onnxruntime]>=2.0.0 ; optimum déclare torch>=1.11 en dépendance cœur (vérifié). Le build installe requirements → torch dans le venv → PyInstaller le bundle malgré le retrait des hiddenimports (4b7c8db). EXE ~+2 Go.
  • optimum n'est importé que par ner_manager_onnx.py (legacy ONNX non câblé à la GUI), Pseudonymisation_Gui_V5.py (V5 legacy) et un script de finetune — jamais par gui_v6/ (le NER GUI = camembert_ner_manager en onnxruntime brut).
  • Fix : retirer optimum (et hiddenimports optimum*) du build frozen GUI ; valider « torch-free » par la taille EXE réelle + grep de l'arbo PyInstaller, pas par le diff du spec.

P0-4 — Build non reproductible : pas de précache des poids OnnxTR

  • anonymisation_gui_v6_onefile.spec:50-67 raise FileNotFoundError si les poids db_resnet50 + crnn_vgg16_bn absents du cache ; scripts/build_windows_oneclick.ps1 ne télécharge jamais ces poids. Le build « marche » seulement grâce au cache résiduel de la machine 192.168.1.11.
  • Fix : étape pré-PyInstaller : python -c "from onnxtr.models import ocr_predictor; ocr_predictor(det_arch='db_resnet50', reco_arch='crnn_vgg16_bn')" + doc.

P0-5 — Crash frozen ONNX probable : le hotfix CLI 6c6f653 non reporté au launcher GUI

  • Le launcher V6 ne pose pas ANON_SKIP_LEGACY_ONNX_MANAGER=1 (lu à l'import du core, anonymizer_core_refactored_onnx.py:211) et ne pré-charge pas CamemBERT — alors que le hotfix CLI (scripts/anonymize_cli.py:87) le fait pour éviter « cannot load module more than once per process » en frozen Windows.
  • Fix : poser le flag tout en haut de Pseudonymisation_Gui_V6.py + pré-charger CamemBERT avant le 1er import du core. À confirmer sur l'EXE (non reproductible en dev).

P0-6 — Contrôle de licence purement décoratif (décision Dom requise)

  • Enforcement nul : tab_usage.py:205 active « Lancer » selon le nombre de documents seulement ; aucune gate sur le statut licence (lu pour affichage uniquement).
  • Pas de vérif locale : license_client.py:208-213 lit license.json sans vérifier signature ni machine_id → fichier copiable sur N postes = N licences « actives ».
  • machine_id = MAC seule 12 chars (machine_id.py:12-14) = clonable, change en VM.
  • D-20.1 (fingerprint multi-composants) et D-20.4 (vérif locale machine_id) non implémentés. La bêta est une fenêtre idéale (aucune activation prod existante à migrer).
  • Décision Dom : niveau d'enforcement bêta (souple / dur / différé) — voir question.

P0-7 — Pas de protection multi-instance (lock perdu vs V5)

  • Pseudonymisation_Gui_V6.py:50-59 lance mainloop() sans file-lock msvcrt.locking (présent en V5). Double-clic testeur → 2 instances, 2 chargements ONNX, 2 écritures dans le même dossier sortie.
  • Fix : réintroduire le lock V5 + message « application déjà ouverte ».

P1 — Forte friction testeur / écarts (à arbitrer pour la bêta)

  • P1-1 — Dropzone décorative (tab_usage.py:103-115) : zone « déposez vos fichiers » sans glisser-déposer réel. Confusion dès la 1re étape. → câbler DnD (tkinterdnd2) ou retirer la métaphore.
  • P1-2 — 7 toggles « Données à détecter » non câblés (tab_config.py:351-357) : les interrupteurs (Noms, DDN, Établissements…) n'ont ni variable ni callback → aucun effet. Promesse UI non tenue. → câbler au moteur ou rendre informatifs (lecture seule).
  • P1-3 — Import/Export config désactivés « à venir » (tab_config.py:817-840) alors que le Partage est mis en avant. Régression vs workflow email V5. → câbler ou retirer.
  • P1-4 — config_path jamais transmis (app.py:155-162, tab_usage.py:53,176) : le dictionnaires.yml externe éditable n'est pas garanti chargé en frozen (pas de copie au 1er lancement). Personnalisations établissement potentiellement ignorées.
  • P1-5 — Erreurs/quarantaine sans pointeur (tab_usage.py:268,282) : « échec » / « quarantaine » sans indiquer où est le doc non livré (_*_failed/). → afficher le chemin sortie + bouton « ouvrir le dossier ».
  • P1-6 — Pas de validation d'inscriptibilité du dossier de sortie (processing_runner.py:214) : dossier en lecture seule → tout le lot échoue par doc avec message cryptique. → test d'écriture en amont.
  • P1-7 — Version app incohérente : gui_v6/__init__.py:11 = 6.0.0-g1 vs artefact/note bêta 2026.06.18.1203 (remonte aussi en télémétrie). → aligner sur le schéma de release.
  • P1-8 — Runbook non exécutable tel quel : republier impose un rebuild après P0-2 (le SHA de note-beta-client.md deviendra caduc). → ajouter étape « vérifier URL portail embarquée » au runbook.
  • P1-9 — Clé de signature licence = clé dev auto-générée (app_aivanov/app/signing.py) : KEY_ID « dev », paire RSA régénérée si volume recréé → licences invalidées. → provisionner une clé prod stable avant d'activer la vérif de signature côté client.
  • P1-10 — Politiques VM (D-20.2) / dossiers partagés (D-20.3) non implémentées (décision Dom, marquées « DECISION REQUIS » dans D-20). Acceptable de différer en bêta interne — à acter.
  • P1-11 — Audit JSONL original="docTR" (anonymizer_core_refactored_onnx.py:5356) : trace de provenance périmée (OCR = OnnxTR) visible dans le livrable d'audit. → "OnnxTR".

P2 — Polish (best-effort)

  • P2-1 — Étapes de progression statiques (tab_usage.py:144-147) : pastilles Extraction/Détection/Masquage/PDF décoratives ; pas de feedback intra-document.
  • P2-2 — Cartes format de sortie décoratives (tab_usage.py:118-127) : suggèrent un choix alors que la sortie est toujours PDF+TXT. → présenter comme « sorties produites ».
  • P2-3 — Commentaires/docstrings docTR périmés (anonymizer_core_refactored_onnx.py:55,102) : cosmétique, comportement OK.
  • P2-4 — os_info non envoyé à l'activation (license_client.py:161-164) : perte d'info diagnostique mineure.
  • P2-5 — Contact / canal de retour vide dans docs/beta/note-beta-client.md:36.
  • P2-6 — /api/v1/version ne déclenche pas de download (tab_about.py:164-178) : le testeur va manuellement sur le portail. Acceptable bêta.

Verdicts positifs (ne pas re-traiter)

  • Télémétrie RGPD : irréprochable. Double filtrage liste-blanche (usage_telemetry.py:72, _ALLOWED_DOC_KEYS) + recalcul serveur Pydantic strict. Aucun nom/chemin/texte ne peut fuiter. Non bloquante (thread daemon, timeout 4 s, spool best-effort). App utilisable hors-ligne.
  • Pipeline cœur fail-closed correct : quarantaine moteur → échec, SEUIL_RESCAN_RESIDUEL=0, redact_pdf_raster pas dans un except: pass. La fuite P0-1 vient du wiring GUI, pas du cœur.
  • engine_bridge / affichage moteurs : logique « n'afficher que les moteurs embarqués » saine et cohérente avec OnnxTR (OCR) + CamemBERT-bio (NER onnxruntime). 0 référence docTR dans les chaînes affichées à l'utilisateur.
  • Câblage ONNXTR_CACHE_DIR frozen : chemins concordants, fail-closed si OnnxTR absent.
  • Portail déployé et fonctionnel : app.aivanov.eu HTTPS, artefact servi, download authentifié (licence active requise). Le serveur n'est pas le problème — le client n'y est pas connecté.

Décompte : P0 = 7 · P1 = 11 · P2 = 6

Synthèse pour décision

Le « complet pour prod bêta » est plus que rebuild + ship : la chaîne prod est aujourd'hui non fonctionnelle bout-en-bout (P0-2 localhost, P0-6 licence décorative), il y a un risque PII #1 (P0-1 fail-open), et le 2 Go n'est pas acquis (P0-3 optimum). Rien de rédhibitoire : findings nets, fixes ciblés. C'est un vrai chantier de finition + câblage, pas une réécriture.