feat: add external tool registry

This commit is contained in:
grayTerminal-sh 2026-07-17 15:14:05 +02:00
parent 1c391335c6
commit c0ef751434
19 changed files with 3063 additions and 2 deletions

View file

@ -41,6 +41,8 @@ TEST_INVESTIGATION_RECORD = tests/test_investigation_record
TEST_INVESTIGATION_DAO := tests/test_investigation_dao TEST_INVESTIGATION_DAO := tests/test_investigation_dao
TEST_INVESTIGATION_SESSION := tests/test_investigation_session TEST_INVESTIGATION_SESSION := tests/test_investigation_session
TEST_BACKGROUND_TASK := tests/test_background_task TEST_BACKGROUND_TASK := tests/test_background_task
TEST_TASK_MANAGER := tests/test_task_manager
TEST_TOOL_REGISTRY := tests/test_tool_registry
all: $(TARGET) all: $(TARGET)
@ -143,6 +145,17 @@ $(TEST_BACKGROUND_TASK): \
src/core/background_task.c src/core/background_task.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_TASK_MANAGER): \
tests/test_task_manager.c \
src/core/task_manager.c \
src/core/background_task.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_TOOL_REGISTRY): \
tests/test_tool_registry.c \
src/core/tool_registry.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
test: \ test: \
$(TEST_NODE) \ $(TEST_NODE) \
$(TEST_TREE_MODEL) \ $(TEST_TREE_MODEL) \
@ -155,7 +168,9 @@ test: \
$(TEST_INVESTIGATION_RECORD) \ $(TEST_INVESTIGATION_RECORD) \
$(TEST_INVESTIGATION_DAO) \ $(TEST_INVESTIGATION_DAO) \
$(TEST_INVESTIGATION_SESSION) \ $(TEST_INVESTIGATION_SESSION) \
$(TEST_BACKGROUND_TASK) $(TEST_BACKGROUND_TASK) \
$(TEST_TASK_MANAGER) \
$(TEST_TOOL_REGISTRY)
@echo "Exécution des tests..." @echo "Exécution des tests..."
@./$(TEST_NODE) @./$(TEST_NODE)
@./$(TEST_TREE_MODEL) @./$(TEST_TREE_MODEL)
@ -169,6 +184,8 @@ test: \
@$(TEST_INVESTIGATION_DAO) @$(TEST_INVESTIGATION_DAO)
@$(TEST_INVESTIGATION_SESSION) @$(TEST_INVESTIGATION_SESSION)
@$(TEST_BACKGROUND_TASK) @$(TEST_BACKGROUND_TASK)
@$(TEST_TASK_MANAGER)
@$(TEST_TOOL_REGISTRY)
@echo "Tous les tests sont valides." @echo "Tous les tests sont valides."
%.o: %.c %.o: %.c
@ -190,6 +207,8 @@ clean:
$(TEST_INVESTIGATION_RECORD) \ $(TEST_INVESTIGATION_RECORD) \
$(TEST_INVESTIGATION_DAO) \ $(TEST_INVESTIGATION_DAO) \
$(TEST_INVESTIGATION_SESSION) \ $(TEST_INVESTIGATION_SESSION) \
$(TEST_BACKGROUND_TASK) $(TEST_BACKGROUND_TASK) \
$(TEST_TASK_MANAGER) \
$(TEST_TOOL_REGISTRY)
.PHONY: clean run test .PHONY: clean run test

View file

