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

11 KiB
Raw Blame History

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.dart14 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éstranslation_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:455save(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:82build() 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 :186catch (_) {} sur subscriptionPlanGet/instanceGet, aucun feedback utilisateur.
    • Resources/show_resource_popup.dart:128resourceApi!.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-64importConfiguration 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érentjsonDecode(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 dialogsshowNewOrUpdateGeoPoint.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.