diff --git a/TICKET-104.md b/TICKET-104.md deleted file mode 100644 index bfdfc28..0000000 --- a/TICKET-104.md +++ /dev/null @@ -1,53 +0,0 @@ -# Ticket 104 — Lier et exploiter les extractions dans l’enquête - -## Objectif - -Rendre les fichiers produits par les outils d’analyse (OSINT, ExifTool, -analyse EML, OCR, récupération PDF, etc.) traçables et directement -exploitables depuis l’interface de l’enquête. - -## Besoins - -- Enregistrer chaque extraction comme une preuve traitée identifiable. -- Conserver la référence de l’entité ou de la preuve à l’origine de - l’extraction. -- Afficher les extractions liées dans la fiche de l’objet source. -- Permettre la prévisualisation des fichiers texte avec défilement. -- Afficher les résultats texte dans les fiches concernées sans ouvrir le - gestionnaire de fichiers. -- Depuis l’onglet **Dossier**, permettre de glisser-déposer une extraction - sur le graphe afin de proposer sa création ou son rattachement à une - entité. -- Préserver l’intégrité et le chemin relatif des fichiers produits dans - `02_Preuves_Traitees/Extractions`. - -## Contraintes d’implémentation - -- Utiliser les associations persistées preuve–entité existantes, sans - dupliquer les fichiers ni les résultats. -- Refuser les chemins sortant du dossier de l’enquête. -- Ne jamais exécuter automatiquement le contenu d’une extraction déposée - dans le graphe. -- Conserver les commentaires Doxygen des nouvelles fonctions publiques. - -## Critères d’acceptation - -1. Une extraction terminée apparaît comme preuve traitée et indique sa - source. -2. La fiche de la source liste les extractions associées. -3. Un fichier `.txt` peut être lu dans une zone défilante depuis sa fiche et - depuis l’onglet Dossier. -4. Un glisser-déposer sur le graphe ouvre une action explicite de création ou - d’association, sans modification silencieuse. -5. Les associations et chemins sont conservés après fermeture et réouverture - de l’enquête. -6. Les tests unitaires couvrent l’association, les chemins invalides et le - transfert Dossier → graphe. - -## Vérification - -- `make -j2` -- `make test` -- Vérification manuelle avec une extraction OSINT et un fichier texte dans - `02_Preuves_Traitees/Extractions`. - diff --git a/TICKET-105.md b/TICKET-105.md new file mode 100644 index 0000000..c867230 --- /dev/null +++ b/TICKET-105.md @@ -0,0 +1,45 @@ +# Ticket 105 — Glisser-déposer des extractions vers le graphe + +## Objectif + +Permettre de prendre une extraction depuis `Dossier → Extractions` et de la +déposer dans le graphe afin de créer ou compléter les liens de l’enquête. + +## Fonctionnement attendu + +1. Une extraction sélectionnée dans la Sidebar peut être déplacée. +2. Le graphe accepte le dépôt uniquement sur une zone valide. +3. Une fenêtre de confirmation affiche le fichier, sa source et son contenu + textuel disponible. +4. L’utilisateur choisit explicitement : + - créer une nouvelle entité à partir de l’extraction ; + - rattacher l’extraction à une entité existante ; + - annuler. +5. L’association est enregistrée en base et visible dans la fiche concernée. +6. Une relation graphique peut être créée sans déplacer ni dupliquer le + fichier original. + +## Contraintes + +- Ne jamais créer de relation silencieusement. +- Refuser les fichiers hors du dossier de l’enquête. +- Conserver les associations déjà présentes. +- Utiliser les API GTK de glisser-déposer et les commentaires Doxygen prévus + par le projet. + +## Critères d’acceptation + +- Un fichier `.txt` d’OSINT est déplaçable depuis la Sidebar. +- Le dépôt sur le graphe ouvre le dialogue de choix. +- L’entité ou l’association créée survit à la réouverture de l’enquête. +- Les dépôts annulés ne modifient ni la base ni le graphe. +- Tests unitaires des chemins invalides, du choix d’action et de la + persistance. + +## Vérification + +```sh +make -j2 +make test +``` + diff --git a/database/schema_current.sql b/database/schema_current.sql index d5f6f1b..652f24d 100644 --- a/database/schema_current.sql +++ b/database/schema_current.sql @@ -65,6 +65,23 @@ CREATE TABLE IF NOT EXISTS graph_node_positions * des relations. L'ancienne table reste présente pour assurer la compatibilité * avec les enquêtes créées avant l'introduction des nœuds de relation. */ +CREATE TABLE IF NOT EXISTS extractions +( + id TEXT PRIMARY KEY, + evidence_id TEXT, + source_kind TEXT NOT NULL, + source_id TEXT NOT NULL, + tool_id TEXT NOT NULL, + created_at TEXT NOT NULL, + FOREIGN KEY (evidence_id) REFERENCES preuves(id) ON DELETE CASCADE, + CHECK (source_kind IN ('evidence', 'entity')), + CHECK (length(trim(source_id)) > 0), + CHECK (length(trim(tool_id)) > 0), + CHECK (length(created_at) = 20) +); +CREATE INDEX IF NOT EXISTS idx_extractions_source + ON extractions(source_kind, source_id); + CREATE TABLE IF NOT EXISTS graph_layout_positions ( node_id TEXT PRIMARY KEY, diff --git a/database/schema_v7.sql b/database/schema_v7.sql new file mode 100644 index 0000000..954133e --- /dev/null +++ b/database/schema_v7.sql @@ -0,0 +1,17 @@ +/* Migration SQLite V6 vers V7 : traçabilité des extractions. */ +CREATE TABLE IF NOT EXISTS extractions +( + id TEXT PRIMARY KEY, + evidence_id TEXT, + source_kind TEXT NOT NULL, + source_id TEXT NOT NULL, + tool_id TEXT NOT NULL, + created_at TEXT NOT NULL, + FOREIGN KEY (evidence_id) REFERENCES preuves(id) ON DELETE CASCADE, + CHECK (source_kind IN ('evidence', 'entity')), + CHECK (length(trim(source_id)) > 0), + CHECK (length(trim(tool_id)) > 0), + CHECK (length(created_at) = 20) +); +CREATE INDEX IF NOT EXISTS idx_extractions_source + ON extractions(source_kind, source_id); diff --git a/docs/DEPENDENCE.md b/docs/DEPENDENCE.md new file mode 100644 index 0000000..7ac6d94 --- /dev/null +++ b/docs/DEPENDENCE.md @@ -0,0 +1,90 @@ +# Dépendances externes + +Ce fichier recense les outils utilisés par LabFy Investigation et leur +installation. Il sera complété à chaque intégration d’un nouvel outil. + +## Principe d’installation + +Les dépendances système sont installées avec le gestionnaire de paquets de la +distribution. Les outils OSINT Python sont installés dans un environnement +isolé afin de ne pas modifier le Python système. + +Le dossier recommandé pour les exécutables OSINT est : + +```text +~/.local/share/labfy-osint/bin +``` + +L’application ajoute automatiquement ce dossier au `PATH` lorsqu’il existe. +Pour les terminaux et les scripts, on peut également ajouter dans +`~/.profile` (ou `~/.zshrc`) : + +```sh +export PATH="$HOME/.local/share/labfy-osint/bin:$PATH" +``` + +## Outils système + +| Fonction | Exécutable | Arch Linux (pacman) | Ubuntu/Debian (apt) | +| --- | --- | --- | --- | +| OCR | `tesseract` | `sudo pacman -S tesseract tesseract-data-fra` | `sudo apt install tesseract-ocr tesseract-ocr-fra` | +| Métadonnées | `exiftool` | `sudo pacman -S perl-image-exiftool` | `sudo apt install libimage-exiftool-perl` | +| Recherche DNS | `dig` | `sudo pacman -S bind` | `sudo apt install dnsutils` | +| Résolution réseau | `host` | `sudo pacman -S bind` | `sudo apt install bind9-host` | +| WHOIS | `whois` | `sudo pacman -S whois` | `sudo apt install whois` | +| Requêtes HTTPS | `curl` | `sudo pacman -S curl` | `sudo apt install curl` | +| Certificats/TLS | `openssl` | `sudo pacman -S openssl` | `sudo apt install openssl` | +| Récupération PDF | `qpdf` | `sudo pacman -S qpdf` | `sudo apt install qpdf` | +| Audit de mot de passe PDF | `john`, `pdf2john` | `sudo pacman -S john` | `sudo apt install john` | + +Après installation, vérifier les outils avec : + +```sh +command -v tesseract exiftool dig host whois curl openssl qpdf john pdf2john +``` + +## Outils OSINT Python + +Préparer l’environnement local : + +```sh +python3 -m venv ~/.local/share/labfy-osint +~/.local/share/labfy-osint/bin/python -m pip install --upgrade pip +~/.local/share/labfy-osint/bin/pip install sherlock-project maigret holehe +``` + +Les commandes attendues par l’application sont : + +```text +~/.local/share/labfy-osint/bin/sherlock +~/.local/share/labfy-osint/bin/maigret +~/.local/share/labfy-osint/bin/holehe +``` + +Sur Ubuntu, installer au préalable les outils de compilation et Python : + +```sh +sudo apt install python3 python3-venv python3-pip +``` + +Sur Arch Linux : + +```sh +sudo pacman -S python python-pip +``` + +## Vérification dans l’application + +Au démarrage, le catalogue des outils indique le nombre d’exécutables +disponibles et leurs versions. Une commande non détectée doit d’abord être +vérifiée avec `command -v`, puis avec son option de version (`--version` ou +`-V` selon l’outil). + +## Notes + +- Ne pas installer ces outils dans le dépôt Git. +- Ne jamais stocker de jetons, mots de passe ou données privées dans ce + fichier. +- Les versions peuvent varier selon la distribution ; noter ici toute + commande particulière nécessaire à Ubuntu des forces de l’ordre. + diff --git a/include/dao/extraction_dao.h b/include/dao/extraction_dao.h new file mode 100644 index 0000000..385c440 --- /dev/null +++ b/include/dao/extraction_dao.h @@ -0,0 +1,15 @@ +#ifndef LABFY_INVESTIGATION_EXTRACTION_DAO_H +#define LABFY_INVESTIGATION_EXTRACTION_DAO_H + +#include "database/database.h" +#include + +typedef struct ExtractionDao ExtractionDao; + +ExtractionDao *extraction_dao_new(Database *database, GError **error); +void extraction_dao_free(ExtractionDao *dao); +gboolean extraction_dao_insert(ExtractionDao *dao, const char *id, + const char *evidence_id, const char *source_kind, const char *source_id, + const char *tool_id, const char *created_at, GError **error); + +#endif diff --git a/include/database/schema.h b/include/database/schema.h index 6ec52ef..2ef2769 100644 --- a/include/database/schema.h +++ b/include/database/schema.h @@ -87,6 +87,8 @@ bool schema_install_v5(Database *database); /** @brief Installe la catégorie d'identité usurpée du schéma V6. */ bool schema_install_v6(Database *database); +bool schema_install_v7(Database *database); + /** * @brief Garantit la présence des extensions du schéma courant V2. * diff --git a/meline59760.txt b/meline59760.txt new file mode 100644 index 0000000..8c8d031 --- /dev/null +++ b/meline59760.txt @@ -0,0 +1,4 @@ +https://forum.velomania.ru/member.php?username=meline59760 +https://www.youtube.com/@meline59760 +https://www.baby.ru/u/meline59760 +Total Websites Username Detected On : 3 diff --git a/src/core/application.c b/src/core/application.c index 3675bbf..1dc4701 100644 --- a/src/core/application.c +++ b/src/core/application.c @@ -44,6 +44,7 @@ #include "dao/relation_dao.h" #include "dao/relation_evidence_dao.h" #include "dao/osint_execution_dao.h" +#include "dao/extraction_dao.h" #include "models/evidence_type.h" #include "widgets/evidence_list_model.h" #include "widgets/evidence_category_model.h" @@ -426,6 +427,7 @@ static void application_on_osint_search_completed(BackgroundTask *task, char *output_directory = NULL; char *output_name = NULL; char *output_path = NULL; + guint output_suffix = 0; const InvestigationProject *project = NULL; GError *error = NULL; if (context == NULL || context->application == NULL) return; @@ -458,12 +460,40 @@ static void application_on_osint_search_completed(BackgroundTask *task, output_name = g_strdup_printf("%s-%s.txt", context->target_identifier, context->action_identifier); output_path = g_build_filename(output_directory, output_name, NULL); + while (g_file_test(output_path, G_FILE_TEST_EXISTS)) + { + char *unique_name = g_strdup_printf("%s-%s (%u).txt", + context->target_identifier, context->action_identifier, + ++output_suffix); + g_free(output_name); + g_free(output_path); + output_name = unique_name; + output_path = g_build_filename(output_directory, output_name, NULL); + } details = g_strdup_printf("Cible : %s\nAction : %s\n\n%s%s%s", context->target_value, context->action_identifier, stdout_text != NULL ? stdout_text : "", stderr_text != NULL && stderr_text[0] != '\0' ? "\n\nErreur :\n" : "", stderr_text != NULL ? stderr_text : ""); if (!g_file_set_contents(output_path, details, -1, &error)) goto failure; + { + ExtractionDao *extraction_dao = extraction_dao_new( + investigation_session_get_database(context->application->session), + &error); + char *extraction_id = g_uuid_string_random(); + char *created_at = application_osint_create_timestamp(); + if (extraction_dao == NULL || extraction_id == NULL || created_at == NULL || + !extraction_dao_insert(extraction_dao, extraction_id, NULL, "entity", + context->target_identifier, context->action_identifier, + created_at, &error)) + { + extraction_dao_free(extraction_dao); + g_free(extraction_id); g_free(created_at); + goto failure; + } + extraction_dao_free(extraction_dao); + g_free(extraction_id); g_free(created_at); + } application_message_dialog_present_details_action( main_window_get_window(context->application->main_window), APPLICATION_MESSAGE_DIALOG_INFORMATION, "Résultat de la recherche OSINT", @@ -5018,6 +5048,9 @@ static void application_on_tree_node_selected( else { g_print("Type : fichier\n"); + main_window_set_evidence_preview( + application->main_window, + investigation_node_get_path(node), node_name); } } diff --git a/src/dao/extraction_dao.c b/src/dao/extraction_dao.c new file mode 100644 index 0000000..5cacec2 --- /dev/null +++ b/src/dao/extraction_dao.c @@ -0,0 +1,44 @@ +#include "dao/extraction_dao.h" +#include "database/statement.h" +#include +#include + +struct ExtractionDao { Database *database; }; + +ExtractionDao *extraction_dao_new(Database *database, GError **error) +{ + if (database == NULL) { g_set_error_literal(error, G_IO_ERROR, + G_IO_ERROR_INVALID_ARGUMENT, "Connexion SQLite invalide."); return NULL; } + ExtractionDao *dao = g_new0(ExtractionDao, 1); + dao->database = database; + return dao; +} + +void extraction_dao_free(ExtractionDao *dao) { g_free(dao); } + +gboolean extraction_dao_insert(ExtractionDao *dao, const char *id, + const char *evidence_id, const char *source_kind, const char *source_id, + const char *tool_id, const char *created_at, GError **error) +{ + DatabaseStatement *statement = NULL; + gboolean ok = FALSE; + (void) error; + if (dao == NULL || id == NULL || evidence_id == NULL || source_kind == NULL || + source_id == NULL || tool_id == NULL || created_at == NULL) return FALSE; + statement = database_statement_prepare(dao->database, + "INSERT INTO extractions(id,evidence_id,source_kind,source_id,tool_id,created_at) VALUES(?,?,?,?,?,?);"); + if (statement == NULL) return FALSE; + ok = database_statement_bind_text(statement, 1, id) && + (evidence_id != NULL ? database_statement_bind_text(statement, 2, evidence_id) : + database_statement_bind_null(statement, 2)) && + database_statement_bind_text(statement, 3, source_kind) && + database_statement_bind_text(statement, 4, source_id) && + database_statement_bind_text(statement, 5, tool_id) && + database_statement_bind_text(statement, 6, created_at) && + database_statement_step(statement) == DATABASE_STATEMENT_STEP_DONE; + if (!ok && error != NULL && *error == NULL) + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Impossible d'insérer la traçabilité de l'extraction."); + database_statement_finalize(statement); + return ok; +} diff --git a/src/database/database.c b/src/database/database.c index 3a61455..6ea91d2 100644 --- a/src/database/database.c +++ b/src/database/database.c @@ -16,12 +16,12 @@ /** * @brief Version actuelle du schéma SQLite. */ -#define DATABASE_SCHEMA_VERSION_CURRENT 6 +#define DATABASE_SCHEMA_VERSION_CURRENT 7 /** * @brief Version actuelle sous forme textuelle pour metadata. */ -#define DATABASE_SCHEMA_VERSION_CURRENT_TEXT "6" +#define DATABASE_SCHEMA_VERSION_CURRENT_TEXT "7" /** * @brief Nom de l'application enregistré dans les métadonnées. @@ -958,6 +958,12 @@ bool database_migrate_to_latest( schema_version = 6; break; + case 6: + if (!schema_install_v7(database)) + return false; + schema_version = 7; + break; + default: database_set_error( database, diff --git a/src/database/schema.c b/src/database/schema.c index e56ef90..093af4b 100644 --- a/src/database/schema.c +++ b/src/database/schema.c @@ -227,6 +227,12 @@ bool schema_install_v6(Database *database) "la migration SQLite V6"); } +bool schema_install_v7(Database *database) +{ + return schema_execute_file(database, "database/schema_v7.sql", + "la migration SQLite V7"); +} + bool schema_ensure_current( Database *database ) diff --git a/src/widgets/workspace.c b/src/widgets/workspace.c index a72ce7f..f4da8d7 100644 --- a/src/widgets/workspace.c +++ b/src/widgets/workspace.c @@ -2620,9 +2620,18 @@ void workspace_set_selected_node( gtk_stack_set_visible_child_name( GTK_STACK(workspace->stack), - WORKSPACE_PAGE_NODE_INFORMATION + node_type == INVESTIGATION_NODE_FILE + ? WORKSPACE_PAGE_EVIDENCE_INFORMATION + : WORKSPACE_PAGE_NODE_INFORMATION ); + if (node_type == INVESTIGATION_NODE_FILE) + { + gtk_label_set_text(GTK_LABEL(workspace->evidence_name_label), + node_name != NULL ? node_name : "Fichier"); + workspace_set_evidence_preview(workspace, node_path, node_name); + } + g_free(children_text); g_free(parent_text); } @@ -2888,7 +2897,10 @@ void workspace_set_evidence_preview(Workspace *workspace, else { char *message = NULL; - if (mime_type != NULL && g_strcmp0(mime_type, "text/plain") == 0) + if ((mime_type != NULL && g_strcmp0(mime_type, "text/plain") == 0) || + g_str_has_suffix(file_path, ".txt") || + g_str_has_suffix(file_path, ".log") || + g_str_has_suffix(file_path, ".json")) { char *contents = NULL; gsize length = 0; diff --git a/tests/test_database.c b/tests/test_database.c index e5859c6..977213c 100644 --- a/tests/test_database.c +++ b/tests/test_database.c @@ -525,7 +525,7 @@ static void test_database_initialize_valid_database(void) "FROM investigation;" ); - assert(strcmp(schema_version, "6") == 0); + assert(strcmp(schema_version, "7") == 0); test_database_assert_table_exists(database, "osint_executions"); test_database_assert_table_exists(database, "osint_execution_entities"); test_database_assert_table_exists(database, "osint_execution_relations");