@ -0,0 +1,825 @@
# TICKET-036 — Registre des outils et dépendances externes
## Statut
À faire
## Priorité
Haute
## Objectif
Créer un module central capable de représenter les outils externes utilisés par Labfy Investigation et de déterminer sils sont disponibles sur la machine.
Ce registre deviendra la source unique permettant à lapplication de savoir :
- quel outil est demandé par une fonctionnalité ;
- si cet outil est obligatoire ou optionnel ;
- sous quel nom il est recherché dans le `PATH` ;
- où se trouve son exécutable ;
- si sa version est connue ;
- pourquoi une fonctionnalité doit être désactivée lorsquune dépendance manque.
Le module doit rester indépendant de GTK.
---
## Contexte
Labfy Investigation utilisera progressivement plusieurs outils externes :
- outils DNS ;
- outils RDAP et WHOIS ;
- outils TLS ;
- outils HTTP ;
- outils de traitement de métadonnées ;
- outils de conversion ou danalyse ;
- adaptateurs spécialisés OSINT.
Ces outils ne seront pas tous installés sur chaque machine.
La cible Ubuntu de la gendarmerie peut également disposer :
- de dépôts limités ;
- dune liste de paquets spécifique ;
- doutils installés dans des chemins non standards ;
- de versions différentes de celles présentes sur Arch Linux.
Le code métier ne doit donc jamais supposer quun exécutable est disponible.
---
## Périmètre
Ce ticket doit fournir :
1. un registre opaque `ToolRegistry` ;
2. une représentation opaque ou privée dun outil enregistré ;
3. lenregistrement dun outil à partir de métadonnées stables ;
4. la détection de lexécutable dans le `PATH` ;
5. la mémorisation du chemin détecté ;
6. la distinction entre dépendance obligatoire et optionnelle ;
7. la recherche dun outil par identifiant ;
8. lénumération des outils enregistrés ;
9. la détection des dépendances obligatoires manquantes ;
10. un champ permettant de mémoriser une version lorsquelle sera détectée ;
11. des tests unitaires sans dépendre des outils réellement installés sur la machine.
---
## Hors périmètre
Ce ticket ne doit pas encore :
- créer dinterface GTK ;
- lancer une commande OSINT réelle ;
- interpréter la sortie de `dig`, `whois`, `curl` ou `openssl` ;
- exécuter automatiquement `--version` ;
- installer des paquets ;
- modifier les dépôts du système ;
- télécharger un outil manquant ;
- utiliser une commande construite sous forme de chaîne shell ;
- contenir une liste définitive des outils OSINT de lapplication.
Lexécution contrôlée des programmes externes sera traitée dans un ticket dédié.
---
## Fichiers attendus
```text
include/core/tool_registry.h
src/core/tool_registry.c
tests/test_tool_registry.c
```
Le `Makefile` devra être mis à jour pour compiler le module et son test.
---
## Contraintes générales
- Standard C17.
- Compilation avec :
```text
-Wall -Wextra -Wpedantic -Werror
```
- Utiliser GLib.
- Respecter les conventions du projet :
- fichiers en `snake_case` ;
- fonctions préfixées par `tool_registry_` ou `tool_info_` ;
- noms de variables explicites ;
- structure publique opaque ;
- aucune variable globale mutable ;
- aucune dépendance à GTK.
- Ne jamais construire une commande shell.
- Ne jamais appeler `system()`, `popen()` ou équivalent.
- Ne pas coder en dur un chemin comme `/usr/bin/dig`.
---
## Modèle de données
### ToolRequirement
nvim.lsp.b_248_save BufWritePost
<buffer=248>
<Lua 3597: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_249_save BufWritePost
<buffer=249>
<Lua 3645: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_252_save BufWritePost
<buffer=252>
<Lua 3681: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_255_save BufWritePost
<buffer=255>
<Lua 2223: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_259_save BufWritePost
<buffer=259>
<Lua 3935: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_262_save BufWritePost
<buffer=262>
<Lua 3099: /usr/share/nvim/runtime/lua/vim/lsp.lua:943> [vim.lsp: textDocument/didSave handler]
Modifié la dernière fois dans ~/.dotfiles/nvim/.config/nvim/init.lua (run Nvim with -V1 for more details
)
nvim.lsp.b_273_save BufWritePost
<buffer=273>
<Lua 4130: /usr/share/nv
Créer une énumération représentant limportance dune dépendance :
```c
typedef enum
{
TOOL_REQUIREMENT_OPTIONAL,
TOOL_REQUIREMENT_REQUIRED
} ToolRequirement;
```
### ToolAvailability
Créer une énumération représentant létat de détection :
```c
typedef enum
{
TOOL_AVAILABILITY_UNKNOWN,
TOOL_AVAILABILITY_AVAILABLE,
TOOL_AVAILABILITY_MISSING
} ToolAvailability;
```
### ToolRegistry
La structure doit rester opaque :
```c
typedef struct ToolRegistry ToolRegistry;
```
### ToolInfo
La représentation dun outil doit également être opaque ou exposée uniquement en lecture :
```c
typedef struct ToolInfo ToolInfo;
```
Un outil enregistré doit au minimum contenir :
```text
identifier
display_name
executable_name
requirement
availability
resolved_path
detected_version
```
### Règles des champs
#### identifier
Identifiant interne stable et unique.
Exemples :
```text
dns.dig
network.curl
tls.openssl
metadata.exiftool
```
Il ne doit pas dépendre du nom du paquet de la distribution.
#### display_name
Nom compréhensible par lutilisateur.
Exemples :
```text
dig
cURL
OpenSSL
ExifTool
```
#### executable_name
Nom recherché dans le `PATH`.
Exemples :
```text
dig
curl
openssl
exiftool
```
#### resolved_path
Chemin absolu détecté.
Exemple :
```text
/usr/bin/dig
```
Ce champ vaut `NULL` lorsque loutil est absent ou na pas encore été vérifié.
#### detected_version
Version connue de loutil.
Ce champ peut rester `NULL` dans ce ticket. Le registre doit néanmoins pouvoir la mémoriser pour les tickets suivants.
---
## Domaine derreur
Créer un domaine derreur dédié :
```c
#define TOOL_REGISTRY_ERROR \
tool_registry_error_quark()
```
Erreurs minimales :
```c
typedef enum
{
TOOL_REGISTRY_ERROR_INVALID_ARGUMENT,
TOOL_REGISTRY_ERROR_DUPLICATE_IDENTIFIER,
TOOL_REGISTRY_ERROR_NOT_FOUND
} ToolRegistryError;
```
Fonction attendue :
```c
GQuark tool_registry_error_quark(void);
```
---
## API publique attendue
LAPI exacte peut être ajustée si une meilleure solution est justifiée, mais elle doit couvrir les opérations suivantes.
### Cycle de vie
```c
ToolRegistry *tool_registry_new(void);
void tool_registry_free(
ToolRegistry *tool_registry
);
```
### Enregistrement
```c
gboolean tool_registry_register(
ToolRegistry *tool_registry,
const char *identifier,
const char *display_name,
const char *executable_name,
ToolRequirement requirement,
GError **error
);
```
Règles :
- tous les textes doivent être non `NULL` et non vides ;
- lidentifiant doit être unique ;
- le registre doit dupliquer les chaînes reçues ;
- loutil nouvellement enregistré commence avec :
- disponibilité `UNKNOWN` ;
- chemin `NULL` ;
- version `NULL`.
### Détection
```c
gboolean tool_registry_refresh(
ToolRegistry *tool_registry,
GError **error
);
```
Cette fonction doit vérifier tous les outils enregistrés.
Pour la recherche dans le `PATH`, utiliser lAPI GLib adaptée, par exemple :
```c
g_find_program_in_path()
```
Règles :
- si lexécutable est trouvé :
- état `AVAILABLE` ;
- chemin détecté mémorisé ;
- sil nest pas trouvé :
- état `MISSING` ;
- ancien chemin supprimé ;
- un second rafraîchissement doit remplacer proprement les anciennes valeurs ;
- aucune fuite mémoire ne doit être produite.
### Consultation
```c
gsize tool_registry_get_count(
const ToolRegistry *tool_registry
);
```
```c
const ToolInfo *tool_registry_get_tool(
const ToolRegistry *tool_registry,
gsize index
);
```
```c
const ToolInfo *tool_registry_find(
const ToolRegistry *tool_registry,
const char *identifier
);
```
Le comportement de propriété doit être documenté clairement.
Pour une première version, les pointeurs retournés peuvent être empruntés et rester valides jusquà la prochaine modification du registre ou jusquà sa destruction.
### Version détectée
```c
gboolean tool_registry_set_version(
ToolRegistry *tool_registry,
const char *identifier,
const char *detected_version,
GError **error
);
```
Règles :
- loutil doit exister ;
- la version peut être remplacée ;
- une chaîne vide ou `NULL` efface la version connue ;
- le registre doit dupliquer la chaîne.
Cette fonction sera utilisée plus tard par le module chargé dinterroger les exécutables.
### État global
```c
gboolean tool_registry_has_missing_required_tools(
const ToolRegistry *tool_registry
);
```
Cette fonction retourne `TRUE` lorsquau moins un outil obligatoire est dans létat `MISSING`.
Un outil obligatoire encore dans létat `UNKNOWN` ne doit pas être considéré comme disponible.
Une seconde fonction est recommandée :
```c
gboolean tool_registry_all_required_tools_available(
const ToolRegistry *tool_registry
);
```
Elle ne retourne `TRUE` que lorsque tous les outils obligatoires sont explicitement `AVAILABLE`.
---
## Accesseurs de ToolInfo
Prévoir au minimum :
```c
const char *tool_info_get_identifier(
const ToolInfo *tool_info
);
```
```c
const char *tool_info_get_display_name(
const ToolInfo *tool_info
);
```
```c
const char *tool_info_get_executable_name(
const ToolInfo *tool_info
);
```
```c
ToolRequirement tool_info_get_requirement(
const ToolInfo *tool_info
);
```
```c
ToolAvailability tool_info_get_availability(
const ToolInfo *tool_info
);
```
```c
const char *tool_info_get_resolved_path(
const ToolInfo *tool_info
);
```
```c
const char *tool_info_get_detected_version(
const ToolInfo *tool_info
);
```
Les chaînes retournées sont empruntées et ne doivent jamais être libérées par lappelant.
---
## Implémentation interne recommandée
Le registre peut utiliser :
```c
GPtrArray
```
pour conserver lordre denregistrement.
Une recherche linéaire par identifiant est acceptable pour cette première version, car le nombre doutils restera faible.
Une table de hachage pourra être ajoutée plus tard seulement si elle devient utile.
Le registre devient propriétaire de tous les outils enregistrés.
Chaque outil doit libérer :
- son identifiant ;
- son nom affiché ;
- son nom dexécutable ;
- son chemin résolu ;
- sa version ;
- sa structure.
---
## Détection testable
Les tests ne doivent pas dépendre de la présence réelle de `dig`, `curl`, `openssl` ou dautres outils.
Le test doit créer un répertoire temporaire contenant un faux exécutable.
Exemple de stratégie :
1. créer un dossier temporaire avec GLib ;
2. créer un fichier nommé `fake_osint_tool` ;
3. lui donner les permissions dexécution ;
4. sauvegarder la valeur actuelle de `PATH` ;
5. placer temporairement le dossier au début de `PATH` ;
6. enregistrer `fake_osint_tool` ;
7. appeler `tool_registry_refresh()` ;
8. vérifier que loutil est disponible ;
9. restaurer impérativement le `PATH` initial ;
10. supprimer les fichiers temporaires.
Le test doit restaurer lenvironnement même en cas déchec dune assertion intermédiaire.
Une fonction de préparation et une fonction de nettoyage de fixture sont recommandées.
---
## Tests unitaires obligatoires
### 1. Construction vide
Vérifier :
- création du registre ;
- nombre doutils égal à zéro ;
- recherche dun identifiant absent ;
- destruction sans erreur.
### 2. Enregistrement valide
Vérifier :
- nombre doutils ;
- conservation de lordre ;
- contenu des accesseurs ;
- état initial `UNKNOWN` ;
- chemin initial `NULL` ;
- version initiale `NULL`.
### 3. Duplication des chaînes
Créer des chaînes dynamiques, enregistrer loutil, puis libérer les chaînes originales.
Le registre doit conserver des valeurs valides.
### 4. Identifiant en double
Enregistrer deux outils avec le même identifiant.
Le second enregistrement doit échouer avec :
```text
TOOL_REGISTRY_ERROR_DUPLICATE_IDENTIFIER
```
Le premier outil ne doit pas être modifié.
### 5. Arguments invalides
Tester au minimum :
- registre `NULL` ;
- identifiant `NULL` ;
- identifiant vide ;
- nom affiché vide ;
- nom dexécutable vide ;
- `GError` déjà initialisé si le projet applique cette règle.
### 6. Recherche par identifiant
Vérifier :
- outil existant ;
- outil absent ;
- identifiant `NULL` ;
- identifiant vide.
### 7. Outil disponible
Avec un faux exécutable placé temporairement dans le `PATH`, vérifier :
- état `AVAILABLE` ;
- chemin absolu non `NULL` ;
- chemin correspondant au faux exécutable.
### 8. Outil absent
Avec un nom improbable, vérifier :
- état `MISSING` ;
- chemin `NULL`.
Exemple :
```text
labfy_tool_that_must_not_exist_036
```
### 9. Rafraîchissement successif
Vérifier quun outil peut passer :
```text
MISSING → AVAILABLE
```
puis :
```text
AVAILABLE → MISSING
```
après modification contrôlée du `PATH`.
### 10. Version
Vérifier :
- ajout dune version ;
- remplacement dune version ;
- effacement avec `NULL` ;
- erreur sur identifiant inconnu.
### 11. Dépendances obligatoires
Tester les combinaisons :
```text
obligatoire + disponible
obligatoire + absent
obligatoire + inconnu
optionnel + absent
```
Une dépendance optionnelle absente ne doit pas rendre létat global invalide.
### 12. Destruction complète
Exécuter les tests avec les outils de vérification mémoire du projet.
---
## Noms de tests suggérés
```text
/tool_registry/construction
/tool_registry/register
/tool_registry/string_ownership
/tool_registry/duplicate_identifier
/tool_registry/invalid_arguments
/tool_registry/find
/tool_registry/available_tool
/tool_registry/missing_tool
/tool_registry/successive_refresh
/tool_registry/version
/tool_registry/required_tools
```
---
## Makefile
Ajouter le nouveau module à la compilation principale.
Ajouter une cible :
```text
test_tool_registry
```
La cible doit compiler au minimum :
```text
tests/test_tool_registry.c
src/core/tool_registry.c
```
avec GLib.
Ajouter le test à la cible globale de tests du projet.
---
## Vérifications manuelles
Commandes attendues :
```bash
make clean
make
make test_tool_registry
./test_tool_registry
```
Puis, si une cible globale existe :
```bash
make test
```
Aucun warning de compilation ne doit être toléré.
---
## Vérification mémoire recommandée
Selon les outils déjà utilisés dans le projet :
```bash
G_DEBUG=gc-friendly \
G_SLICE=always-malloc \
valgrind \
--leak-check=full \
--show-leak-kinds=all \
./test_tool_registry
```
Le test ne doit laisser aucune allocation définitivement perdue provenant du registre.
Les allocations internes conservées par GLib doivent être interprétées avec prudence.
---
## Critères dacceptation
Le ticket est validé lorsque :
- le projet compile avec les options strictes ;
- le registre est opaque ;
- plusieurs outils peuvent être enregistrés ;
- les identifiants en double sont refusés ;
- les chaînes sont copiées ;
- les outils sont recherchés dans le `PATH` ;
- le chemin détecté est mémorisé ;
- un outil absent est marqué `MISSING` ;
- un rafraîchissement remplace correctement létat précédent ;
- une version peut être stockée et effacée ;
- les dépendances obligatoires manquantes sont détectées ;
- les outils optionnels absents ne bloquent pas létat global ;
- les tests ne dépendent pas des outils réellement installés ;
- le `PATH` original est restauré après chaque test ;
- aucune dépendance GTK nest introduite ;
- aucun appel shell nest utilisé ;
- tous les tests passent ;
- aucune erreur mémoire liée au module nest détectée.
---
## Résultat attendu
À la fin de ce ticket, le code suivant doit être conceptuellement possible :
```c
ToolRegistry *tool_registry = NULL;
const ToolInfo *dig_tool = NULL;
GError *error = NULL;
tool_registry = tool_registry_new();
tool_registry_register(
tool_registry,
"dns.dig",
"dig",
"dig",
TOOL_REQUIREMENT_OPTIONAL,
&error
);
tool_registry_refresh(
tool_registry,
&error
);
dig_tool = tool_registry_find(
tool_registry,
"dns.dig"
);
if (tool_info_get_availability(dig_tool) ==
TOOL_AVAILABILITY_AVAILABLE)
{
g_print(
"dig détecté dans : %s\n",
tool_info_get_resolved_path(dig_tool)
);
}
```
Cet exemple décrit le comportement attendu. Il ne constitue pas une obligation dorganisation interne.
---
## Suite prévue
Après validation de ce ticket :
1. catalogue initial des outils utilisés par Labfy Investigation ;
2. abstraction contrôlée autour de `GSubprocess` ;
3. interrogation des versions ;
4. adaptateurs doutils OSINT ;
5. affichage des dépendances dans linterface ;
6. intégration avec `BackgroundTask` et `TaskManager` ;
7. conservation des sorties brutes et de leur provenance ;
8. prise en compte du paquet Ubuntu et du script dinstallation.

