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

162 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-175``UNAVAILABLE`, `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:57``AnonymisationApp()` 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.