DOCS/security/audit-manager-app.md
2026-09-04 16:48:04 +02:00

59 lines
11 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 manager-app — incohérences & pistes d'amélioration
Audit du 2026-07-13. Trois axes analysés : architecture/state management, écrans/composants (duplication), qualité transversale (i18n, constants, dead code, pubspec).
## Critique
- **Client généré désynchronisé (`manager_api_new/`)** : 153 fichiers modèle mais seulement 139 déclarés `part` dans `lib/api.dart`**14 fichiers orphelins** (`agenda_event_stat_dto`, `app_configuration_link_dto_application_instance`, `content_dto_resource`, `content_geo_point`, `day_stat_dto`, `game_stat_dto`, `level_dto`, `map_dto_map_provider`, `map_dto_map_type`, `map_dto_map_type_mapbox`, `poi_stat_dto`, `quiz_stat_dto`, `section_stat_dto`, `user`) qui déclarent `part of openapi.api;` sans être inclus → génère **183 des ~200 erreurs `flutter analyze`** (`mapValueOfType`/`mapDateTime`/classes indéfinies). La plupart sont du code mort (0 référence dans `lib/`), quelques-uns utilisés (`level_dto`, `poi_stat_dto`, `quiz_stat_dto`).
→ Résultat concret : `flutter analyze` est inexploitable (bruit noie les vraies erreurs). Nettoyer les orphelins morts, ajouter la ligne `part` manquante pour ceux utilisés. Cohérent avec la politique "pas de régénération OpenAPI" ([[feedback_manager_api_new_no_regen]]) — les résidus s'accumulent au fil des patches manuels, à surveiller à chaque édition de `manager_api_new/`.
## Haute
- **Duplication du shell de dialog** — tous les `showNewOrUpdate*.dart` (Parcours, Event, Map, Agenda, Quiz) répètent le même squelette : `Dialog` + `Container` + calcul `contentWidth/halfWidth/thirdWidth` recopié à l'identique + ligne boutons Annuler/Sauvegarder (15 occurrences). → extraire un `FormDialogScaffold({title, child, onSave, onCancel, widthFactor})`. ⚠️ **Mis à jour le 2026-09-04 — le périmètre de cette extraction a changé.** La refonte des écrans Configuration/Section (`DOCS/claude design/refonte-configuration-sections.html` §6) **supprime** les 8 `showNewOrUpdate*` : ils sont absorbés par le panneau d'édition en place de l'éditeur de collection. Extraire un scaffold pour eux serait refactoriser du code à jeter. Le scaffold reste utile, mais pour les dialogues qui **survivent** — traduction, nouvelle section, nouvelle configuration, sélecteur de couleur, fichiers PDF, catégories de carte. Et la coquille de référence existe déjà : `confirmation_dialog.dart`. **Ordre : absorber d'abord, extraire ensuite sur ce qui reste.**
- **Composants quasi-copiés** — `translation_input_container.dart` (365 l.) vs `translation_input_and_resource_container.dart` (347 l.) : ~80% identiques, mêmes méthodes aux mêmes numéros de ligne, seule différence la gestion du `resourceId`. Même schéma pour `multi_string_input_container.dart` vs `..._and_resource_container.dart`. → fusionner avec un flag `withResource`.
- **Méthodes/`build()` monolithiques** :
- `section_detail_screen.dart:455``save(bool isTraduction, AppContext)` fait ~440 lignes (455→894), logique métier (appels API, traduction, notifications) mêlée à l'UI.
- `app_configuration_link_screen.dart:82``build()` de ~640 lignes définissant 4 builders locaux (`_generalInfoCard`, `_phoneConfigCard`, `_previewHero`, `_previewCard`, 100-270 lignes chacun) recréés à chaque rebuild.
→ extraire la logique métier hors de l'UI, promouvoir les builders en méthodes/sous-widgets de la classe.
- **i18n contournée** — infra l10n complète (fr/nl/en, 373 clés) mais seulement 52 fichiers/120 utilisent `AppLocalizations`. ~100+ chaînes françaises en dur, ex. `main_screen.dart:610/617/626` (`'Application mobile non configurée'` etc.), `statistics_screen.dart:491`, `web_app_screen.dart:129`, `map_geometry_picker.dart:139`.
## Moyenne
- **Sécurité — session locale** (`Helpers/FileHelper.dart:15-16,32-33`) : clé AES **en dur dans le source** + IV fixe (`IV.fromLength(16)` = zéros) → annule la sécurité d'AES-CBC. Incohérence supplémentaire : `writeSession` chiffre le password mais `readSessionWeb` (chemin actif) fait `Session.fromJson` **sans déchiffrer** — le chiffrement ne sert donc à rien (l'auto-login passe par le token). → retirer le chiffrement mort ou passer à IV aléatoire + clé hors source.
- **Erreurs API avalées silencieusement** :
- `main_screen.dart:93` et `:186``catch (_) {}` sur `subscriptionPlanGet`/`instanceGet`, aucun feedback utilisateur.
- `Resources/show_resource_popup.dart:128``resourceApi!.resourceDelete(...)` sans try/catch (notif de succès peut s'afficher à tort) ; `:139` `catch (e) {}` vide.
- `main.dart:213-221` — token expiré → `null` silencieux, redirection `/login` commentée (`// todo`).
- `Helpers/FileHelper.dart:60-64``importConfiguration` ne gère que le `409`, toute autre erreur avalée.
- **`_localPath` cassé** (`Helpers/FileHelper.dart:19-23`) : `var directory; return directory.path;``directory` toujours null → `NoSuchMethodError` si appelé. Mort sur web (branche `!kIsWeb`) mais casserait sur desktop.
- **Nettoyage fichiers** :
- 2 fichiers morts avec espaces dans le nom (non conforme, fragile multiplateforme) : `Quizz/new_update_response_quizz - deprecated.dart`, `Quizz/new_update_score_quizz - deprecated.dart`.
- `lib/api/openApiTest.dart` jamais référencé (surtout du code commenté).
- `dart analyze` : ~23 imports inutilisés (`auto_size_text`, `dart:convert`, `dart:js_interop`... dans une dizaine de fichiers), dead code (`get_element_for_resource.dart:32/53`, `FileHelper.dart:25` `_localFile`, `policy_screen.dart:19/64`).
- **TODO visibles à l'écran** (placeholders livrés en prod) : `Components/upload_audio_container.dart:141` `"TODO player affiché ou pas ?"`, `Components/upload_online_resources_container.dart:59` `"LOADING TODO FRAISE"`, `Main/main_screen.dart:629` `"TODO vr"`.
- **`pubspec.yaml`** : dépendances inutilisées (`drag_and_drop_lists`, `material_segmented_control`, `multiselect_formfield` — doublon de `multi_select_flutter` réellement utilisé —, `tab_container`, `convert`) ; contraintes de version vides (`rxdart:`, `provider:`, `collection: any`).
- **Deep-clone incohérent** — `jsonDecode(jsonEncode(...))` répété ~13 fois dans les dialogs (`showNewOrUpdateGuidedPath.dart:25`, `showNewOrUpdateEventAgenda.dart:19`, `showNewOrUpdateGeoPoint.dart:20`, `showNewOrUpdateQuizQuestion.dart:41`...), mais fait "à la main" champ par champ dans `showNewOrUpdateProgrammeBlock.dart:16-31` — incohérence pour un besoin identique. → helper `deepCopyDto<T>` unique.
- **Constants dupliquées / mortes** : `statistics_screen.dart:395/398` redéfinit `Color(0xFF264863)`/`Color(0xFFC2C9D6)` qui dupliquent `kPrimaryColor`/`kSecond` de `constants.dart` ; `constants.dart` contient lui-même du code mort (`// TO FILL WIT CORRECT COLOR`, `kBackgroundColor` commenté, bloc `kTextStyle` entièrement commenté).
- **Signatures de callbacks incohérentes entre dialogs** — `showNewOrUpdateGeoPoint.dart:16` utilise `Function getResult` non typé avec un ordre d'arguments différent des autres (`showNewOrUpdateEventAgenda.dart:12-17`, `showNewOrUpdateProgrammeBlock.dart:11-15`) ; `onSave` tantôt `Function(X)`, tantôt `FutureOr<void> Function(X)`.
## Basse
- **`client.dart`** : 18 façades API (`SectionApi?`, `ResourceApi?`...) typées nullable alors qu'assignées inconditionnellement dans le constructeur, jamais renullifiées → pattern `clientAPI!.xApi!.method()` répété 54 fois dans 18 fichiers, bruit qui masque le vrai point de nullabilité (`ManagerAppContext.clientAPI`). → passer les façades en `final` non-nullable.
- **`ManagerAppContext`** (`Models/managerContext.dart:7`) mixe `ChangeNotifier` mais `notifyListeners()` n'est jamais appelé dessus (le vrai notifier est `AppContext`) — mixin trompeur/mort. Champ `isLoading` porté mais jamais utilisé (loading géré en `setState` local ailleurs).
- **58 `print()`** de debug oubliés (dump DTO dans `app_configuration_link_screen.dart:268-289`, `"error auth"` dans `login_screen.dart`, `"Refresh UI"` dans `map_config.dart`/`menu_config.dart`/`quizz_config.dart`...) alors que `log()` (`dart:developer`) n'est utilisé qu'une fois (`configuration_detail_screen.dart:385`) → incohérence, à unifier derrière `kDebugMode`.
- **`login_screen.dart:172,211-212`** : host localhost et credentials de test codés en dur dans `initState` pour le cas debug local.
- **`configuration_detail_screen.dart:371/380`** : `configurationExport(config.id!)` appelé deux fois (résultat de l'appel ligne 371 ignoré).
- **Listes sans keys stables** : `showNewOrUpdateProgrammeBlock.dart:255` `ListView.builder` sans key par item ; `showNewOrUpdateGuidedPath.dart:297` contourne avec une clé de liste globale (`stepsRevision++`) plutôt que des `ValueKey(step.id)` par item.
- **Nommage non conforme aux conventions Dart** (camelCase/PascalCase au lieu de snake_case) : fichiers (`managerContext.dart`, `FileHelper.dart`, `PDFHelper.dart`, `dropDown_input_container.dart`, `dropDown_input_container_categories.dart`, `dropDown_configuration.dart`, `listView_card_*.dart` ×4, `new_update_pdfFile.dart`, `PDF_config.dart`, tous les `showNewOrUpdate*.dart`, `openApiTest.dart`, `section_reorderList.dart`) et dossiers (`Screens/`, `Components/`, `Models/`, `Helpers/`, `Services/`).
- **Composants de sélection qui se recouvrent** : `single_choice_input_container.dart`, `single_select_container.dart`, `dropDown_input_container.dart`, `segmented_enum_input_container.dart` couvrent tous "choisir une valeur dans une liste" — candidats à consolidation.
- **Incohérence StatefulWidget vs Provider** : 45 écrans en `StatefulWidget`, 37 en Provider/`context.watch`, souvent mélangés dans le même écran (ex. `app_configuration_link_screen` mixe `Provider.of` + `setState` + `StatefulBuilder`). Pas un bug, absence de convention documentée.
- **`analysis_options.yaml`** minimal (seulement `flutter_lints` par défaut, aucune règle additionnelle activée) — laisse passer imports inutilisés et `print()` sans avertissement.
- **`Models/Model.dart:33`** : `return credential!;` juste après un `if (credential != null)` qui peut ne pas être pris → crash potentiel si null.
- **`app_context.dart:10`** : `getContext()` sans type de retour explicite, forçant les appelants à faire `as ManagerAppContext` partout.
## Notes complémentaires
- Les fichiers générés `lib/l10n/app_localizations*.dart` ont été modifiés (Jul 10) après les `.arb` sources (Jul 9) — édition manuelle de fichiers générés l10n, à surveiller (risque d'écrasement à la prochaine génération, même logique de risque que `manager_api_new/`).
- `test/widget_test.dart` est cassé (`missing_required_argument` sur `managerAppContext`/`router`/`session`) — le seul test du projet ne compile pas.
- `Components/vlc_viewer.dart` (`DartVLC`) utilisé alors que `dart_vlc` est commenté dans `pubspec.yaml` — dépendance ambiguë à vérifier.