View file

@ -0,0 +1,332 @@
/******************************************************************************
* @file tool_registry.h
* @brief Registre des outils et dépendances externes.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_TOOL_REGISTRY_H
#define LABFY_INVESTIGATION_TOOL_REGISTRY_H
#include <glib.h>
G_BEGIN_DECLS
/**
* @brief Importance dun outil externe pour lapplication.
*/
typedef enum
{
/**
* Labsence de loutil ne bloque pas lapplication.
*/
TOOL_REQUIREMENT_OPTIONAL,
/**
* Loutil est indispensable à la fonctionnalité concernée.
*/
TOOL_REQUIREMENT_REQUIRED
} ToolRequirement;
/**
* @brief État de disponibilité dun outil externe.
*/
typedef enum
{
/**
* Loutil na pas encore é recherché.
*/
TOOL_AVAILABILITY_UNKNOWN,
/**
* Lexécutable a é trouvé.
*/
TOOL_AVAILABILITY_AVAILABLE,
/**
* Lexécutable na pas é trouvé.
*/
TOOL_AVAILABILITY_MISSING
} ToolAvailability;
/**
* @brief Erreurs produites par ToolRegistry.
*/
typedef enum
{
/**
* Un argument transmis à une fonction est invalide.
*/
TOOL_REGISTRY_ERROR_INVALID_ARGUMENT,
/**
* Un outil possède le même identifiant quun outil déjà enregistré.
*/
TOOL_REGISTRY_ERROR_DUPLICATE_IDENTIFIER,
/**
* Loutil demandé nexiste pas dans le registre.
*/
TOOL_REGISTRY_ERROR_NOT_FOUND
} ToolRegistryError;
/**
* @brief Domaine derreur du registre doutils.
*/
#define TOOL_REGISTRY_ERROR \
tool_registry_error_quark()
/**
* @brief Registre opaque des outils externes.
*/
typedef struct ToolRegistry ToolRegistry;
/**
* @brief Informations opaques décrivant un outil externe.
*/
typedef struct ToolInfo ToolInfo;
/**
* @brief Retourne le domaine derreur du registre.
*
* @return Quark GLib du domaine derreur.
*/
GQuark tool_registry_error_quark(void);
/**
* @brief Crée un registre doutils vide.
*
* @return Nouveau registre, ou NULL en cas déchec.
*/
ToolRegistry *tool_registry_new(void);
/**
* @brief Libère un registre et tous les outils quil contient.
*
* @param tool_registry Registre à libérer, ou NULL.
*/
void tool_registry_free(
ToolRegistry *tool_registry
);
/**
* @brief Enregistre un nouvel outil externe.
*
* Le registre duplique toutes les chaînes reçues.
*
* Loutil commence avec :
*
* - une disponibilité inconnue ;
* - aucun chemin résolu ;
* - aucune version détectée.
*
* @param tool_registry Registre cible.
* @param identifier Identifiant interne unique.
* @param display_name Nom affiché à lutilisateur.
* @param executable_name Nom de lexécutable recherché dans le PATH.
* @param requirement Importance de la dépendance.
* @param error Emplacement facultatif pour lerreur.
*
* @return TRUE en cas de succès, sinon FALSE.
*/
gboolean tool_registry_register(
ToolRegistry *tool_registry,
const char *identifier,
const char *display_name,
const char *executable_name,
ToolRequirement requirement,
GError **error
);
/**
* @brief Recherche tous les exécutables enregistrés dans le PATH.
*
* Chaque ancien chemin résolu est remplacé par le résultat de la nouvelle
* détection.
*
* @param tool_registry Registre à actualiser.
* @param error Emplacement facultatif pour lerreur.
*
* @return TRUE lorsque le rafraîchissement a pu être effectué.
*/
gboolean tool_registry_refresh(
ToolRegistry *tool_registry,
GError **error
);
/**
* @brief Retourne le nombre doutils enregistrés.
*
* @param tool_registry Registre consulté.
*
* @return Nombre doutils, ou zéro si le registre est NULL.
*/
gsize tool_registry_get_count(
const ToolRegistry *tool_registry
);
/**
* @brief Retourne un outil à partir de son index.
*
* Le pointeur retourné est emprunté. Il ne doit pas être libéré.
*
* @param tool_registry Registre consulté.
* @param index Index de loutil.
*
* @return Outil correspondant, ou NULL si lindex est invalide.
*/
const ToolInfo *tool_registry_get_tool(
const ToolRegistry *tool_registry,
gsize index
);
/**
* @brief Recherche un outil à partir de son identifiant.
*
* Le pointeur retourné est emprunté. Il ne doit pas être libéré.
*
* @param tool_registry Registre consulté.
* @param identifier Identifiant recherché.
*
* @return Outil correspondant, ou NULL sil est absent.
*/
const ToolInfo *tool_registry_find(
const ToolRegistry *tool_registry,
const char *identifier
);
/**
* @brief Mémorise la version détectée dun outil.
*
* Une version NULL ou vide efface la version actuellement enregistrée.
*
* @param tool_registry Registre contenant loutil.
* @param identifier Identifiant de loutil.
* @param detected_version Version détectée, ou NULL.
* @param error Emplacement facultatif pour lerreur.
*
* @return TRUE en cas de succès, sinon FALSE.
*/
gboolean tool_registry_set_version(
ToolRegistry *tool_registry,
const char *identifier,
const char *detected_version,
GError **error
);
/**
* @brief Indique si une dépendance obligatoire est explicitement absente.
*
* Les outils encore dans létat UNKNOWN ne sont pas considérés ici comme
* explicitement absents.
*
* @param tool_registry Registre consulté.
*
* @return TRUE si au moins un outil obligatoire est MISSING.
*/
gboolean tool_registry_has_missing_required_tools(
const ToolRegistry *tool_registry
);
/**
* @brief Indique si toutes les dépendances obligatoires sont disponibles.
*
* Un outil obligatoire dans létat UNKNOWN empêche cette fonction de
* retourner TRUE.
*
* @param tool_registry Registre consulté.
*
* @return TRUE si tous les outils obligatoires sont AVAILABLE.
*/
gboolean tool_registry_all_required_tools_available(
const ToolRegistry *tool_registry
);
/**
* @brief Retourne lidentifiant interne dun outil.
*
* La chaîne retournée est empruntée.
*
* @param tool_info Outil consulté.
*
* @return Identifiant, ou NULL.
*/
const char *tool_info_get_identifier(
const ToolInfo *tool_info
);
/**
* @brief Retourne le nom affiché dun outil.
*
* La chaîne retournée est empruntée.
*
* @param tool_info Outil consulté.
*
* @return Nom affiché, ou NULL.
*/
const char *tool_info_get_display_name(
const ToolInfo *tool_info
);
/**
* @brief Retourne le nom de lexécutable dun outil.
*
* La chaîne retournée est empruntée.
*
* @param tool_info Outil consulté.
*
* @return Nom de lexécutable, ou NULL.
*/
const char *tool_info_get_executable_name(
const ToolInfo *tool_info
);
/**
* @brief Retourne limportance dun outil.
*
* @param tool_info Outil consulté.
*
* @return Importance de loutil.
*/
ToolRequirement tool_info_get_requirement(
const ToolInfo *tool_info
);
/**
* @brief Retourne létat de disponibilité dun outil.
*
* @param tool_info Outil consulté.
*
* @return État de disponibilité.
*/
ToolAvailability tool_info_get_availability(
const ToolInfo *tool_info
);
/**
* @brief Retourne le chemin absolu détecté dun outil.
*
* La chaîne retournée est empruntée.
*
* @param tool_info Outil consulté.
*
* @return Chemin détecté, ou NULL.
*/
const char *tool_info_get_resolved_path(
const ToolInfo *tool_info
);
/**
* @brief Retourne la version détectée dun outil.
*
* La chaîne retournée est empruntée.
*
* @param tool_info Outil consulté.
*
* @return Version détectée, ou NULL.
*/
const char *tool_info_get_detected_version(
const ToolInfo *tool_info
);
G_END_DECLS
#endif

