feat(osint): integrate public username and email research tools
This commit is contained in:
parent
4e97d0979a
commit
6cfd26c2bd
5 changed files with 105 additions and 2 deletions
53
TICKET-104.md
Normal file
53
TICKET-104.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# 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`.
|
||||||
|
|
||||||
1
reports/report_meline59760_simple.json
Normal file
1
reports/report_meline59760_simple.json
Normal 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}}
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue