feat(osint): integrate public username and email research tools

This commit is contained in:
grayTerminal-sh 2026-07-23 12:08:24 +02:00
parent 4e97d0979a
commit 6cfd26c2bd
5 changed files with 105 additions and 2 deletions

53
TICKET-104.md Normal file
View file

@ -0,0 +1,53 @@
# Ticket 104 — Lier et exploiter les extractions dans lenquête
## Objectif
Rendre les fichiers produits par les outils danalyse (OSINT, ExifTool,
analyse EML, OCR, récupération PDF, etc.) traçables et directement
exploitables depuis linterface de lenquête.
## Besoins
- Enregistrer chaque extraction comme une preuve traitée identifiable.
- Conserver la référence de lentité ou de la preuve à lorigine de
lextraction.
- Afficher les extractions liées dans la fiche de lobjet 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 longlet **Dossier**, permettre de glisser-déposer une extraction
sur le graphe afin de proposer sa création ou son rattachement à une
entité.
- Préserver lintégrité et le chemin relatif des fichiers produits dans
`02_Preuves_Traitees/Extractions`.
## Contraintes dimplémentation
- Utiliser les associations persistées preuveentité existantes, sans
dupliquer les fichiers ni les résultats.
- Refuser les chemins sortant du dossier de lenquête.
- Ne jamais exécuter automatiquement le contenu dune extraction déposée
dans le graphe.
- Conserver les commentaires Doxygen des nouvelles fonctions publiques.
## Critères dacceptation
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 longlet Dossier.
4. Un glisser-déposer sur le graphe ouvre une action explicite de création ou
dassociation, sans modification silencieuse.
5. Les associations et chemins sont conservés après fermeture et réouverture
de lenquête.
6. Les tests unitaires couvrent lassociation, 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`.

View file

@ -0,0 +1 @@
{"Instagram": {"site": {"tags": ["networking", "photo", "social"], "checkType": "message", "urlProbe": "https://www.instagram.com/api/v1/users/web_profile_info/?username={username}", "headers": {"x-ig-app-id": "936619743392459", "referer": "https://www.instagram.com/"}, "presenseStrs": ["\"biography\""], "absenceStrs": ["Sorry, this page isn't available.", "dialog-404"], "errors": {"Login \u2022 Instagram": "Login required", "\"routePath\":\"\\/\"": "Login required (rate-limited or session blocked)", "Please wait a few minutes before you try again.": "Rate limited"}, "alexaRank": 4, "urlMain": "https://www.instagram.com/", "url": "https://www.instagram.com/{username}/", "usernameClaimed": "cristiano", "usernameUnclaimed": "noonewouldeverusethis77777", "protection": ["tls_fingerprint"]}, "username": "meline59760", "keywords": [], "parsing_enabled": true, "url_main": "https://www.instagram.com/", "cookies": null, "url_user": "https://www.instagram.com/meline59760/", "url_probe": "https://www.instagram.com/api/v1/users/web_profile_info/?username=meline59760", "ids_usernames": {"meline59760": "username"}, "ids_links": [], "status": {"username": "meline59760", "site_name": "Instagram", "url": "https://www.instagram.com/meline59760/", "status": "Claimed", "ids": {"username": "meline59760", "fullname": "M\u00e9line Mignon", "id": "23685360083", "image": "https://scontent-cdg4-1.cdninstagram.com/v/t51.82787-19/724092486_18109543660784084_9144824295371353753_n.jpg?stp=dst-jpg_s320x320_tt6&efg=eyJ2ZW5jb2RlX3RhZyI6InByb2ZpbGVfcGljLmRqYW5nby4zMjAuYzIifQ&_nc_ht=scontent-cdg4-1.cdninstagram.com&_nc_cat=108&_nc_oc=Q6cZ2gHFderv5RK6nIlzUJzRyHTOdeaqKM3gUV4Vo2DVognufvoKgRywYw-bMdEu_y4IFC4&_nc_ohc=_hNKnSuKlbUQ7kNvwE9OlKd&_nc_gid=n6RBziFUv7ZEDBwwlMp9Uw&edm=AOQ1c0wBAAAA&ccb=7-5&oh=00_AQD6CBmIB7n96DndM11ZrubcUJ5rJVODRwrNSpuzpDMFRg&oe=6A67B191&_nc_sid=8b3546", "bio": "\ud83d\udc95", "facebook_uid": "17841423522510670", "is_business": "False", "is_joined_recently": "False", "is_private": "True", "is_verified": "False", "follower_count": "104", "following_count": "16", "_extractor": "Instagram GraphQL"}, "tags": ["networking", "photo", "social"], "keywords": [], "keyword_match_status": "No Keywords"}, "http_status": 200, "is_similar": false, "rank": 4}, "ChaturBate": {"site": {"protection": ["ip_reputation", "tls_fingerprint"], "tags": ["porn", "streaming", "video", "webcam"], "checkType": "status_code", "alexaRank": 3611, "urlMain": "https://chaturbate.com", "url": "https://chaturbate.com/{username}/", "usernameClaimed": "cute18cute", "usernameUnclaimed": "noonewouldeverusethis77777", "errors": {"Just a moment": "Cloudflare challenge", "challenges.cloudflare.com": "Cloudflare challenge"}}, "username": "meline59760", "keywords": [], "parsing_enabled": true, "url_main": "https://chaturbate.com", "cookies": null, "url_user": "https://chaturbate.com/meline59760/", "url_probe": "https://chaturbate.com/meline59760/", "status": {"username": "meline59760", "site_name": "ChaturBate", "url": "https://chaturbate.com/meline59760/", "status": "Claimed", "ids": {}, "tags": ["porn", "streaming", "video", "webcam"], "keywords": [], "keyword_match_status": "No Keywords"}, "http_status": 200, "is_similar": false, "rank": 3611}}

View file

@ -29,7 +29,7 @@
/** /**
* @brief Position initiale de la séparation verticale. * @brief Position initiale de la séparation verticale.
*/ */
#define MAIN_WINDOW_TASK_PANEL_POSITION 430 #define MAIN_WINDOW_TASK_PANEL_POSITION 390
/** /**
* @brief Titre par défaut de la fenêtre. * @brief Titre par défaut de la fenêtre.

View file

@ -32,6 +32,7 @@ typedef struct
GtkWidget *progress_bar; GtkWidget *progress_bar;
GtkWidget *detail_label; GtkWidget *detail_label;
GtkWidget *cancel_button; GtkWidget *cancel_button;
GtkWidget *remove_button;
} TaskPanelRow; } TaskPanelRow;
/** /**
@ -188,6 +189,19 @@ static void task_panel_on_clear_clicked(
); );
} }
/** @brief Retire uniquement la tâche terminée de cette ligne. */
static void task_panel_on_remove_clicked(GtkButton *button, gpointer user_data)
{
TaskPanel *task_panel = user_data;
BackgroundTask *task = NULL;
if (task_panel == NULL || task_panel->task_manager == NULL) return;
task = g_object_get_data(G_OBJECT(button), "labfy-background-task");
if (task != NULL && background_task_get_state(task) !=
BACKGROUND_TASK_STATE_RUNNING && background_task_get_state(task) !=
BACKGROUND_TASK_STATE_PENDING)
task_manager_remove(task_panel->task_manager, task);
}
/** /**
* @brief Réagit aux changements structurels du gestionnaire. * @brief Réagit aux changements structurels du gestionnaire.
* *
@ -437,6 +451,9 @@ static void task_panel_row_update(
task_row->cancel_button, task_row->cancel_button,
state == BACKGROUND_TASK_STATE_RUNNING state == BACKGROUND_TASK_STATE_RUNNING
); );
gtk_widget_set_visible(task_row->remove_button,
state != BACKGROUND_TASK_STATE_RUNNING &&
state != BACKGROUND_TASK_STATE_PENDING);
g_clear_error( g_clear_error(
&error &error
@ -564,6 +581,7 @@ static TaskPanelRow *task_panel_create_task_row(
gtk_button_new_with_label( gtk_button_new_with_label(
"Annuler" "Annuler"
); );
task_row->remove_button = gtk_button_new_with_label("Nettoyer");
gtk_widget_set_halign( gtk_widget_set_halign(
task_row->cancel_button, task_row->cancel_button,
@ -593,6 +611,10 @@ static TaskPanelRow *task_panel_create_task_row(
), ),
task_panel task_panel
); );
g_object_set_data(G_OBJECT(task_row->remove_button),
"labfy-background-task", task_row->task);
g_signal_connect(task_row->remove_button, "clicked",
G_CALLBACK(task_panel_on_remove_clicked), task_panel);
gtk_box_append( gtk_box_append(
GTK_BOX(header_box), GTK_BOX(header_box),
@ -608,6 +630,7 @@ static TaskPanelRow *task_panel_create_task_row(
GTK_BOX(header_box), GTK_BOX(header_box),
task_row->cancel_button task_row->cancel_button
); );
gtk_box_append(GTK_BOX(header_box), task_row->remove_button);
task_row->progress_bar = task_row->progress_bar =
gtk_progress_bar_new(); gtk_progress_bar_new();

View file

@ -97,6 +97,7 @@ struct Workspace
GtkWidget *recover_pdf_password_button; GtkWidget *recover_pdf_password_button;
GtkWidget *evidence_preview_stack; GtkWidget *evidence_preview_stack;
GtkWidget *evidence_preview_status; GtkWidget *evidence_preview_status;
GtkWidget *evidence_preview_text;
GtkWidget *evidence_preview_picture; GtkWidget *evidence_preview_picture;
GtkWidget *evidence_preview_video; GtkWidget *evidence_preview_video;
GtkWidget *evidence_preview_expand_button; GtkWidget *evidence_preview_expand_button;
@ -1425,6 +1426,12 @@ Workspace *workspace_new(void)
gtk_video_set_autoplay(GTK_VIDEO(workspace->evidence_preview_video), FALSE); gtk_video_set_autoplay(GTK_VIDEO(workspace->evidence_preview_video), FALSE);
gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack), gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack),
workspace->evidence_preview_status, "status"); workspace->evidence_preview_status, "status");
workspace->evidence_preview_text = gtk_text_view_new();
gtk_text_view_set_editable(GTK_TEXT_VIEW(workspace->evidence_preview_text), FALSE);
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(workspace->evidence_preview_text), GTK_WRAP_WORD_CHAR);
gtk_widget_set_vexpand(workspace->evidence_preview_text, TRUE);
gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack),
workspace->evidence_preview_text, "text");
gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack), gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack),
workspace->evidence_preview_picture, "image"); workspace->evidence_preview_picture, "image");
gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack), gtk_stack_add_named(GTK_STACK(workspace->evidence_preview_stack),
@ -2880,7 +2887,25 @@ void workspace_set_evidence_preview(Workspace *workspace,
} }
else else
{ {
char *message = g_strdup_printf( char *message = NULL;
if (mime_type != NULL && g_strcmp0(mime_type, "text/plain") == 0)
{
char *contents = NULL;
gsize length = 0;
if (g_file_get_contents(file_path, &contents, &length, NULL))
{
GtkTextBuffer *buffer = gtk_text_view_get_buffer(
GTK_TEXT_VIEW(workspace->evidence_preview_text));
gtk_text_buffer_set_text(buffer, contents, (gint) MIN(length, 200000));
gtk_stack_set_visible_child_name(GTK_STACK(
workspace->evidence_preview_stack), "text");
workspace->evidence_preview_is_video = FALSE;
g_free(contents);
goto text_preview_ready;
}
g_free(contents);
}
message = g_strdup_printf(
"Aperçu indisponible pour %s%s%s.", "Aperçu indisponible pour %s%s%s.",
display_name != NULL ? display_name : "ce fichier", display_name != NULL ? display_name : "ce fichier",
error != NULL ? " : " : "", error != NULL ? " : " : "",
@ -2892,6 +2917,7 @@ void workspace_set_evidence_preview(Workspace *workspace,
g_free(message); g_free(message);
goto cleanup; goto cleanup;
} }
text_preview_ready:
workspace->evidence_preview_path = g_strdup(file_path); workspace->evidence_preview_path = g_strdup(file_path);
gtk_widget_set_sensitive(workspace->evidence_preview_expand_button, gtk_widget_set_sensitive(workspace->evidence_preview_expand_button,
workspace->evidence_preview_path != NULL); workspace->evidence_preview_path != NULL);