13 KiB
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 :
ToolRegistry
ToolProcess
ToolTask
Le catalogue décrit les outils connus par l’application. Le registre décrit leur état sur la machine courante.
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à d’enregistrer 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 l’application ;
- 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
include/core/tool_catalog.h
src/core/tool_catalog.c
tests/test_tool_catalog.c
Le ticket ne doit pas modifier l’API publique de ToolRegistry, sauf nécessité démontrée pendant l’implémentation.
Responsabilités de ToolCatalog
Le module doit :
- conserver une liste statique et immuable des outils connus ;
- garantir l’unicité de leurs identifiants ;
- exposer leurs informations descriptives ;
- enregistrer l’ensemble du catalogue dans un
ToolRegistry; - définir les arguments de détection de version ;
- exécuter ces arguments avec
ToolProcess; - normaliser la sortie de version ;
- retourner la version détectée sans modifier directement le registre ;
- gérer l’annulation ;
- 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 l’interface 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 l’application 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 :
typedef struct ToolCatalogEntry ToolCatalogEntry;
Elle conserve au minimum :
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 l’appelant.
Domaine d’erreur
#define TOOL_CATALOG_ERROR \
tool_catalog_error_quark()
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;
GQuark tool_catalog_error_quark(void);
API publique proposée
Consultation
gsize tool_catalog_get_count(void);
const ToolCatalogEntry *tool_catalog_get_entry(
gsize index
);
const ToolCatalogEntry *tool_catalog_find(
const char *identifier
);
Accesseurs
const char *tool_catalog_entry_get_identifier(
const ToolCatalogEntry *entry
);
const char *tool_catalog_entry_get_display_name(
const ToolCatalogEntry *entry
);
const char *tool_catalog_entry_get_executable_name(
const ToolCatalogEntry *entry
);
ToolRequirement tool_catalog_entry_get_requirement(
const ToolCatalogEntry *entry
);
gsize tool_catalog_entry_get_version_argument_count(
const ToolCatalogEntry *entry
);
const char *tool_catalog_entry_get_version_argument(
const ToolCatalogEntry *entry,
gsize index
);
Enregistrement
gboolean tool_catalog_register_defaults(
ToolRegistry *tool_registry,
GError **error
);
Détection d’une version
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 d’enregistrement
tool_catalog_register_defaults() doit vérifier avant toute insertion qu’aucun identifiant du catalogue n’existe déjà dans le registre.
En cas de doublon :
- aucune entrée ne doit être ajoutée ;
- la fonction retourne
FALSE; - l’erreur 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
NULLou vide ; - un
out_versionégal àNULL; - un
out_versionpointant déjà vers une chaîne ; - un
GErrordéjà initialisé.
Au début d’un appel valide :
*out_version = NULL;
Cas d’erreur :
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 :
tool_process_run()
avec :
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 d’une ligne de commande ;
- redirections interprétées ;
- ajout ou transformation des arguments.
Annulation
Si ToolProcess retourne TOOL_PROCESS_ERROR_CANCELLED, la fonction doit produire :
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 :
TOOL_CATALOG_ERROR_VERSION_COMMAND
Une terminaison par signal produit la même catégorie d’erreur.
La sortie ne doit pas être interprétée lorsque le processus n’a pas terminé normalement avec le code zéro.
Sélection de la sortie
Règle générique :
- prendre la première ligne non vide de stdout ;
- si stdout ne contient rien d’exploitable, essayer stderr ;
- 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 :
DiG 9.20.8
curl 8.14.1 (x86_64-pc-linux-gnu)
OpenSSL 3.5.1 1 Jul 2025
Algorithme :
- obtenir les octets du flux ;
- inspecter au maximum 4096 octets par flux ;
- convertir les octets invalides avec
g_utf8_make_valid(); - séparer les lignes ;
- ignorer les lignes vides ;
- choisir la première ligne non vide ;
- retirer espaces et tabulations en début et fin ;
- retirer
\ret\n; - refuser un résultat vide ;
- 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 à l’appelant 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 l’exécution ;
- ne conserve aucun pointeur vers
ToolInfoaprè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
- nombre exact d’entrées ;
- contenu exact des cinq entrées ;
- index invalide ;
- recherche par identifiant ;
- identifiant inconnu ;
- unicité des identifiants ;
- arguments de version ;
- enregistrement complet dans un registre vide ;
- état initial
UNKNOWN; - prévalidation d’un doublon sans insertion partielle.
Tests de détection
- arguments invalides ;
- entrée inconnue ;
- outil non enregistré ;
- outil non vérifié ;
- outil absent ;
- version sur stdout ;
- version sur stderr ;
- première ligne non vide ;
- normalisation des espaces et fins de ligne ;
- sortie non UTF-8 ;
- sortie vide ;
- code de sortie non nul ;
- terminaison par signal ;
- annulation ;
- indépendance du registre ;
- plusieurs détections successives.
Noms de tests suggérés
/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
TEST_TOOL_CATALOG := tests/test_tool_catalog
$(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
make clean
make
make tests/test_tool_catalog
./tests/test_tool_catalog
make test
git diff --check
Vérification mémoire
G_DEBUG=gc-friendly \
G_SLICE=always-malloc \
valgrind \
--leak-check=full \
--show-leak-kinds=all \
./tests/test_tool_catalog
Critères d’acceptation
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 s’enregistre dans un registre vide ;
- un doublon empêche toute insertion partielle ;
- la détection utilise
ToolProcess; - aucun shell n’est 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 ;
- l’inspection est limitée à 4096 octets par flux ;
- un code non nul ou un signal est refusé ;
- l’annulation produit
G_IO_ERROR_CANCELLED; - la fonction ne modifie pas directement le registre ;
- les versions successives sont indépendantes ;
- les tests ne dépendent d’aucun outil installé ;
- tous les tests passent ;
- aucune fuite mémoire n’est détectée ;
- le test est intégré au
Makefile.
Démonstration finale
Après validation :
- créer un
ToolRegistry; - appeler
tool_catalog_register_defaults(); - appeler
tool_registry_refresh(); - afficher les outils présents et absents ;
- détecter la version d’un outil disponible ;
- stocker explicitement la version avec
tool_registry_set_version().
Cette démonstration restera hors de l’interface GTK principale.
Suite prévue
#040 — Initialisation des dépendances et analyse asynchrone au démarrage
Ce ticket devra créer le registre global de l’application, enregistrer le catalogue, rafraîchir les disponibilités hors du thread GTK, détecter les versions et publier une tâche dans TaskManager.