Binary file not shown.

635
src/core/tool_registry.c Normal file
View file

@ -0,0 +1,635 @@
/******************************************************************************
* @file tool_registry.c
* @brief Implémentation du registre des outils externes.
******************************************************************************/
#include "core/tool_registry.h"
/**
* @struct ToolInfo
* @brief Informations internes décrivant un outil externe.
*/
struct ToolInfo
{
char *identifier;
char *display_name;
char *executable_name;
ToolRequirement requirement;
ToolAvailability availability;
char *resolved_path;
char *detected_version;
};
/**
* @struct ToolRegistry
* @brief Registre interne des outils externes.
*/
struct ToolRegistry
{
GPtrArray *tools;
};
/**
* @brief Vérifie qu'une chaîne est définie et non vide.
*
* @param text Chaîne à vérifier.
*
* @return TRUE si la chaîne est valide.
*/
static gboolean tool_registry_string_is_valid(
const char *text
)
{
return text != NULL &&
text[0] != '\0';
}
/**
* @brief Vérifie qu'une importance de dépendance est valide.
*
* @param requirement Valeur à vérifier.
*
* @return TRUE si la valeur est reconnue.
*/
static gboolean tool_registry_requirement_is_valid(
ToolRequirement requirement
)
{
return requirement == TOOL_REQUIREMENT_OPTIONAL ||
requirement == TOOL_REQUIREMENT_REQUIRED;
}
/**
* @brief Libère un outil et toutes ses chaînes.
*
* Cette fonction est utilisée comme fonction de destruction du GPtrArray.
*
* @param user_data Pointeur vers ToolInfo.
*/
static void tool_info_free(
gpointer user_data
)
{
ToolInfo *tool_info = user_data;
if (tool_info == NULL)
{
return;
}
g_clear_pointer(
&tool_info->identifier,
g_free
);
g_clear_pointer(
&tool_info->display_name,
g_free
);
g_clear_pointer(
&tool_info->executable_name,
g_free
);
g_clear_pointer(
&tool_info->resolved_path,
g_free
);
g_clear_pointer(
&tool_info->detected_version,
g_free
);
g_free(
tool_info
);
}
/**
* @brief Recherche un outil modifiable dans le registre.
*
* @param tool_registry Registre consulté.
* @param identifier Identifiant recherché.
*
* @return Outil correspondant, ou NULL.
*/
static ToolInfo *tool_registry_find_internal(
const ToolRegistry *tool_registry,
const char *identifier
)
{
guint index = 0;
if (tool_registry == NULL ||
tool_registry->tools == NULL ||
!tool_registry_string_is_valid(identifier))
{
return NULL;
}
for (index = 0;
index < tool_registry->tools->len;
index++)
{
ToolInfo *tool_info = NULL;
tool_info = g_ptr_array_index(
tool_registry->tools,
index
);
if (tool_info != NULL &&
g_strcmp0(
tool_info->identifier,
identifier
) == 0)
{
return tool_info;
}
}
return NULL;
}
GQuark tool_registry_error_quark(void)
{
return g_quark_from_static_string(
"labfy-investigation-tool-registry-error"
);
}
ToolRegistry *tool_registry_new(void)
{
ToolRegistry *tool_registry = NULL;
tool_registry = g_new0(
ToolRegistry,
1
);
tool_registry->tools =
g_ptr_array_new_with_free_func(
tool_info_free
);
return tool_registry;
}
void tool_registry_free(
ToolRegistry *tool_registry
)
{
if (tool_registry == NULL)
{
return;
}
if (tool_registry->tools != NULL)
{
g_ptr_array_unref(
tool_registry->tools
);
tool_registry->tools = NULL;
}
g_free(
tool_registry
);
}
gboolean tool_registry_register(
ToolRegistry *tool_registry,
const char *identifier,
const char *display_name,
const char *executable_name,
ToolRequirement requirement,
GError **error
)
{
ToolInfo *tool_info = NULL;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (tool_registry == NULL ||
tool_registry->tools == NULL ||
!tool_registry_string_is_valid(identifier) ||
!tool_registry_string_is_valid(display_name) ||
!tool_registry_string_is_valid(executable_name) ||
!tool_registry_requirement_is_valid(requirement))
{
g_set_error_literal(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_INVALID_ARGUMENT,
"Les arguments fournis au registre d'outils sont invalides."
);
return FALSE;
}
if (tool_registry_find_internal(
tool_registry,
identifier
) != NULL)
{
g_set_error(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_DUPLICATE_IDENTIFIER,
"Un outil portant l'identifiant '%s' est déjà enregistré.",
identifier
);
return FALSE;
}
tool_info = g_new0(
ToolInfo,
1
);
tool_info->identifier = g_strdup(
identifier
);
tool_info->display_name = g_strdup(
display_name
);
tool_info->executable_name = g_strdup(
executable_name
);
tool_info->requirement = requirement;
tool_info->availability =
TOOL_AVAILABILITY_UNKNOWN;
tool_info->resolved_path = NULL;
tool_info->detected_version = NULL;
g_ptr_array_add(
tool_registry->tools,
tool_info
);
return TRUE;
}
gboolean tool_registry_refresh(
ToolRegistry *tool_registry,
GError **error
)
{
guint index = 0;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (tool_registry == NULL ||
tool_registry->tools == NULL)
{
g_set_error_literal(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_INVALID_ARGUMENT,
"Le registre d'outils est invalide."
);
return FALSE;
}
for (index = 0;
index < tool_registry->tools->len;
index++)
{
ToolInfo *tool_info = NULL;
char *found_path = NULL;
char *canonical_path = NULL;
tool_info = g_ptr_array_index(
tool_registry->tools,
index
);
if (tool_info == NULL)
{
continue;
}
found_path = g_find_program_in_path(
tool_info->executable_name
);
g_clear_pointer(
&tool_info->resolved_path,
g_free
);
if (found_path == NULL)
{
tool_info->availability =
TOOL_AVAILABILITY_MISSING;
continue;
}
/*
* g_find_program_in_path() retourne une nouvelle chaîne.
*
* La canonicalisation garantit que le registre mémorise un
* chemin absolu et normalisé.
*/
canonical_path = g_canonicalize_filename(
found_path,
NULL
);
g_free(
found_path
);
tool_info->resolved_path =
canonical_path;
tool_info->availability =
TOOL_AVAILABILITY_AVAILABLE;
}
return TRUE;
}
gsize tool_registry_get_count(
const ToolRegistry *tool_registry
)
{
if (tool_registry == NULL ||
tool_registry->tools == NULL)
{
return 0;
}
return tool_registry->tools->len;
}
const ToolInfo *tool_registry_get_tool(
const ToolRegistry *tool_registry,
gsize index
)
{
if (tool_registry == NULL ||
tool_registry->tools == NULL ||
index >= tool_registry->tools->len)
{
return NULL;
}
return g_ptr_array_index(
tool_registry->tools,
(guint) index
);
}
const ToolInfo *tool_registry_find(
const ToolRegistry *tool_registry,
const char *identifier
)
{
return tool_registry_find_internal(
tool_registry,
identifier
);
}
gboolean tool_registry_set_version(
ToolRegistry *tool_registry,
const char *identifier,
const char *detected_version,
GError **error
)
{
ToolInfo *tool_info = NULL;
g_return_val_if_fail(
error == NULL || *error == NULL,
FALSE
);
if (tool_registry == NULL ||
tool_registry->tools == NULL ||
!tool_registry_string_is_valid(identifier))
{
g_set_error_literal(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_INVALID_ARGUMENT,
"Le registre ou l'identifiant de l'outil est invalide."
);
return FALSE;
}
tool_info = tool_registry_find_internal(
tool_registry,
identifier
);
if (tool_info == NULL)
{
g_set_error(
error,
TOOL_REGISTRY_ERROR,
TOOL_REGISTRY_ERROR_NOT_FOUND,
"Aucun outil portant l'identifiant '%s' n'est enregistré.",
identifier
);
return FALSE;
}
g_clear_pointer(
&tool_info->detected_version,
g_free
);
if (tool_registry_string_is_valid(
detected_version
))
{
tool_info->detected_version =
g_strdup(
detected_version
);
}
return TRUE;
}
gboolean tool_registry_has_missing_required_tools(
const ToolRegistry *tool_registry
)
{
guint index = 0;
if (tool_registry == NULL ||
tool_registry->tools == NULL)
{
return FALSE;
}
for (index = 0;
index < tool_registry->tools->len;
index++)
{
const ToolInfo *tool_info = NULL;
tool_info = g_ptr_array_index(
tool_registry->tools,
index
);
if (tool_info != NULL &&
tool_info->requirement ==
TOOL_REQUIREMENT_REQUIRED &&
tool_info->availability ==
TOOL_AVAILABILITY_MISSING)
{
return TRUE;
}
}
return FALSE;
}
gboolean tool_registry_all_required_tools_available(
const ToolRegistry *tool_registry
)
{
guint index = 0;
if (tool_registry == NULL ||
tool_registry->tools == NULL)
{
return FALSE;
}
for (index = 0;
index < tool_registry->tools->len;
index++)
{
const ToolInfo *tool_info = NULL;
tool_info = g_ptr_array_index(
tool_registry->tools,
index
);
if (tool_info != NULL &&
tool_info->requirement ==
TOOL_REQUIREMENT_REQUIRED &&
tool_info->availability !=
TOOL_AVAILABILITY_AVAILABLE)
{
return FALSE;
}
}
/*
* Lorsqu'aucun outil obligatoire n'est enregistré, toutes les
* dépendances obligatoires sont considérées comme satisfaites.
*/
return TRUE;
}
const char *tool_info_get_identifier(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->identifier;
}
const char *tool_info_get_display_name(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->display_name;
}
const char *tool_info_get_executable_name(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->executable_name;
}
ToolRequirement tool_info_get_requirement(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return TOOL_REQUIREMENT_OPTIONAL;
}
return tool_info->requirement;
}
ToolAvailability tool_info_get_availability(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return TOOL_AVAILABILITY_UNKNOWN;
}
return tool_info->availability;
}
const char *tool_info_get_resolved_path(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->resolved_path;
}
const char *tool_info_get_detected_version(
const ToolInfo *tool_info
)
{
if (tool_info == NULL)
{
return NULL;
}
return tool_info->detected_version;
}

BIN
tests/test_background_task Executable file

Binary file not shown.

BIN
tests/test_database Executable file

Binary file not shown.

BIN
tests/test_error Executable file

Binary file not shown.

BIN
tests/test_investigation_dao Executable file

Binary file not shown.

BIN
tests/test_investigation_node Executable file

Binary file not shown.

BIN
tests/test_investigation_project Executable file

Binary file not shown.

BIN
tests/test_investigation_record Executable file

Binary file not shown.

BIN
tests/test_investigation_session Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
tests/test_statement Executable file

Binary file not shown.

BIN
tests/test_tool_registry Executable file

Binary file not shown.

1250
tests/test_tool_registry.c Normal file

File diff suppressed because it is too large Load diff

BIN
tests/test_transaction Executable file

Binary file not shown.