From dc45abaf0d4dbba2622500ed3ae7f89973ad263c Mon Sep 17 00:00:00 2001 From: grayTerminal-sh Date: Fri, 17 Jul 2026 15:42:30 +0200 Subject: [PATCH] feat: add secure external tool execution --- Makefile | 16 +- docs/tickets/closed/TICKET-037.md | 1129 +++++++++++++++++++++++ include/core/tool_process.h | 189 ++++ labfy-investigation | Bin 258624 -> 275472 bytes src/core/tool_process.c | 612 +++++++++++++ tests/test_tool_process | Bin 0 -> 41480 bytes tests/test_tool_process.c | 1397 +++++++++++++++++++++++++++++ 7 files changed, 3340 insertions(+), 3 deletions(-) create mode 100644 docs/tickets/closed/TICKET-037.md create mode 100644 include/core/tool_process.h create mode 100644 src/core/tool_process.c create mode 100755 tests/test_tool_process create mode 100644 tests/test_tool_process.c diff --git a/Makefile b/Makefile index d797e70..6c48d4e 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ TEST_INVESTIGATION_SESSION := tests/test_investigation_session TEST_BACKGROUND_TASK := tests/test_background_task TEST_TASK_MANAGER := tests/test_task_manager TEST_TOOL_REGISTRY := tests/test_tool_registry +TEST_TOOL_PROCESS := tests/test_tool_process all: $(TARGET) @@ -156,6 +157,12 @@ $(TEST_TOOL_REGISTRY): \ src/core/tool_registry.c $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) +$(TEST_TOOL_PROCESS): \ + tests/test_tool_process.c \ + src/core/tool_process.c + $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) + + test: \ $(TEST_NODE) \ $(TEST_TREE_MODEL) \ @@ -170,7 +177,8 @@ test: \ $(TEST_INVESTIGATION_SESSION) \ $(TEST_BACKGROUND_TASK) \ $(TEST_TASK_MANAGER) \ - $(TEST_TOOL_REGISTRY) + $(TEST_TOOL_REGISTRY) \ + $(TEST_TOOL_PROCESS) @echo "Exécution des tests..." @./$(TEST_NODE) @./$(TEST_TREE_MODEL) @@ -186,6 +194,7 @@ test: \ @$(TEST_BACKGROUND_TASK) @$(TEST_TASK_MANAGER) @$(TEST_TOOL_REGISTRY) + @$(TEST_TOOL_PROCESS) @echo "Tous les tests sont valides." %.o: %.c @@ -208,7 +217,8 @@ clean: $(TEST_INVESTIGATION_DAO) \ $(TEST_INVESTIGATION_SESSION) \ $(TEST_BACKGROUND_TASK) \ - $(TEST_TASK_MANAGER) \ - $(TEST_TOOL_REGISTRY) + $(TEST_TASK_MANAGER) \ + $(TEST_TOOL_REGISTRY) \ + $(TEST_TOOL_PROCESS) .PHONY: clean run test diff --git a/docs/tickets/closed/TICKET-037.md b/docs/tickets/closed/TICKET-037.md new file mode 100644 index 0000000..8557a19 --- /dev/null +++ b/docs/tickets/closed/TICKET-037.md @@ -0,0 +1,1129 @@ +# TICKET-037 — Exécution sécurisée des outils externes avec GSubprocess + +## Statut + +À faire + +## Priorité + +Haute + +## Objectif + +Créer une abstraction C centralisée permettant à Labfy Investigation d’exécuter un outil externe de manière contrôlée avec `GSubprocess`, sans passer par un shell. + +Le module devra : + +- recevoir le chemin d’un exécutable ; +- recevoir une liste d’arguments séparés ; +- lancer directement le programme ; +- capturer intégralement `stdout` et `stderr` ; +- conserver le code de sortie ; +- distinguer une fin normale d’une terminaison par signal ; +- permettre l’annulation avec `GCancellable` ; +- restituer un résultat structuré ; +- rester indépendant de GTK ; +- pouvoir être utilisé dans un worker `BackgroundTask`. + +--- + +## Contexte + +Le ticket #036 fournit désormais un registre capable de détecter les outils présents sur la machine et de mémoriser leur chemin. + +Le prochain besoin est d’exécuter ces outils sans introduire de vulnérabilité liée à une commande shell. + +Labfy Investigation devra progressivement lancer des outils tels que : + +```text +dig +curl +openssl +whois +exiftool +``` + +Les arguments pourront contenir : + +- des noms de domaines ; +- des chemins de fichiers ; +- des URL ; +- des options ; +- des espaces ; +- des caractères normalement interprétés par un shell. + +Ces valeurs ne doivent jamais être concaténées dans une commande textuelle. + +--- + +## Principe de sécurité + +Le module doit lancer l’exécutable et ses arguments sous forme de tableau. + +Exemple conceptuel correct : + +```c +const char *arguments[] = +{ + "+short", + "example.org", + NULL +}; +``` + +Exemple interdit : + +```c +char *command = g_strdup_printf( + "dig +short %s", + domain_name +); + +system(command); +``` + +Les API suivantes sont interdites dans le code de production : + +```text +system() +popen() +execl("/bin/sh", ...) +sh -c +bash -c +g_spawn_command_line_sync() +g_spawn_command_line_async() +``` + +Le module doit utiliser `GSubprocess` ou `GSubprocessLauncher` avec un vecteur d’arguments. + +--- + +## Périmètre + +Ce ticket doit fournir : + +1. un module autonome `tool_process` ; +2. une structure opaque `ToolProcessResult` ; +3. une fonction synchrone et annulable d’exécution ; +4. la capture brute de `stdout` ; +5. la capture brute de `stderr` ; +6. le code de sortie ; +7. l’état de fin normale ou par signal ; +8. la conservation du signal de terminaison lorsqu’il est disponible ; +9. la gestion d’un dossier de travail facultatif ; +10. une distinction claire entre : + - une erreur de lancement ; + - une erreur de communication ; + - une annulation ; + - un programme ayant simplement retourné un code différent de zéro ; +11. des tests unitaires avec de faux outils temporaires ; +12. l’intégration au `Makefile`. + +--- + +## Hors périmètre + +Ce ticket ne doit pas encore : + +- créer une interface GTK ; +- lancer automatiquement les outils du registre ; +- analyser la sortie de `dig`, `curl`, `openssl` ou d’un autre outil ; +- déterminer la version d’un outil ; +- ajouter une limite de durée automatique ; +- gérer un pool de processus ; +- gérer une file d’attente ; +- enregistrer les sorties dans SQLite ; +- créer une preuve ou une observation OSINT ; +- modifier l’environnement complet du processus ; +- afficher la progression détaillée d’une commande ; +- ajouter un pseudo-terminal ; +- exécuter une commande à travers un shell. + +L’intégration avec `BackgroundTask` sera effectuée après validation de cette abstraction. + +--- + +## Fichiers attendus + +```text +include/core/tool_process.h +src/core/tool_process.c +tests/test_tool_process.c +``` + +Le `Makefile` devra compiler le module dans l’application principale et fournir une cible : + +```text +tests/test_tool_process +``` + +--- + +## Contraintes générales + +- C17. +- GLib et GIO. +- Aucune dépendance GTK. +- Compilation stricte : + +```text +-Wall -Wextra -Wpedantic -Werror +``` + +- Structure publique opaque. +- Fonctions préfixées par : + +```text +tool_process_ +tool_process_result_ +``` + +- Aucun état global mutable. +- Aucun shell. +- Aucun chemin d’outil codé en dur dans le module. +- Les arguments doivent être transmis séparément. +- Les sorties doivent être conservées sous forme d’octets bruts. +- Les données reçues doivent être copiées ou référencées avec une propriété clairement documentée. + +--- + +## Modèle public + +### ToolProcessResult + +```c +typedef struct ToolProcessResult ToolProcessResult; +``` + +La structure doit rester opaque. + +Elle doit contenir au minimum : + +```text +stdout_bytes +stderr_bytes +exited_normally +exit_status +was_signaled +termination_signal +``` + +### Propriété + +Le résultat retourné appartient à l’appelant. + +Il doit être libéré avec : + +```c +void tool_process_result_free( + ToolProcessResult *result +); +``` + +Les sorties doivent être conservées avec `GBytes`. + +--- + +## Domaine d’erreur + +Créer : + +```c +#define TOOL_PROCESS_ERROR \ + tool_process_error_quark() +``` + +Énumération minimale : + +```c +typedef enum +{ + TOOL_PROCESS_ERROR_INVALID_ARGUMENT, + TOOL_PROCESS_ERROR_SPAWN, + TOOL_PROCESS_ERROR_COMMUNICATION, + TOOL_PROCESS_ERROR_CANCELLED, + TOOL_PROCESS_ERROR_INVALID_RESULT +} ToolProcessError; +``` + +Fonction : + +```c +GQuark tool_process_error_quark(void); +``` + +### Règle importante + +Un programme qui se lance correctement puis retourne : + +```text +exit 1 +exit 2 +exit 127 +``` + +ne constitue pas une erreur de l’API. + +Dans ce cas : + +```text +tool_process_run() retourne TRUE +``` + +et le code de sortie est conservé dans `ToolProcessResult`. + +La fonction ne retourne `FALSE` que lorsque le module n’a pas pu mener l’exécution jusqu’à l’obtention d’un résultat exploitable. + +--- + +## API publique attendue + +L’API exacte peut être ajustée si une meilleure conception est justifiée, mais elle doit couvrir les opérations suivantes. + +### Exécution + +```c +gboolean tool_process_run( + const char *executable_path, + const char *const arguments[], + const char *working_directory, + GCancellable *cancellable, + ToolProcessResult **out_result, + GError **error +); +``` + +### Paramètres + +#### executable_path + +Chemin de l’exécutable. + +Le module doit accepter un chemin absolu détecté par `ToolRegistry`. + +Le chemin doit être : + +- non `NULL` ; +- non vide. + +Le module ne doit pas reconstruire une commande textuelle. + +#### arguments + +Tableau terminé par `NULL`. + +Ce tableau contient uniquement les arguments placés après `argv[0]`. + +Exemple : + +```c +const char *arguments[] = +{ + "+short", + "example.org", + NULL +}; +``` + +`arguments` peut être `NULL`, ce qui signifie aucun argument supplémentaire. + +Le module doit construire en interne un vecteur de ce type : + +```text +argv[0] = executable_path +argv[1] = arguments[0] +argv[2] = arguments[1] +... +argv[n] = NULL +``` + +Chaque argument doit rester un élément distinct. + +#### working_directory + +Dossier de travail facultatif. + +- `NULL` signifie utiliser le dossier courant hérité ; +- une chaîne non vide demande l’utilisation de ce dossier ; +- une chaîne vide est invalide. + +Utiliser `GSubprocessLauncher` lorsqu’un dossier de travail est fourni. + +#### cancellable + +Objet d’annulation facultatif. + +- `NULL` signifie que l’appel n’est pas annulable ; +- un objet déjà annulé doit provoquer un échec avec : + - `TOOL_PROCESS_ERROR_CANCELLED`. + +#### out_result + +Doit être non `NULL`. + +Avant l’appel : + +```c +*out_result == NULL +``` + +En cas de succès : + +```c +*out_result != NULL +``` + +En cas d’échec : + +```c +*out_result == NULL +``` + +#### error + +Respecter la convention GLib : + +```c +error == NULL || *error == NULL +``` + +--- + +## Utilisation de GSubprocess + +Créer le processus avec les drapeaux : + +```c +G_SUBPROCESS_FLAGS_STDOUT_PIPE +G_SUBPROCESS_FLAGS_STDERR_PIPE +``` + +La capture recommandée est : + +```c +g_subprocess_communicate() +``` + +Cette API permet de récupérer des `GBytes` et évite de supposer que les sorties sont valides en UTF-8. + +Le module ne doit pas utiliser exclusivement : + +```c +g_subprocess_communicate_utf8() +``` + +car une sortie brute peut contenir des octets non UTF-8. + +--- + +## Données envoyées à stdin + +Dans ce ticket, aucun contenu n’est envoyé à l’entrée standard. + +La communication doit transmettre : + +```c +stdin_buf = NULL +``` + +Une gestion explicite de stdin pourra être ajoutée plus tard. + +--- + +## Construction du vecteur d’arguments + +L’implémentation peut utiliser : + +```c +GPtrArray +``` + +Exemple conceptuel : + +```c +GPtrArray *argv_builder = NULL; + +argv_builder = g_ptr_array_new(); + +g_ptr_array_add( + argv_builder, + (gpointer) executable_path +); + +/* Ajout de chaque argument séparément. */ + +g_ptr_array_add( + argv_builder, + NULL +); +``` + +Le module doit décider clairement s’il duplique les chaînes ou s’il les emprunte uniquement pendant l’appel. + +Comme `g_subprocess_launcher_spawnv()` consomme le tableau pendant l’appel de création, les chaînes peuvent être empruntées si leur durée de vie couvre cet appel. + +Aucune fonction ne doit concaténer les arguments. + +--- + +## Résultat d’exécution + +### Cycle de vie + +```c +void tool_process_result_free( + ToolProcessResult *result +); +``` + +La fonction doit accepter `NULL`. + +### Sortie standard + +```c +GBytes *tool_process_result_ref_stdout( + const ToolProcessResult *result +); +``` + +Retourne une nouvelle référence. + +L’appelant doit la libérer avec : + +```c +g_bytes_unref() +``` + +### Sortie d’erreur + +```c +GBytes *tool_process_result_ref_stderr( + const ToolProcessResult *result +); +``` + +Retourne une nouvelle référence. + +### Fin normale + +```c +gboolean tool_process_result_exited_normally( + const ToolProcessResult *result +); +``` + +### Code de sortie + +```c +int tool_process_result_get_exit_status( + const ToolProcessResult *result +); +``` + +Règles recommandées : + +- retourne le code réel si le programme s’est terminé normalement ; +- retourne `-1` si le résultat est `NULL` ou si le processus ne s’est pas terminé normalement. + +### Terminaison par signal + +```c +gboolean tool_process_result_was_signaled( + const ToolProcessResult *result +); +``` + +```c +int tool_process_result_get_termination_signal( + const ToolProcessResult *result +); +``` + +Règles recommandées : + +- retourne le signal réel si disponible ; +- retourne `0` dans les autres cas. + +### Succès fonctionnel + +Ajouter une fonction pratique : + +```c +gboolean tool_process_result_is_success( + const ToolProcessResult *result +); +``` + +Elle retourne `TRUE` uniquement lorsque : + +```text +exited_normally == TRUE +exit_status == 0 +``` + +Cette fonction ne doit pas confondre le succès de l’API et le succès du programme lancé. + +--- + +## Gestion des sorties vides + +Même lorsqu’un programme n’écrit rien, le résultat doit rester exploitable. + +Les choix acceptables sont : + +```text +GBytes vide +``` + +ou : + +```text +NULL documenté comme sortie vide +``` + +La solution recommandée est de conserver un `GBytes` vide afin de simplifier les appelants. + +Les deux flux doivent être indépendants. + +--- + +## Gestion de l’annulation + +Lorsqu’un `GCancellable` est annulé pendant : + +```c +g_subprocess_communicate() +``` + +le module doit : + +1. interrompre l’attente ; +2. forcer l’arrêt du processus si nécessaire avec : + ```c + g_subprocess_force_exit() + ``` +3. attendre ou finaliser proprement l’objet `GSubprocess` selon le comportement GLib ; +4. ne retourner aucun résultat partiel ; +5. retourner `FALSE` ; +6. produire : + ```text + TOOL_PROCESS_ERROR_CANCELLED + ``` + +Le module ne doit pas laisser de processus enfant actif après l’annulation. + +--- + +## Nettoyage obligatoire + +Tous les chemins d’erreur doivent libérer : + +- le tableau d’arguments ; +- le `GSubprocessLauncher` ; +- le `GSubprocess` ; +- les `GBytes` temporaires ; +- le résultat partiellement construit ; +- toute erreur intermédiaire. + +L’implémentation doit utiliser lorsque cela améliore la lisibilité : + +```c +g_autoptr() +g_clear_object() +g_clear_pointer() +``` + +Le style doit rester cohérent avec le reste du projet. + +--- + +## Comportement attendu + +### Exécutable introuvable + +```text +retour : FALSE +résultat : NULL +erreur : TOOL_PROCESS_ERROR_SPAWN +``` + +### Exécutable valide, code zéro + +```text +retour : TRUE +résultat : non NULL +is_success : TRUE +exit_status : 0 +``` + +### Exécutable valide, code non nul + +```text +retour : TRUE +résultat : non NULL +is_success : FALSE +exit_status : code réel +``` + +### Annulation + +```text +retour : FALSE +résultat : NULL +erreur : TOOL_PROCESS_ERROR_CANCELLED +``` + +### Sortie invalide UTF-8 + +```text +retour : TRUE +stdout brut conservé dans GBytes +``` + +--- + +## Tests unitaires obligatoires + +Les tests doivent créer leurs propres faux exécutables temporaires. + +Ils ne doivent pas dépendre de : + +```text +dig +curl +openssl +whois +``` + +Ils peuvent utiliser des scripts temporaires avec un shebang, car le projet cible Linux. + +Le code du module ne doit cependant jamais lancer un shell explicitement. + +--- + +### 1. Arguments invalides + +Tester au minimum : + +- `executable_path == NULL` ; +- chemin vide ; +- `out_result == NULL` ; +- `*out_result != NULL` ; +- dossier de travail vide ; +- `GError` déjà initialisé si cette convention est vérifiée par le projet. + +Résultat attendu : + +```text +TOOL_PROCESS_ERROR_INVALID_ARGUMENT +``` + +--- + +### 2. Exécution sans argument + +Créer un faux outil qui affiche un texte fixe. + +Vérifier : + +- retour `TRUE` ; +- fin normale ; +- code zéro ; +- sortie capturée ; +- `stderr` vide ; +- `is_success == TRUE`. + +--- + +### 3. Transmission exacte des arguments + +Le faux outil doit afficher chaque argument reçu sur une ligne séparée. + +Tester des arguments comme : + +```text +example.org +nom avec espaces +$(touch /tmp/labfy-injection) +; echo danger +* +``` + +Vérifier que les valeurs sont restituées littéralement. + +Le test doit également vérifier qu’aucun fichier d’injection n’a été créé. + +Ce test prouve qu’aucun shell n’interprète les arguments. + +--- + +### 4. Capture séparée de stdout et stderr + +Le faux outil écrit : + +```text +message-out +``` + +dans `stdout`, et : + +```text +message-err +``` + +dans `stderr`. + +Vérifier que les deux sorties ne sont pas mélangées. + +--- + +### 5. Code de sortie non nul + +Le faux outil doit : + +```text +exit 7 +``` + +Vérifier : + +- `tool_process_run() == TRUE` ; +- fin normale ; +- code de sortie `7` ; +- `is_success == FALSE`. + +--- + +### 6. Exécutable inexistant + +Utiliser un chemin temporaire inexistant. + +Vérifier : + +- retour `FALSE` ; +- résultat `NULL` ; +- erreur `TOOL_PROCESS_ERROR_SPAWN`. + +--- + +### 7. Dossier de travail + +Créer un dossier temporaire et un outil affichant son répertoire courant. + +Exécuter avec `working_directory`. + +Vérifier que la sortie correspond au chemin demandé. + +--- + +### 8. Annulation avant le lancement + +Créer un `GCancellable`, l’annuler avant l’appel, puis exécuter le faux outil. + +Vérifier : + +- retour `FALSE` ; +- résultat `NULL` ; +- erreur `TOOL_PROCESS_ERROR_CANCELLED`. + +--- + +### 9. Annulation pendant l’exécution + +Créer un faux outil long, par exemple : + +```sh +#!/bin/sh +sleep 10 +``` + +Lancer l’annulation depuis un second thread après un court délai. + +Vérifier : + +- retour `FALSE` ; +- erreur d’annulation ; +- aucun résultat ; +- durée du test nettement inférieure à dix secondes ; +- aucun processus enfant laissé actif. + +Le test ne doit pas dépendre d’une boucle principale GTK. + +--- + +### 10. Sortie vide + +Créer un outil qui ne produit aucune sortie. + +Vérifier que les accesseurs de sortie restent sûrs. + +--- + +### 11. Octets non UTF-8 + +Créer un outil qui écrit au moins un octet non UTF-8 dans `stdout`. + +Vérifier : + +- retour `TRUE` ; +- récupération des octets avec `GBytes` ; +- taille exacte ; +- contenu exact. + +Ce test confirme que le module n’utilise pas une API limitée au texte UTF-8. + +--- + +### 12. Terminaison par signal + +Sur Linux, créer un faux outil qui se termine lui-même par un signal. + +Vérifier lorsque la plateforme le permet : + +- `exited_normally == FALSE` ; +- `was_signaled == TRUE` ; +- signal non nul ; +- `exit_status == -1` ; +- `is_success == FALSE`. + +Le test peut être conditionné avec les macros de plateforme appropriées. + +--- + +## Noms de tests suggérés + +```text +/tool_process/invalid_arguments +/tool_process/no_arguments +/tool_process/literal_arguments +/tool_process/stdout_stderr +/tool_process/nonzero_exit +/tool_process/missing_executable +/tool_process/working_directory +/tool_process/cancelled_before_start +/tool_process/cancelled_during_run +/tool_process/empty_output +/tool_process/non_utf8_output +/tool_process/signaled +``` + +--- + +## Fixtures de test + +Une fixture peut contenir : + +```c +typedef struct +{ + char *temporary_directory; + char *tool_path; +} ToolProcessFixture; +``` + +Le `setup` doit : + +- créer un dossier temporaire ; +- préparer les chemins. + +Le `teardown` doit : + +- supprimer tous les scripts ; +- supprimer les fichiers produits par les tests ; +- supprimer le dossier ; +- libérer toutes les chaînes. + +Chaque test doit rester isolé. + +--- + +## Makefile + +Ajouter : + +```make +TEST_TOOL_PROCESS := tests/test_tool_process +``` + +Règle attendue : + +```make +$(TEST_TOOL_PROCESS): \ + tests/test_tool_process.c \ + src/core/tool_process.c + $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) +``` + +Ajouter la cible dans : + +```make +test +``` + +et dans : + +```make +clean +``` + +`TEST_LDFLAGS` contient déjà `gio-2.0`, nécessaire pour `GSubprocess`. + +--- + +## Vérifications manuelles + +```bash +make clean +make +make tests/test_tool_process +./tests/test_tool_process +make test +``` + +Aucun avertissement de compilation ne doit être accepté. + +--- + +## Vérification mémoire + +Exécuter : + +```bash +G_DEBUG=gc-friendly \ +G_SLICE=always-malloc \ +valgrind \ + --leak-check=full \ + --show-leak-kinds=all \ + ./tests/test_tool_process +``` + +Vérifier particulièrement les chemins : + +- lancement impossible ; +- code de sortie non nul ; +- annulation ; +- sortie vide ; +- erreur de communication. + +--- + +## Vérification des processus enfants + +Pendant le test d’annulation, contrôler si nécessaire qu’aucun faux outil ne reste actif après la fin du test. + +Le module ne doit créer ni zombie ni processus abandonné. + +--- + +## Critères d’acceptation + +Le ticket est validé lorsque : + +- le module compile en C17 strict ; +- il n’a aucune dépendance GTK ; +- il utilise `GSubprocess` ou `GSubprocessLauncher` ; +- il n’utilise aucun shell ; +- l’exécutable et les arguments sont transmis séparément ; +- `stdout` et `stderr` sont capturés séparément ; +- les sorties sont conservées dans des `GBytes` ; +- un code de sortie non nul produit tout de même un résultat ; +- l’échec de lancement produit une erreur ; +- le dossier de travail facultatif fonctionne ; +- l’annulation avant le lancement fonctionne ; +- l’annulation pendant l’exécution arrête le processus ; +- aucune sortie partielle n’est retournée après annulation ; +- les arguments contenant des caractères de shell restent littéraux ; +- les octets non UTF-8 sont conservés ; +- la terminaison par signal est représentée ; +- aucun processus enfant ne reste actif ; +- aucune fuite mémoire liée au module n’est détectée ; +- tous les tests passent ; +- le test est intégré à `make test` et `make clean`. + +--- + +## Exemple d’utilisation attendu + +```c +ToolProcessResult *result = NULL; +GBytes *stdout_bytes = NULL; +GError *error = NULL; + +const char *arguments[] = +{ + "+short", + "example.org", + NULL +}; + +if (!tool_process_run( + "/usr/bin/dig", + arguments, + NULL, + NULL, + &result, + &error + )) +{ + g_warning( + "Impossible d’exécuter dig : %s", + error != NULL + ? error->message + : "erreur inconnue" + ); + + g_clear_error( + &error + ); + + return; +} + +stdout_bytes = tool_process_result_ref_stdout( + result +); + +if (!tool_process_result_is_success( + result + )) +{ + g_warning( + "dig a retourné le code %d", + tool_process_result_get_exit_status( + result + ) + ); +} + +g_bytes_unref( + stdout_bytes +); + +tool_process_result_free( + result +); +``` + +L’exemple illustre le comportement attendu. Il ne constitue pas une obligation d’organisation interne. + +--- + +## Intégration future + +Après validation de ce ticket : + +1. création d’une tâche `BackgroundTask` exécutant `ToolProcess` ; +2. interrogation de la version des outils enregistrés ; +3. premier adaptateur OSINT, probablement DNS ; +4. conservation de la commande structurée : + - exécutable ; + - arguments ; + - date ; + - code de sortie ; + - sortie brute ; + - erreur brute ; +5. enregistrement de la provenance ; +6. affichage des résultats dans le panneau de travail ; +7. gestion ultérieure d’une durée maximale ; +8. ajout des dépendances facultatives aux installateurs Ubuntu. + diff --git a/include/core/tool_process.h b/include/core/tool_process.h new file mode 100644 index 0000000..fcea3dd --- /dev/null +++ b/include/core/tool_process.h @@ -0,0 +1,189 @@ +/****************************************************************************** + * @file tool_process.h + * @brief Exécution sécurisée des outils externes. + ******************************************************************************/ + +#ifndef LABFY_INVESTIGATION_TOOL_PROCESS_H +#define LABFY_INVESTIGATION_TOOL_PROCESS_H + +#include +#include + +G_BEGIN_DECLS + +/** + * @brief Erreurs produites pendant l’exécution d’un outil externe. + */ +typedef enum +{ + /** + * Un argument transmis à la fonction est invalide. + */ + TOOL_PROCESS_ERROR_INVALID_ARGUMENT, + + /** + * Le processus n’a pas pu être créé ou lancé. + */ + TOOL_PROCESS_ERROR_SPAWN, + + /** + * La communication avec le processus a échoué. + */ + TOOL_PROCESS_ERROR_COMMUNICATION, + + /** + * L’exécution a été annulée. + */ + TOOL_PROCESS_ERROR_CANCELLED, + + /** + * Le processus s’est terminé sans produire un résultat exploitable. + */ + TOOL_PROCESS_ERROR_INVALID_RESULT +} ToolProcessError; + +/** + * @brief Domaine d’erreur de ToolProcess. + */ +#define TOOL_PROCESS_ERROR \ + tool_process_error_quark() + +/** + * @brief Résultat opaque d’une exécution externe. + */ +typedef struct ToolProcessResult ToolProcessResult; + +/** + * @brief Retourne le domaine d’erreur de ToolProcess. + * + * @return Quark GLib du domaine d’erreur. + */ +GQuark tool_process_error_quark(void); + +/** + * @brief Exécute un outil externe sans passer par un shell. + * + * Le programme et chacun de ses arguments sont transmis séparément à + * GSubprocess. + * + * Un code de sortie différent de zéro ne constitue pas une erreur de cette + * fonction. Dans ce cas, la fonction retourne TRUE et le code de sortie est + * disponible dans ToolProcessResult. + * + * @param executable_path Chemin de l’exécutable. + * @param arguments Arguments supplémentaires terminés par NULL, ou NULL. + * @param working_directory Dossier de travail, ou NULL. + * @param cancellable Objet d’annulation facultatif. + * @param out_result Emplacement recevant le résultat. + * @param error Emplacement facultatif pour l’erreur. + * + * @return TRUE lorsqu’un résultat exploitable a été obtenu, sinon FALSE. + */ +gboolean tool_process_run( + const char *executable_path, + const char *const arguments[], + const char *working_directory, + GCancellable *cancellable, + ToolProcessResult **out_result, + GError **error +); + +/** + * @brief Libère un résultat d’exécution. + * + * @param result Résultat à libérer, ou NULL. + */ +void tool_process_result_free( + ToolProcessResult *result +); + +/** + * @brief Retourne une nouvelle référence sur la sortie standard. + * + * La référence retournée doit être libérée avec g_bytes_unref(). + * + * @param result Résultat consulté. + * + * @return Nouvelle référence sur stdout, ou NULL. + */ +GBytes *tool_process_result_ref_stdout( + const ToolProcessResult *result +); + +/** + * @brief Retourne une nouvelle référence sur la sortie d’erreur. + * + * La référence retournée doit être libérée avec g_bytes_unref(). + * + * @param result Résultat consulté. + * + * @return Nouvelle référence sur stderr, ou NULL. + */ +GBytes *tool_process_result_ref_stderr( + const ToolProcessResult *result +); + +/** + * @brief Indique si le processus s’est terminé normalement. + * + * @param result Résultat consulté. + * + * @return TRUE si le processus a appelé exit() ou retourné depuis main(). + */ +gboolean tool_process_result_exited_normally( + const ToolProcessResult *result +); + +/** + * @brief Retourne le code de sortie du processus. + * + * @param result Résultat consulté. + * + * @return Code de sortie, ou -1 si le processus ne s’est pas terminé + * normalement. + */ +int tool_process_result_get_exit_status( + const ToolProcessResult *result +); + +/** + * @brief Indique si le processus a été terminé par un signal. + * + * @param result Résultat consulté. + * + * @return TRUE si le processus a été terminé par un signal. + */ +gboolean tool_process_result_was_signaled( + const ToolProcessResult *result +); + +/** + * @brief Retourne le signal ayant terminé le processus. + * + * @param result Résultat consulté. + * + * @return Numéro du signal, ou zéro si aucun signal n’est disponible. + */ +int tool_process_result_get_termination_signal( + const ToolProcessResult *result +); + +/** + * @brief Indique si le programme s’est terminé avec succès. + * + * Le succès fonctionnel signifie : + * + * - fin normale ; + * - code de sortie égal à zéro. + * + * @param result Résultat consulté. + * + * @return TRUE si le programme a retourné zéro. + */ +gboolean tool_process_result_is_success( + const ToolProcessResult *result +); + +G_END_DECLS + +#endif diff --git a/labfy-investigation b/labfy-investigation index ddbf124190c369fd5db5a1630db103e0b1d788cc..a960d38724647dbdac21a27e88b656c00d7c5c65 100755 GIT binary patch delta 104861 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�}Y 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 delta 93163 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 != NULL + ? cause->message + : "erreur inconnue"; + + g_set_error( + error, + TOOL_PROCESS_ERROR, + error_code, + "%s : %s", + context_message, + cause_message + ); +} + +/** + * @brief Force l’arrêt d’un processus puis attend sa terminaison. + * + * Cette fonction est utilisée après une annulation ou une erreur de + * communication afin de ne pas abandonner le processus enfant. + * + * @param subprocess Processus à arrêter. + */ +static void tool_process_force_exit_and_wait( + GSubprocess *subprocess +) +{ + GError *wait_error = NULL; + + if (subprocess == NULL) + { + return; + } + + g_subprocess_force_exit( + subprocess + ); + + /* + * L’attente ne doit pas utiliser le GCancellable déjà annulé. + */ + if (!g_subprocess_wait( + subprocess, + NULL, + &wait_error + )) + { + g_clear_error( + &wait_error + ); + } +} + +GQuark tool_process_error_quark(void) +{ + return g_quark_from_static_string( + "labfy-investigation-tool-process-error" + ); +} + +gboolean tool_process_run( + const char *executable_path, + const char *const arguments[], + const char *working_directory, + GCancellable *cancellable, + ToolProcessResult **out_result, + GError **error +) +{ + GPtrArray *argument_vector = NULL; + + GSubprocessLauncher *launcher = NULL; + GSubprocess *subprocess = NULL; + + GBytes *stdout_bytes = NULL; + GBytes *stderr_bytes = NULL; + + ToolProcessResult *result = NULL; + + GError *local_error = NULL; + + GSubprocessFlags subprocess_flags = + G_SUBPROCESS_FLAGS_STDOUT_PIPE | + G_SUBPROCESS_FLAGS_STDERR_PIPE; + + gboolean communication_success = FALSE; + + g_return_val_if_fail( + error == NULL || *error == NULL, + FALSE + ); + + if (!tool_process_string_is_valid( + executable_path + ) || + out_result == NULL || + *out_result != NULL || + (working_directory != NULL && + working_directory[0] == '\0')) + { + g_set_error_literal( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_INVALID_ARGUMENT, + "Les arguments fournis à ToolProcess sont invalides." + ); + + return FALSE; + } + + /* + * Ne pas lancer un processus si l’opération est déjà annulée. + */ + if (cancellable != NULL && + g_cancellable_is_cancelled( + cancellable + )) + { + g_set_error_literal( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_CANCELLED, + "L’exécution de l’outil a été annulée avant son lancement." + ); + + return FALSE; + } + + argument_vector = tool_process_build_argv( + executable_path, + arguments + ); + + if (argument_vector == NULL) + { + g_set_error_literal( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_INVALID_ARGUMENT, + "Le vecteur d’arguments n’a pas pu être construit." + ); + + return FALSE; + } + + launcher = g_subprocess_launcher_new( + subprocess_flags + ); + + if (launcher == NULL) + { + g_ptr_array_unref( + argument_vector + ); + + g_set_error_literal( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_SPAWN, + "Le lanceur de processus n’a pas pu être créé." + ); + + return FALSE; + } + + if (working_directory != NULL) + { + g_subprocess_launcher_set_cwd( + launcher, + working_directory + ); + } + + /* + * Chaque argument est transmis séparément. + * + * Aucun shell n’interprète les espaces, étoiles, points-virgules + * ou substitutions présents dans les arguments. + */ + subprocess = g_subprocess_launcher_spawnv( + launcher, + (const char *const *) argument_vector->pdata, + &local_error + ); + + g_clear_object( + &launcher + ); + + g_ptr_array_unref( + argument_vector + ); + + argument_vector = NULL; + + if (subprocess == NULL) + { + tool_process_set_wrapped_error( + error, + TOOL_PROCESS_ERROR_SPAWN, + "Impossible de lancer l’outil externe", + local_error + ); + + g_clear_error( + &local_error + ); + + return FALSE; + } + + communication_success = g_subprocess_communicate( + subprocess, + NULL, + cancellable, + &stdout_bytes, + &stderr_bytes, + &local_error + ); + + if (!communication_success) + { + gboolean was_cancelled = FALSE; + + was_cancelled = + (local_error != NULL && + g_error_matches( + local_error, + G_IO_ERROR, + G_IO_ERROR_CANCELLED + )) || + (cancellable != NULL && + g_cancellable_is_cancelled( + cancellable + )); + + /* + * En cas d’erreur, GSubprocess ne garantit pas que les valeurs + * de sortie soient exploitables. + */ + g_clear_pointer( + &stdout_bytes, + g_bytes_unref + ); + + g_clear_pointer( + &stderr_bytes, + g_bytes_unref + ); + + tool_process_force_exit_and_wait( + subprocess + ); + + if (was_cancelled) + { + tool_process_set_wrapped_error( + error, + TOOL_PROCESS_ERROR_CANCELLED, + "L’exécution de l’outil a été annulée", + local_error + ); + } + else + { + tool_process_set_wrapped_error( + error, + TOOL_PROCESS_ERROR_COMMUNICATION, + "La communication avec l’outil externe a échoué", + local_error + ); + } + + g_clear_error( + &local_error + ); + + g_clear_object( + &subprocess + ); + + return FALSE; + } + + /* + * communicate() a réussi : le processus est terminé et son état + * peut maintenant être consulté. + */ + result = g_new0( + ToolProcessResult, + 1 + ); + + if (stdout_bytes != NULL) + { + result->stdout_bytes = + stdout_bytes; + + stdout_bytes = NULL; + } + else + { + result->stdout_bytes = + g_bytes_new_static( + "", + 0 + ); + } + + if (stderr_bytes != NULL) + { + result->stderr_bytes = + stderr_bytes; + + stderr_bytes = NULL; + } + else + { + result->stderr_bytes = + g_bytes_new_static( + "", + 0 + ); + } + + result->exited_normally = + g_subprocess_get_if_exited( + subprocess + ); + + result->was_signaled = + g_subprocess_get_if_signaled( + subprocess + ); + + result->exit_status = -1; + result->termination_signal = 0; + + if (result->exited_normally) + { + result->exit_status = + g_subprocess_get_exit_status( + subprocess + ); + } + + if (result->was_signaled) + { + result->termination_signal = + g_subprocess_get_term_sig( + subprocess + ); + } + + /* + * Un processus terminé doit avoir soit quitté normalement, + * soit été terminé par un signal. + */ + if (!result->exited_normally && + !result->was_signaled) + { + tool_process_result_free( + result + ); + + result = NULL; + + g_clear_object( + &subprocess + ); + + g_set_error_literal( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_INVALID_RESULT, + "Le processus s’est terminé dans un état non exploitable." + ); + + return FALSE; + } + + g_clear_object( + &subprocess + ); + + *out_result = result; + + return TRUE; +} + +void tool_process_result_free( + ToolProcessResult *result +) +{ + if (result == NULL) + { + return; + } + + g_clear_pointer( + &result->stdout_bytes, + g_bytes_unref + ); + + g_clear_pointer( + &result->stderr_bytes, + g_bytes_unref + ); + + g_free( + result + ); +} + +GBytes *tool_process_result_ref_stdout( + const ToolProcessResult *result +) +{ + if (result == NULL || + result->stdout_bytes == NULL) + { + return NULL; + } + + return g_bytes_ref( + result->stdout_bytes + ); +} + +GBytes *tool_process_result_ref_stderr( + const ToolProcessResult *result +) +{ + if (result == NULL || + result->stderr_bytes == NULL) + { + return NULL; + } + + return g_bytes_ref( + result->stderr_bytes + ); +} + +gboolean tool_process_result_exited_normally( + const ToolProcessResult *result +) +{ + if (result == NULL) + { + return FALSE; + } + + return result->exited_normally; +} + +int tool_process_result_get_exit_status( + const ToolProcessResult *result +) +{ + if (result == NULL || + !result->exited_normally) + { + return -1; + } + + return result->exit_status; +} + +gboolean tool_process_result_was_signaled( + const ToolProcessResult *result +) +{ + if (result == NULL) + { + return FALSE; + } + + return result->was_signaled; +} + +int tool_process_result_get_termination_signal( + const ToolProcessResult *result +) +{ + if (result == NULL || + !result->was_signaled) + { + return 0; + } + + return result->termination_signal; +} + +gboolean tool_process_result_is_success( + const ToolProcessResult *result +) +{ + if (result == NULL) + { + return FALSE; + } + + return result->exited_normally && + result->exit_status == 0; +} diff --git a/tests/test_tool_process b/tests/test_tool_process new file mode 100755 index 0000000000000000000000000000000000000000..fdb4d2765ece67089b784a55d5ca003438acf4b1 GIT binary patch literal 41480 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 literal 0 HcmV?d00001 diff --git a/tests/test_tool_process.c b/tests/test_tool_process.c new file mode 100644 index 0000000..8fb7168 --- /dev/null +++ b/tests/test_tool_process.c @@ -0,0 +1,1397 @@ +/****************************************************************************** + * @file test_tool_process.c + * @brief Tests unitaires de l’exécution sécurisée des outils externes. + ******************************************************************************/ + +#include "core/tool_process.h" + +#include +#include + +#include +#include + +#include + +/** + * @struct ToolProcessFixture + * @brief Environnement temporaire utilisé par les tests. + */ +typedef struct +{ + char *temporary_directory; + char *tool_path; + char *injection_path; +} ToolProcessFixture; + +/** + * @struct ToolProcessCancellationData + * @brief Données transmises au thread chargé d'annuler une exécution. + */ +typedef struct +{ + GCancellable *cancellable; + guint64 delay_microseconds; +} ToolProcessCancellationData; + +/** + * @brief Annule une opération après un court délai. + * + * @param user_data Pointeur vers ToolProcessCancellationData. + * + * @return Toujours NULL. + */ +static gpointer test_tool_process_cancel_after_delay( + gpointer user_data +) +{ + ToolProcessCancellationData *cancellation_data = + user_data; + + if (cancellation_data == NULL || + cancellation_data->cancellable == NULL) + { + return NULL; + } + + g_usleep( + cancellation_data->delay_microseconds + ); + + g_cancellable_cancel( + cancellation_data->cancellable + ); + + return NULL; +} + +/** + * @brief Crée un script exécutable utilisé comme faux outil. + * + * @param fixture Fixture du test. + * @param script_content Contenu complet du script. + */ +static void test_tool_process_write_script( + ToolProcessFixture *fixture, + const char *script_content +) +{ + GError *error = NULL; + gboolean write_success = FALSE; + int chmod_result = 0; + + g_assert_nonnull( + fixture + ); + + g_assert_nonnull( + fixture->tool_path + ); + + g_assert_nonnull( + script_content + ); + + write_success = g_file_set_contents( + fixture->tool_path, + script_content, + -1, + &error + ); + + g_assert_no_error( + error + ); + + g_assert_true( + write_success + ); + + chmod_result = g_chmod( + fixture->tool_path, + S_IRUSR | + S_IWUSR | + S_IXUSR + ); + + g_assert_cmpint( + chmod_result, + ==, + 0 + ); +} + +/** + * @brief Vérifie qu’un GBytes contient exactement le texte attendu. + * + * La fonction ne suppose pas que les données sont terminées par '\0'. + * + * @param bytes Données à vérifier. + * @param expected_text Texte attendu. + */ +static void test_tool_process_assert_bytes_equal_text( + GBytes *bytes, + const char *expected_text +) +{ + gconstpointer bytes_data = NULL; + gsize bytes_size = 0; + gsize expected_size = 0; + + g_assert_nonnull( + bytes + ); + + g_assert_nonnull( + expected_text + ); + + bytes_data = g_bytes_get_data( + bytes, + &bytes_size + ); + + expected_size = strlen( + expected_text + ); + + g_assert_cmpuint( + bytes_size, + ==, + expected_size + ); + + g_assert_cmpmem( + bytes_data, + bytes_size, + expected_text, + expected_size + ); +} + +/** + * @brief Prépare le répertoire temporaire d’un test. + * + * @param fixture Fixture à initialiser. + * @param user_data Données inutilisées. + */ +static void test_tool_process_fixture_setup( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + GError *error = NULL; + + (void) user_data; + + fixture->temporary_directory = + g_dir_make_tmp( + "labfy-tool-process-XXXXXX", + &error + ); + + g_assert_no_error( + error + ); + + g_assert_nonnull( + fixture->temporary_directory + ); + + fixture->tool_path = g_build_filename( + fixture->temporary_directory, + "fake_tool", + NULL + ); + + fixture->injection_path = g_build_filename( + fixture->temporary_directory, + "injection_created", + NULL + ); +} + +/** + * @brief Supprime les fichiers et le dossier temporaire. + * + * @param fixture Fixture à nettoyer. + * @param user_data Données inutilisées. + */ +static void test_tool_process_fixture_teardown( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + (void) user_data; + + if (fixture->tool_path != NULL) + { + g_remove( + fixture->tool_path + ); + } + + if (fixture->injection_path != NULL) + { + g_remove( + fixture->injection_path + ); + } + + if (fixture->temporary_directory != NULL) + { + g_rmdir( + fixture->temporary_directory + ); + } + + g_clear_pointer( + &fixture->tool_path, + g_free + ); + + g_clear_pointer( + &fixture->injection_path, + g_free + ); + + g_clear_pointer( + &fixture->temporary_directory, + g_free + ); +} + +/** + * @brief Vérifie le refus des arguments invalides. + */ +static void test_tool_process_invalid_arguments( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + ToolProcessResult *occupied_result = NULL; + + GError *error = NULL; + + (void) user_data; + + g_assert_false( + tool_process_run( + NULL, + NULL, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_error( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_INVALID_ARGUMENT + ); + + g_assert_null( + result + ); + + g_clear_error( + &error + ); + + g_assert_false( + tool_process_run( + "", + NULL, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_error( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_INVALID_ARGUMENT + ); + + g_clear_error( + &error + ); + + g_assert_false( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + NULL, + NULL, + &error + ) + ); + + g_assert_error( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_INVALID_ARGUMENT + ); + + g_clear_error( + &error + ); + + /* + * out_result doit pointer vers NULL avant l’appel. + * + * Ce pointeur sert uniquement de marqueur et n’est jamais déréférencé. + */ + occupied_result = + (ToolProcessResult *) fixture; + + g_assert_false( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + NULL, + &occupied_result, + &error + ) + ); + + g_assert_error( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_INVALID_ARGUMENT + ); + + g_clear_error( + &error + ); + + g_assert_false( + tool_process_run( + fixture->tool_path, + NULL, + "", + NULL, + &result, + &error + ) + ); + + g_assert_error( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_INVALID_ARGUMENT + ); + + g_clear_error( + &error + ); +} + +/** + * @brief Vérifie l’exécution d’un outil sans argument. + */ +static void test_tool_process_no_arguments( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + + GBytes *stdout_bytes = NULL; + GBytes *stderr_bytes = NULL; + + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "printf 'outil-execute'\n" + ); + + g_assert_true( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_no_error( + error + ); + + g_assert_nonnull( + result + ); + + g_assert_true( + tool_process_result_exited_normally( + result + ) + ); + + g_assert_false( + tool_process_result_was_signaled( + result + ) + ); + + g_assert_cmpint( + tool_process_result_get_exit_status( + result + ), + ==, + 0 + ); + + g_assert_cmpint( + tool_process_result_get_termination_signal( + result + ), + ==, + 0 + ); + + g_assert_true( + tool_process_result_is_success( + result + ) + ); + + stdout_bytes = + tool_process_result_ref_stdout( + result + ); + + stderr_bytes = + tool_process_result_ref_stderr( + result + ); + + test_tool_process_assert_bytes_equal_text( + stdout_bytes, + "outil-execute" + ); + + test_tool_process_assert_bytes_equal_text( + stderr_bytes, + "" + ); + + g_bytes_unref( + stdout_bytes + ); + + g_bytes_unref( + stderr_bytes + ); + + tool_process_result_free( + result + ); +} + +/** + * @brief Vérifie que les arguments ne sont pas interprétés par un shell. + */ +static void test_tool_process_literal_arguments( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + GBytes *stdout_bytes = NULL; + + char *injection_argument = NULL; + char *expected_output = NULL; + + const char *arguments[6]; + + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "for argument in \"$@\"\n" + "do\n" + " printf '%s\\n' \"$argument\"\n" + "done\n" + ); + + injection_argument = g_strdup_printf( + "$(touch %s)", + fixture->injection_path + ); + + arguments[0] = "example.org"; + arguments[1] = "nom avec espaces"; + arguments[2] = injection_argument; + arguments[3] = "; echo danger"; + arguments[4] = "*"; + arguments[5] = NULL; + + expected_output = g_strdup_printf( + "example.org\n" + "nom avec espaces\n" + "%s\n" + "; echo danger\n" + "*\n", + injection_argument + ); + + g_assert_false( + g_file_test( + fixture->injection_path, + G_FILE_TEST_EXISTS + ) + ); + + g_assert_true( + tool_process_run( + fixture->tool_path, + arguments, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_no_error( + error + ); + + g_assert_true( + tool_process_result_is_success( + result + ) + ); + + stdout_bytes = + tool_process_result_ref_stdout( + result + ); + + test_tool_process_assert_bytes_equal_text( + stdout_bytes, + expected_output + ); + + /* + * Si un shell avait interprété l’argument, ce fichier existerait. + */ + g_assert_false( + g_file_test( + fixture->injection_path, + G_FILE_TEST_EXISTS + ) + ); + + g_bytes_unref( + stdout_bytes + ); + + tool_process_result_free( + result + ); + + g_free( + expected_output + ); + + g_free( + injection_argument + ); +} + +/** + * @brief Vérifie la séparation de stdout et stderr. + */ +static void test_tool_process_stdout_stderr( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + + GBytes *stdout_bytes = NULL; + GBytes *stderr_bytes = NULL; + + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "printf 'message-out'\n" + "printf 'message-err' >&2\n" + ); + + g_assert_true( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_no_error( + error + ); + + stdout_bytes = + tool_process_result_ref_stdout( + result + ); + + stderr_bytes = + tool_process_result_ref_stderr( + result + ); + + test_tool_process_assert_bytes_equal_text( + stdout_bytes, + "message-out" + ); + + test_tool_process_assert_bytes_equal_text( + stderr_bytes, + "message-err" + ); + + g_bytes_unref( + stdout_bytes + ); + + g_bytes_unref( + stderr_bytes + ); + + tool_process_result_free( + result + ); +} + +/** + * @brief Vérifie qu’un code non nul produit tout de même un résultat. + */ +static void test_tool_process_nonzero_exit( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "printf 'echec-fonctionnel' >&2\n" + "exit 7\n" + ); + + g_assert_true( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_no_error( + error + ); + + g_assert_nonnull( + result + ); + + g_assert_true( + tool_process_result_exited_normally( + result + ) + ); + + g_assert_cmpint( + tool_process_result_get_exit_status( + result + ), + ==, + 7 + ); + + g_assert_false( + tool_process_result_is_success( + result + ) + ); + + tool_process_result_free( + result + ); +} + +/** + * @brief Vérifie l’erreur produite par un exécutable inexistant. + */ +static void test_tool_process_missing_executable( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + char *missing_path = NULL; + GError *error = NULL; + + (void) user_data; + + missing_path = g_build_filename( + fixture->temporary_directory, + "tool_that_does_not_exist", + NULL + ); + + g_assert_false( + tool_process_run( + missing_path, + NULL, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_error( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_SPAWN + ); + + g_assert_null( + result + ); + + g_clear_error( + &error + ); + + g_free( + missing_path + ); +} + +/** + * @brief Vérifie l'utilisation du dossier de travail demandé. + */ +static void test_tool_process_working_directory( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + GBytes *stdout_bytes = NULL; + + char *canonical_directory = NULL; + + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "printf '%s' \"$PWD\"\n" + ); + + g_assert_true( + tool_process_run( + fixture->tool_path, + NULL, + fixture->temporary_directory, + NULL, + &result, + &error + ) + ); + + g_assert_no_error( + error + ); + + g_assert_nonnull( + result + ); + + canonical_directory = g_canonicalize_filename( + fixture->temporary_directory, + NULL + ); + + stdout_bytes = + tool_process_result_ref_stdout( + result + ); + + test_tool_process_assert_bytes_equal_text( + stdout_bytes, + canonical_directory + ); + + g_bytes_unref( + stdout_bytes + ); + + g_free( + canonical_directory + ); + + tool_process_result_free( + result + ); +} + +/** + * @brief Vérifie une annulation demandée avant le lancement. + */ +static void test_tool_process_cancelled_before_start( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + GCancellable *cancellable = NULL; + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "exit 0\n" + ); + + cancellable = g_cancellable_new(); + + g_cancellable_cancel( + cancellable + ); + + g_assert_false( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + cancellable, + &result, + &error + ) + ); + + g_assert_error( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_CANCELLED + ); + + g_assert_null( + result + ); + + g_clear_error( + &error + ); + + g_object_unref( + cancellable + ); +} + +/** + * @brief Vérifie l'annulation d'un processus en cours. + */ +static void test_tool_process_cancelled_during_run( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + + GCancellable *cancellable = NULL; + GThread *cancellation_thread = NULL; + + ToolProcessCancellationData cancellation_data; + + gint64 start_time = 0; + gint64 end_time = 0; + gint64 elapsed_microseconds = 0; + + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "while :\n" + "do\n" + " :\n" + "done\n" + ); + + cancellable = g_cancellable_new(); + + cancellation_data.cancellable = + cancellable; + + cancellation_data.delay_microseconds = + 200000; + + cancellation_thread = g_thread_new( + "tool-process-canceller", + test_tool_process_cancel_after_delay, + &cancellation_data + ); + + g_assert_nonnull( + cancellation_thread + ); + + start_time = g_get_monotonic_time(); + + g_assert_false( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + cancellable, + &result, + &error + ) + ); + + end_time = g_get_monotonic_time(); + + g_thread_join( + cancellation_thread + ); + + elapsed_microseconds = + end_time - start_time; + + g_assert_error( + error, + TOOL_PROCESS_ERROR, + TOOL_PROCESS_ERROR_CANCELLED + ); + + g_assert_null( + result + ); + + /* + * L'exécution doit être interrompue bien avant plusieurs secondes. + */ + g_assert_cmpint( + elapsed_microseconds, + <, + 3000000 + ); + + g_clear_error( + &error + ); + + g_object_unref( + cancellable + ); +} + +/** + * @brief Vérifie qu'une exécution sans sortie produit des GBytes vides. + */ +static void test_tool_process_empty_output( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + + GBytes *stdout_bytes = NULL; + GBytes *stderr_bytes = NULL; + + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "exit 0\n" + ); + + g_assert_true( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_no_error( + error + ); + + stdout_bytes = + tool_process_result_ref_stdout( + result + ); + + stderr_bytes = + tool_process_result_ref_stderr( + result + ); + + g_assert_nonnull( + stdout_bytes + ); + + g_assert_nonnull( + stderr_bytes + ); + + g_assert_cmpuint( + g_bytes_get_size( + stdout_bytes + ), + ==, + 0 + ); + + g_assert_cmpuint( + g_bytes_get_size( + stderr_bytes + ), + ==, + 0 + ); + + g_bytes_unref( + stdout_bytes + ); + + g_bytes_unref( + stderr_bytes + ); + + tool_process_result_free( + result + ); +} + +/** + * @brief Vérifie la conservation d'une sortie non UTF-8. + */ +static void test_tool_process_non_utf8_output( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + GBytes *stdout_bytes = NULL; + + gconstpointer stdout_data = NULL; + const guint8 *stdout_octets = NULL; + + gsize stdout_size = 0; + + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "printf '\\377'\n" + ); + + g_assert_true( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_no_error( + error + ); + + stdout_bytes = + tool_process_result_ref_stdout( + result + ); + + g_assert_nonnull( + stdout_bytes + ); + + stdout_data = g_bytes_get_data( + stdout_bytes, + &stdout_size + ); + + stdout_octets = stdout_data; + + g_assert_cmpuint( + stdout_size, + ==, + 1 + ); + + g_assert_nonnull( + stdout_octets + ); + + g_assert_cmpuint( + stdout_octets[0], + ==, + 0xFF + ); + + g_bytes_unref( + stdout_bytes + ); + + tool_process_result_free( + result + ); +} + +/** + * @brief Vérifie la représentation d'une terminaison par signal. + */ +static void test_tool_process_signaled( + ToolProcessFixture *fixture, + gconstpointer user_data +) +{ + ToolProcessResult *result = NULL; + GError *error = NULL; + + (void) user_data; + + test_tool_process_write_script( + fixture, + "#!/bin/sh\n" + "kill -TERM $$\n" + ); + + g_assert_true( + tool_process_run( + fixture->tool_path, + NULL, + NULL, + NULL, + &result, + &error + ) + ); + + g_assert_no_error( + error + ); + + g_assert_nonnull( + result + ); + + g_assert_false( + tool_process_result_exited_normally( + result + ) + ); + + g_assert_true( + tool_process_result_was_signaled( + result + ) + ); + + g_assert_cmpint( + tool_process_result_get_exit_status( + result + ), + ==, + -1 + ); + + g_assert_cmpint( + tool_process_result_get_termination_signal( + result + ), + ==, + SIGTERM + ); + + g_assert_false( + tool_process_result_is_success( + result + ) + ); + + tool_process_result_free( + result + ); +} + +int main( + int argc, + char **argv +) +{ + g_test_init( + &argc, + &argv, + NULL + ); + + g_test_add( + "/tool_process/invalid_arguments", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_invalid_arguments, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/no_arguments", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_no_arguments, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/literal_arguments", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_literal_arguments, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/stdout_stderr", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_stdout_stderr, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/nonzero_exit", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_nonzero_exit, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/missing_executable", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_missing_executable, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/working_directory", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_working_directory, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/cancelled_before_start", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_cancelled_before_start, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/cancelled_during_run", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_cancelled_during_run, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/empty_output", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_empty_output, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/non_utf8_output", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_non_utf8_output, + test_tool_process_fixture_teardown + ); + + g_test_add( + "/tool_process/signaled", + ToolProcessFixture, + NULL, + test_tool_process_fixture_setup, + test_tool_process_signaled, + test_tool_process_fixture_teardown + ); + + return g_test_run(); +}