labfy-investigation/docs/tickets/closed/TICKET-039.md
2026-07-17 22:51:22 +02:00

515 lines
13 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.

# TICKET-039 — Catalogue initial des outils externes et détection de leurs versions
## Statut
À faire
## Priorité
Haute
## Objectif
Créer le catalogue initial des outils externes utilisés par Labfy Investigation et fournir une détection fiable de leur version.
Ce ticket complète les modules déjà validés :
```text
ToolRegistry
ToolProcess
ToolTask
```
Le catalogue décrit les outils connus par lapplication. Le registre décrit leur état sur la machine courante.
```text
ToolCatalog
description statique des outils connus
ToolRegistry
disponibilité, chemin résolu, version détectée
ToolProcess
exécution sécurisée de la commande de version
```
## Contexte
`ToolRegistry` permet déjà denregistrer un outil, de rechercher son exécutable dans le `PATH`, de conserver son chemin résolu et sa version détectée, et de distinguer les états `UNKNOWN`, `AVAILABLE` et `MISSING`.
Il manque une source centrale définissant :
- les outils reconnus par lapplication ;
- leur identifiant interne ;
- leur nom affiché ;
- leur exécutable ;
- leur importance ;
- les arguments permettant de demander leur version ;
- la manière de normaliser la sortie obtenue.
Sans catalogue central, chaque fonctionnalité finirait par enregistrer elle-même ses dépendances.
## Modules attendus
```text
include/core/tool_catalog.h
src/core/tool_catalog.c
tests/test_tool_catalog.c
```
Le ticket ne doit pas modifier lAPI publique de `ToolRegistry`, sauf nécessité démontrée pendant limplémentation.
## Responsabilités de ToolCatalog
Le module doit :
1. conserver une liste statique et immuable des outils connus ;
2. garantir lunicité de leurs identifiants ;
3. exposer leurs informations descriptives ;
4. enregistrer lensemble du catalogue dans un `ToolRegistry` ;
5. définir les arguments de détection de version ;
6. exécuter ces arguments avec `ToolProcess` ;
7. normaliser la sortie de version ;
8. retourner la version détectée sans modifier directement le registre ;
9. gérer lannulation ;
10. rester indépendant de GTK.
Le module ne doit pas installer de dépendance, modifier le `PATH`, utiliser un shell, analyser un résultat OSINT ni modifier linterface graphique.
## Catalogue initial
| Identifiant interne | Nom affiché | Exécutable | Importance | Arguments de version |
|---|---|---|---|---|
| `dns.dig` | `dig` | `dig` | optionnel | `-v` |
| `dns.host` | `host` | `host` | optionnel | `-V` |
| `network.whois` | `whois` | `whois` | optionnel | `--version` |
| `http.curl` | `curl` | `curl` | optionnel | `--version` |
| `tls.openssl` | `OpenSSL` | `openssl` | optionnel | `version` |
Tous ces outils sont optionnels au niveau de lapplication entière.
Ne pas ajouter encore `nslookup`, `traceroute`, `jq`, `file`, `exiftool`, `nmap`, `subfinder`, `amass`, les outils Python ou les outils installés depuis GitHub.
## Structure ToolCatalogEntry
Créer une structure opaque :
```c
typedef struct ToolCatalogEntry ToolCatalogEntry;
```
Elle conserve au minimum :
```text
identifier
display_name
executable_name
requirement
version_arguments
```
Les données du catalogue sont statiques et immuables. Elles ne doivent jamais être libérées par lappelant.
## Domaine derreur
```c
#define TOOL_CATALOG_ERROR \
tool_catalog_error_quark()
```
```c
typedef enum
{
TOOL_CATALOG_ERROR_INVALID_ARGUMENT,
TOOL_CATALOG_ERROR_ENTRY_NOT_FOUND,
TOOL_CATALOG_ERROR_REGISTRATION,
TOOL_CATALOG_ERROR_TOOL_NOT_REGISTERED,
TOOL_CATALOG_ERROR_TOOL_NOT_CHECKED,
TOOL_CATALOG_ERROR_TOOL_MISSING,
TOOL_CATALOG_ERROR_INVALID_TOOL_STATE,
TOOL_CATALOG_ERROR_PROCESS,
TOOL_CATALOG_ERROR_VERSION_COMMAND,
TOOL_CATALOG_ERROR_VERSION_OUTPUT,
TOOL_CATALOG_ERROR_CANCELLED
} ToolCatalogError;
```
```c
GQuark tool_catalog_error_quark(void);
```
## API publique proposée
### Consultation
```c
gsize tool_catalog_get_count(void);
```
```c
const ToolCatalogEntry *tool_catalog_get_entry(
gsize index
);
```
```c
const ToolCatalogEntry *tool_catalog_find(
const char *identifier
);
```
### Accesseurs
```c
const char *tool_catalog_entry_get_identifier(
const ToolCatalogEntry *entry
);
```
```c
const char *tool_catalog_entry_get_display_name(
const ToolCatalogEntry *entry
);
```
```c
const char *tool_catalog_entry_get_executable_name(
const ToolCatalogEntry *entry
);
```
```c
ToolRequirement tool_catalog_entry_get_requirement(
const ToolCatalogEntry *entry
);
```
```c
gsize tool_catalog_entry_get_version_argument_count(
const ToolCatalogEntry *entry
);
```
```c
const char *tool_catalog_entry_get_version_argument(
const ToolCatalogEntry *entry,
gsize index
);
```
### Enregistrement
```c
gboolean tool_catalog_register_defaults(
ToolRegistry *tool_registry,
GError **error
);
```
### Détection dune version
```c
gboolean tool_catalog_detect_version(
const ToolRegistry *tool_registry,
const char *identifier,
GCancellable *cancellable,
char **out_version,
GError **error
);
```
`out_version` reçoit une chaîne nouvellement allouée, à libérer avec `g_free()`.
La fonction ne doit pas appeler `tool_registry_set_version()`.
## Règles denregistrement
`tool_catalog_register_defaults()` doit vérifier avant toute insertion quaucun identifiant du catalogue nexiste déjà dans le registre.
En cas de doublon :
- aucune entrée ne doit être ajoutée ;
- la fonction retourne `FALSE` ;
- lerreur vaut `TOOL_CATALOG_ERROR_REGISTRATION`.
Cette prévalidation évite une insertion partielle, car `ToolRegistry` ne fournit pas de suppression.
## Validation de la détection
La fonction doit refuser :
- un registre `NULL` ;
- un identifiant `NULL` ou vide ;
- un `out_version` égal à `NULL` ;
- un `out_version` pointant déjà vers une chaîne ;
- un `GError` déjà initialisé.
Au début dun appel valide :
```c
*out_version = NULL;
```
Cas derreur :
```text
entrée absente du catalogue → TOOL_CATALOG_ERROR_ENTRY_NOT_FOUND
outil absent du registre → TOOL_CATALOG_ERROR_TOOL_NOT_REGISTERED
état UNKNOWN → TOOL_CATALOG_ERROR_TOOL_NOT_CHECKED
état MISSING → TOOL_CATALOG_ERROR_TOOL_MISSING
AVAILABLE sans chemin résolu → TOOL_CATALOG_ERROR_INVALID_TOOL_STATE
```
## Exécution de la commande de version
Utiliser exclusivement :
```c
tool_process_run()
```
avec :
```text
executable_path = chemin résolu du ToolRegistry
arguments = arguments statiques du ToolCatalogEntry
working_directory = NULL
cancellable = paramètre reçu
```
Interdictions :
- `/bin/sh -c` ;
- `system()` ;
- `popen()` ;
- concaténation dune ligne de commande ;
- redirections interprétées ;
- ajout ou transformation des arguments.
## Annulation
Si `ToolProcess` retourne `TOOL_PROCESS_ERROR_CANCELLED`, la fonction doit produire :
```text
G_IO_ERROR
G_IO_ERROR_CANCELLED
```
Aucune version partielle ne doit être retournée.
## Code de sortie
Pour une commande de version, un code de sortie non nul est un échec :
```text
TOOL_CATALOG_ERROR_VERSION_COMMAND
```
Une terminaison par signal produit la même catégorie derreur.
La sortie ne doit pas être interprétée lorsque le processus na pas terminé normalement avec le code zéro.
## Sélection de la sortie
Règle générique :
1. prendre la première ligne non vide de stdout ;
2. si stdout ne contient rien dexploitable, essayer stderr ;
3. si aucun flux ne contient de texte exploitable, échouer.
Cette règle évite une logique spéciale par outil pendant ce premier ticket.
## Normalisation
Le catalogue ne doit pas extraire seulement un numéro sémantique.
La version conservée est la première ligne descriptive exploitable, par exemple :
```text
DiG 9.20.8
curl 8.14.1 (x86_64-pc-linux-gnu)
OpenSSL 3.5.1 1 Jul 2025
```
Algorithme :
1. obtenir les octets du flux ;
2. inspecter au maximum 4096 octets par flux ;
3. convertir les octets invalides avec `g_utf8_make_valid()` ;
4. séparer les lignes ;
5. ignorer les lignes vides ;
6. choisir la première ligne non vide ;
7. retirer espaces et tabulations en début et fin ;
8. retirer `\r` et `\n` ;
9. refuser un résultat vide ;
10. retourner une nouvelle chaîne.
## Propriété des données
Les entrées et chaînes du catalogue sont empruntées et statiques.
La chaîne placée dans `*out_version` appartient à lappelant et doit être libérée avec `g_free()`.
`ToolCatalog` ne devient jamais propriétaire du registre.
## Contraintes de thread
Le catalogue statique est immuable.
`tool_catalog_detect_version()` :
- ne modifie pas le catalogue ;
- ne modifie pas le registre ;
- copie le chemin nécessaire avant lexécution ;
- ne conserve aucun pointeur vers `ToolInfo` après la préparation.
Le stockage avec `tool_registry_set_version()` reste la responsabilité du propriétaire du registre, idéalement dans le thread principal.
## Tests unitaires obligatoires
Les tests utilisent uniquement de faux exécutables temporaires.
Ils ne doivent pas dépendre des outils réellement installés.
### Tests du catalogue
1. nombre exact dentrées ;
2. contenu exact des cinq entrées ;
3. index invalide ;
4. recherche par identifiant ;
5. identifiant inconnu ;
6. unicité des identifiants ;
7. arguments de version ;
8. enregistrement complet dans un registre vide ;
9. état initial `UNKNOWN` ;
10. prévalidation dun doublon sans insertion partielle.
### Tests de détection
11. arguments invalides ;
12. entrée inconnue ;
13. outil non enregistré ;
14. outil non vérifié ;
15. outil absent ;
16. version sur stdout ;
17. version sur stderr ;
18. première ligne non vide ;
19. normalisation des espaces et fins de ligne ;
20. sortie non UTF-8 ;
21. sortie vide ;
22. code de sortie non nul ;
23. terminaison par signal ;
24. annulation ;
25. indépendance du registre ;
26. plusieurs détections successives.
## Noms de tests suggérés
```text
/tool_catalog/entries
/tool_catalog/invalid_index
/tool_catalog/find
/tool_catalog/unique_entries
/tool_catalog/register_defaults
/tool_catalog/register_duplicate
/tool_catalog/detect_invalid_arguments
/tool_catalog/detect_unknown_entry
/tool_catalog/detect_unregistered_tool
/tool_catalog/detect_unchecked_tool
/tool_catalog/detect_missing_tool
/tool_catalog/version_stdout
/tool_catalog/version_stderr
/tool_catalog/version_first_nonempty_line
/tool_catalog/version_normalization
/tool_catalog/version_non_utf8
/tool_catalog/version_empty
/tool_catalog/version_nonzero_exit
/tool_catalog/version_signaled
/tool_catalog/version_cancelled
/tool_catalog/version_registry_independence
/tool_catalog/version_successive_runs
```
## Makefile
```make
TEST_TOOL_CATALOG := tests/test_tool_catalog
```
```make
$(TEST_TOOL_CATALOG): \
tests/test_tool_catalog.c \
src/core/tool_catalog.c \
src/core/tool_registry.c \
src/core/tool_process.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
```
Ajouter le binaire à `make test` et `make clean`.
## Vérifications
```bash
make clean
make
make tests/test_tool_catalog
./tests/test_tool_catalog
make test
git diff --check
```
## Vérification mémoire
```bash
G_DEBUG=gc-friendly \
G_SLICE=always-malloc \
valgrind \
--leak-check=full \
--show-leak-kinds=all \
./tests/test_tool_catalog
```
## Critères dacceptation
Le ticket est validé lorsque :
- le catalogue contient exactement les cinq outils prévus ;
- toutes les entrées sont immuables ;
- tous les identifiants sont uniques ;
- le catalogue senregistre dans un registre vide ;
- un doublon empêche toute insertion partielle ;
- la détection utilise `ToolProcess` ;
- aucun shell nest utilisé ;
- stdout et stderr sont pris en charge ;
- la première ligne non vide est normalisée ;
- une sortie invalide devient un UTF-8 valide ;
- linspection est limitée à 4096 octets par flux ;
- un code non nul ou un signal est refusé ;
- lannulation produit `G_IO_ERROR_CANCELLED` ;
- la fonction ne modifie pas directement le registre ;
- les versions successives sont indépendantes ;
- les tests ne dépendent daucun outil installé ;
- tous les tests passent ;
- aucune fuite mémoire nest détectée ;
- le test est intégré au `Makefile`.
## Démonstration finale
Après validation :
1. créer un `ToolRegistry` ;
2. appeler `tool_catalog_register_defaults()` ;
3. appeler `tool_registry_refresh()` ;
4. afficher les outils présents et absents ;
5. détecter la version dun outil disponible ;
6. stocker explicitement la version avec `tool_registry_set_version()`.
Cette démonstration restera hors de linterface GTK principale.
## Suite prévue
```text
#040 — Initialisation des dépendances et analyse asynchrone au démarrage
```
Ce ticket devra créer le registre global de lapplication, enregistrer le catalogue, rafraîchir les disponibilités hors du thread GTK, détecter les versions et publier une tâche dans `TaskManager`.