feat(osint): execute DNS lookup action
This commit is contained in:
parent
b1b2320469
commit
6c8113dfea
13 changed files with 720 additions and 18 deletions
13
Makefile
13
Makefile
|
|
@ -115,6 +115,7 @@ TEST_INVESTIGATION_GRAPH_LOAD_TASK := tests/test_investigation_graph_load_task
|
|||
TEST_GRAPH_NODE_POSITION_DAO := tests/test_graph_node_position_dao
|
||||
TEST_OSINT_SELECTION_CONTEXT := tests/test_osint_selection_context
|
||||
TEST_OSINT_ACTION_CATALOG := tests/test_osint_action_catalog
|
||||
TEST_OSINT_DNS_QUERY := tests/test_osint_dns_query
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
|
|
@ -545,6 +546,11 @@ $(TEST_OSINT_ACTION_CATALOG): \
|
|||
src/models/relation_record.c
|
||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
$(TEST_OSINT_DNS_QUERY): \
|
||||
tests/test_osint_dns_query.c \
|
||||
src/models/osint_dns_query.c
|
||||
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
|
||||
|
||||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK): \
|
||||
tests/test_investigation_graph_load_task.c \
|
||||
src/core/investigation_graph_load_task.c \
|
||||
|
|
@ -614,7 +620,8 @@ test: \
|
|||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
||||
$(TEST_GRAPH_NODE_POSITION_DAO) \
|
||||
$(TEST_OSINT_SELECTION_CONTEXT) \
|
||||
$(TEST_OSINT_ACTION_CATALOG)
|
||||
$(TEST_OSINT_ACTION_CATALOG) \
|
||||
$(TEST_OSINT_DNS_QUERY)
|
||||
@echo "Exécution des tests..."
|
||||
@./$(TEST_NODE)
|
||||
@./$(TEST_TREE_MODEL)
|
||||
|
|
@ -664,6 +671,7 @@ test: \
|
|||
@$(TEST_GRAPH_NODE_POSITION_DAO)
|
||||
@$(TEST_OSINT_SELECTION_CONTEXT)
|
||||
@$(TEST_OSINT_ACTION_CATALOG)
|
||||
@$(TEST_OSINT_DNS_QUERY)
|
||||
@echo "Tous les tests sont valides."
|
||||
|
||||
%.o: %.c
|
||||
|
|
@ -719,7 +727,8 @@ clean:
|
|||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
||||
$(TEST_GRAPH_NODE_POSITION_DAO) \
|
||||
$(TEST_OSINT_SELECTION_CONTEXT) \
|
||||
$(TEST_OSINT_ACTION_CATALOG)
|
||||
$(TEST_OSINT_ACTION_CATALOG) \
|
||||
$(TEST_OSINT_DNS_QUERY)
|
||||
|
||||
-include $(DEP)
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,9 @@ Le socle actuel comprend notamment :
|
|||
- menu OSINT contextuel préparé pour les entités et relations du graphe ;
|
||||
- contexte de sélection OSINT indépendant de GTK et validé par des tests ;
|
||||
- catalogue déterministe des actions compatibles avec la sélection OSINT ;
|
||||
- disponibilité des actions OSINT synchronisée avec le registre d’outils.
|
||||
- disponibilité des actions OSINT synchronisée avec le registre d’outils ;
|
||||
- résolution DNS asynchrone avec `dig` depuis une entité domaine ;
|
||||
- affichage sélectionnable des sorties standard et d’erreur, sans persistance.
|
||||
|
||||
Les outils actuellement présents dans le catalogue initial sont :
|
||||
|
||||
|
|
|
|||
30
include/models/osint_dns_query.h
Normal file
30
include/models/osint_dns_query.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/******************************************************************************
|
||||
* @file osint_dns_query.h
|
||||
* @brief Validation des cibles utilisées pour une résolution DNS OSINT.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_OSINT_DNS_QUERY_H
|
||||
#define LABFY_INVESTIGATION_OSINT_DNS_QUERY_H
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @brief Vérifie qu'une cible est un nom de domaine DNS exploitable.
|
||||
*
|
||||
* La fonction accepte les lettres ASCII sans distinction de casse, les
|
||||
* chiffres, les tirets internes et les points séparant les labels. Un point
|
||||
* final représentant la racine DNS est également accepté.
|
||||
*
|
||||
* @param target_value Valeur à vérifier.
|
||||
*
|
||||
* @return TRUE lorsque la cible peut être transmise à dig.
|
||||
*/
|
||||
gboolean osint_dns_query_is_valid_target(
|
||||
const char *target_value
|
||||
);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
@ -49,6 +49,26 @@ void application_message_dialog_present(
|
|||
const char *message
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Affiche un message accompagné d'un contenu textuel défilable.
|
||||
*
|
||||
* Le contenu détaillé est affiché dans une zone monospace sélectionnable.
|
||||
* Les chaînes sont copiées par les widgets GTK.
|
||||
*
|
||||
* @param parent_window Fenêtre parente, ou NULL.
|
||||
* @param message_type Type de message.
|
||||
* @param title Titre de la fenêtre, ou NULL.
|
||||
* @param message Message principal, ou NULL.
|
||||
* @param details Contenu détaillé, ou NULL.
|
||||
*/
|
||||
void application_message_dialog_present_details(
|
||||
GtkWindow *parent_window,
|
||||
ApplicationMessageDialogType message_type,
|
||||
const char *title,
|
||||
const char *message,
|
||||
const char *details
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Affiche un dialogue modal demandant une confirmation.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -121,6 +121,19 @@ typedef void (*MainWindowAddRelationCallback)(
|
|||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Callback appelé lors du déclenchement d'une action OSINT.
|
||||
*
|
||||
* @param action_identifier Identifiant stable de l'action.
|
||||
* @param target_value Valeur métier ciblée.
|
||||
* @param user_data Données privées fournies par l'appelant.
|
||||
*/
|
||||
typedef void (*MainWindowOsintActionCallback)(
|
||||
const char *action_identifier,
|
||||
const char *target_value,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback de vérification d'une preuve.
|
||||
*
|
||||
|
|
@ -181,6 +194,21 @@ void main_window_set_add_relation_callback(
|
|||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback de déclenchement des actions OSINT.
|
||||
*
|
||||
* MainWindow relaie uniquement la demande provenant du Workspace.
|
||||
*
|
||||
* @param main_window Fenêtre principale.
|
||||
* @param callback Callback facultatif.
|
||||
* @param user_data Données privées transmises au callback.
|
||||
*/
|
||||
void main_window_set_osint_action_callback(
|
||||
MainWindow *main_window,
|
||||
MainWindowOsintActionCallback callback,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback du bouton « Nouvelle enquête ».
|
||||
*
|
||||
|
|
|
|||
|
|
@ -78,6 +78,21 @@ typedef void (*WorkspaceAddRelationCallback)(
|
|||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Callback appelé lors du déclenchement d'une action OSINT.
|
||||
*
|
||||
* Les chaînes sont empruntées et uniquement valides pendant l'appel.
|
||||
*
|
||||
* @param action_identifier Identifiant stable de l'action.
|
||||
* @param target_value Valeur métier ciblée par l'action.
|
||||
* @param user_data Données empruntées fournies par l'appelant.
|
||||
*/
|
||||
typedef void (*WorkspaceOsintActionCallback)(
|
||||
const char *action_identifier,
|
||||
const char *target_value,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Crée une nouvelle zone de travail.
|
||||
*
|
||||
|
|
@ -171,6 +186,22 @@ void workspace_set_osint_tool_state(
|
|||
const char *version
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback de déclenchement des actions OSINT.
|
||||
*
|
||||
* Le Workspace ne lance aucun outil externe. Il relaie uniquement l'action
|
||||
* et la valeur du contexte courant vers la couche applicative.
|
||||
*
|
||||
* @param workspace Zone de travail.
|
||||
* @param callback Callback facultatif.
|
||||
* @param user_data Données empruntées transmises au callback.
|
||||
*/
|
||||
void workspace_set_osint_action_callback(
|
||||
Workspace *workspace,
|
||||
WorkspaceOsintActionCallback callback,
|
||||
gpointer user_data
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Définit le callback de vérification de la preuve affichée.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "models/investigation_graph_layout.h"
|
||||
#include "models/investigation_graph_model.h"
|
||||
#include "models/investigation_record.h"
|
||||
#include "models/osint_dns_query.h"
|
||||
#include "core/investigation_tree_builder.h"
|
||||
#include "views/create_investigation_dialog.h"
|
||||
#include "views/folder_dialog.h"
|
||||
|
|
@ -21,6 +22,7 @@
|
|||
#include "core/task_manager.h"
|
||||
#include "core/background_task.h"
|
||||
#include "core/tool_initializer.h"
|
||||
#include "core/tool_task.h"
|
||||
#include "views/file_dialog.h"
|
||||
#include "core/evidence_import_task.h"
|
||||
#include "models/evidence_record.h"
|
||||
|
|
@ -151,6 +153,33 @@ typedef struct
|
|||
char *original_name;
|
||||
} ApplicationEvidenceIntegrityContext;
|
||||
|
||||
/**
|
||||
* @brief Contexte conservé jusqu'à la fin d'une action OSINT.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Application *application;
|
||||
char *target_value;
|
||||
} ApplicationOsintActionContext;
|
||||
|
||||
/**
|
||||
* @brief Libère le contexte d'une action OSINT.
|
||||
*/
|
||||
static void application_osint_action_context_free(
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
ApplicationOsintActionContext *context = user_data;
|
||||
|
||||
if (context == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_free(context->target_value);
|
||||
g_free(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Libère le contexte du dialogue de métadonnées.
|
||||
*/
|
||||
|
|
@ -914,6 +943,261 @@ static void application_start_tool_initialization(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convertit une sortie binaire d'outil en texte UTF-8 affichable.
|
||||
*
|
||||
* @param bytes Octets empruntés, ou NULL.
|
||||
*
|
||||
* @return Nouvelle chaîne UTF-8, jamais NULL.
|
||||
*/
|
||||
static char *application_osint_output_to_utf8(
|
||||
GBytes *bytes
|
||||
)
|
||||
{
|
||||
gconstpointer data = NULL;
|
||||
gsize data_size = 0;
|
||||
|
||||
if (bytes == NULL)
|
||||
{
|
||||
return g_strdup("");
|
||||
}
|
||||
|
||||
data = g_bytes_get_data(bytes, &data_size);
|
||||
if (data == NULL || data_size == 0U)
|
||||
{
|
||||
return g_strdup("");
|
||||
}
|
||||
|
||||
return g_utf8_make_valid(data, (gssize) data_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Présente le résultat final d'une résolution DNS.
|
||||
*/
|
||||
static void application_on_dns_lookup_completed(
|
||||
BackgroundTask *task,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
ApplicationOsintActionContext *context = user_data;
|
||||
Application *application = NULL;
|
||||
const ToolTaskResult *task_result = NULL;
|
||||
const ToolProcessResult *process_result = NULL;
|
||||
GBytes *stdout_bytes = NULL;
|
||||
GBytes *stderr_bytes = NULL;
|
||||
char *stdout_text = NULL;
|
||||
char *stderr_text = NULL;
|
||||
char *details = NULL;
|
||||
char *message = NULL;
|
||||
GError *error = NULL;
|
||||
|
||||
if (task == NULL || context == NULL || context->application == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
application = context->application;
|
||||
if (application->main_window == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (background_task_get_state(task) != BACKGROUND_TASK_STATE_COMPLETED)
|
||||
{
|
||||
error = background_task_dup_error(task);
|
||||
application_present_error(
|
||||
application,
|
||||
"Résolution DNS impossible",
|
||||
error != NULL
|
||||
? error->message
|
||||
: "L'exécution de dig a été annulée ou a échoué."
|
||||
);
|
||||
g_clear_error(&error);
|
||||
return;
|
||||
}
|
||||
|
||||
task_result = tool_task_result_from_background_task(task);
|
||||
process_result = tool_task_result_get_process_result(task_result);
|
||||
if (process_result == NULL)
|
||||
{
|
||||
application_present_error(
|
||||
application,
|
||||
"Résolution DNS incomplète",
|
||||
"La tâche s'est terminée sans résultat exploitable."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
stdout_bytes = tool_process_result_ref_stdout(process_result);
|
||||
stderr_bytes = tool_process_result_ref_stderr(process_result);
|
||||
stdout_text = application_osint_output_to_utf8(stdout_bytes);
|
||||
stderr_text = application_osint_output_to_utf8(stderr_bytes);
|
||||
|
||||
if (stdout_text != NULL && stdout_text[0] != '\0' &&
|
||||
stderr_text != NULL && stderr_text[0] != '\0')
|
||||
{
|
||||
details = g_strdup_printf(
|
||||
"Sortie standard :\n%s\n\nSortie d'erreur :\n%s",
|
||||
stdout_text,
|
||||
stderr_text
|
||||
);
|
||||
}
|
||||
else if (stdout_text != NULL && stdout_text[0] != '\0')
|
||||
{
|
||||
details = g_strdup(stdout_text);
|
||||
}
|
||||
else if (stderr_text != NULL && stderr_text[0] != '\0')
|
||||
{
|
||||
details = g_strdup(stderr_text);
|
||||
}
|
||||
else
|
||||
{
|
||||
details = g_strdup("Aucun enregistrement DNS retourné.");
|
||||
}
|
||||
|
||||
message = g_strdup_printf(
|
||||
"Cible : %s — code de sortie : %d",
|
||||
context->target_value,
|
||||
tool_process_result_get_exit_status(process_result)
|
||||
);
|
||||
|
||||
application_message_dialog_present_details(
|
||||
main_window_get_window(application->main_window),
|
||||
tool_process_result_is_success(process_result)
|
||||
? APPLICATION_MESSAGE_DIALOG_INFORMATION
|
||||
: APPLICATION_MESSAGE_DIALOG_WARNING,
|
||||
"Résultat de la résolution DNS",
|
||||
message,
|
||||
details
|
||||
);
|
||||
|
||||
g_clear_pointer(&stdout_bytes, g_bytes_unref);
|
||||
g_clear_pointer(&stderr_bytes, g_bytes_unref);
|
||||
g_free(stdout_text);
|
||||
g_free(stderr_text);
|
||||
g_free(details);
|
||||
g_free(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prépare et démarre une résolution DNS avec dig.
|
||||
*/
|
||||
static void application_start_dns_lookup(
|
||||
Application *application,
|
||||
const char *target_value
|
||||
)
|
||||
{
|
||||
ToolRegistry *tool_registry = NULL;
|
||||
ToolTask *tool_task = NULL;
|
||||
BackgroundTask *background_task = NULL;
|
||||
ApplicationOsintActionContext *context = NULL;
|
||||
const char *arguments[] = {"+noall", "+answer", target_value, NULL};
|
||||
GError *error = NULL;
|
||||
|
||||
if (application == NULL || application->task_manager == NULL ||
|
||||
application->tool_initializer == NULL || target_value == NULL ||
|
||||
!osint_dns_query_is_valid_target(target_value))
|
||||
{
|
||||
if (application != NULL)
|
||||
{
|
||||
application_present_error(
|
||||
application,
|
||||
"Résolution DNS impossible",
|
||||
"Le nom de domaine sélectionné est invalide."
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
tool_registry = tool_initializer_get_registry(application->tool_initializer);
|
||||
tool_task = tool_task_new(
|
||||
tool_registry,
|
||||
"dns.dig",
|
||||
"Résolution DNS",
|
||||
arguments,
|
||||
NULL,
|
||||
&error
|
||||
);
|
||||
|
||||
if (tool_task == NULL)
|
||||
{
|
||||
application_present_error(
|
||||
application,
|
||||
"Résolution DNS impossible",
|
||||
error != NULL ? error->message : "Impossible de préparer dig."
|
||||
);
|
||||
g_clear_error(&error);
|
||||
return;
|
||||
}
|
||||
|
||||
context = g_try_new0(ApplicationOsintActionContext, 1);
|
||||
if (context != NULL)
|
||||
{
|
||||
context->application = application;
|
||||
context->target_value = g_strdup(target_value);
|
||||
}
|
||||
|
||||
if (context == NULL || context->target_value == NULL)
|
||||
{
|
||||
application_osint_action_context_free(context);
|
||||
tool_task_free(tool_task);
|
||||
application_present_error(
|
||||
application,
|
||||
"Résolution DNS impossible",
|
||||
"Impossible de conserver le contexte de l'action."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
background_task = tool_task_get_background_task(tool_task);
|
||||
if (!task_manager_add(application->task_manager, background_task, &error) ||
|
||||
!tool_task_start(
|
||||
tool_task,
|
||||
application_on_dns_lookup_completed,
|
||||
context,
|
||||
application_osint_action_context_free,
|
||||
&error
|
||||
))
|
||||
{
|
||||
task_manager_remove(application->task_manager, background_task);
|
||||
application_osint_action_context_free(context);
|
||||
application_present_error(
|
||||
application,
|
||||
"Résolution DNS impossible",
|
||||
error != NULL ? error->message : "Impossible de démarrer dig."
|
||||
);
|
||||
g_clear_error(&error);
|
||||
tool_task_free(tool_task);
|
||||
return;
|
||||
}
|
||||
|
||||
tool_task_free(tool_task);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Traite une action OSINT relayée par la fenêtre principale.
|
||||
*/
|
||||
static void application_on_osint_action_requested(
|
||||
const char *action_identifier,
|
||||
const char *target_value,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
Application *application = user_data;
|
||||
|
||||
if (g_strcmp0(action_identifier, "dns-preview") == 0)
|
||||
{
|
||||
application_start_dns_lookup(application, target_value);
|
||||
return;
|
||||
}
|
||||
|
||||
application_present_error(
|
||||
application,
|
||||
"Action OSINT inconnue",
|
||||
"Cette action n'est pas encore prise en charge."
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Affiche une erreur dans une fenêtre GTK.
|
||||
*
|
||||
|
|
@ -5102,6 +5386,12 @@ static void application_on_activate(
|
|||
application
|
||||
);
|
||||
|
||||
main_window_set_osint_action_callback(
|
||||
application->main_window,
|
||||
application_on_osint_action_requested,
|
||||
application
|
||||
);
|
||||
|
||||
main_window_set_new_investigation_callback(
|
||||
application->main_window,
|
||||
application_on_new_investigation_requested,
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ OsintActionCatalog *osint_action_catalog_new_defaults(void)
|
|||
);
|
||||
dns_action = osint_action_new(
|
||||
"dns-preview", "Résolution DNS",
|
||||
"Préfiguration de l'adaptateur DNS.",
|
||||
"Exécute dig sur le nom de domaine sélectionné.",
|
||||
OSINT_SELECTION_CONTEXT_KIND_ENTITY, "domain_name", "dns.dig", FALSE,
|
||||
"L'outil requis n'a pas encore été vérifié."
|
||||
);
|
||||
|
|
|
|||
70
src/models/osint_dns_query.c
Normal file
70
src/models/osint_dns_query.c
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/******************************************************************************
|
||||
* @file osint_dns_query.c
|
||||
* @brief Validation des cibles utilisées pour une résolution DNS OSINT.
|
||||
******************************************************************************/
|
||||
|
||||
#include "models/osint_dns_query.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
gboolean osint_dns_query_is_valid_target(
|
||||
const char *target_value
|
||||
)
|
||||
{
|
||||
gsize target_length = 0;
|
||||
gsize label_length = 0;
|
||||
|
||||
if (target_value == NULL || target_value[0] == '\0')
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
target_length = strlen(target_value);
|
||||
if (target_length > 253U)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (gsize character_index = 0;
|
||||
character_index < target_length;
|
||||
character_index++)
|
||||
{
|
||||
const char character = target_value[character_index];
|
||||
|
||||
if (character == '.')
|
||||
{
|
||||
if (label_length == 0U)
|
||||
{
|
||||
return character_index == target_length - 1U &&
|
||||
character_index > 0U;
|
||||
}
|
||||
|
||||
if (target_value[character_index - 1U] == '-')
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (character_index == target_length - 1U)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
label_length = 0U;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(g_ascii_isalnum(character) || character == '-'))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((label_length == 0U && character == '-') || label_length >= 63U)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
label_length++;
|
||||
}
|
||||
|
||||
return label_length > 0U && target_value[target_length - 1U] != '-';
|
||||
}
|
||||
|
|
@ -355,6 +355,93 @@ void application_message_dialog_present(
|
|||
);
|
||||
}
|
||||
|
||||
void application_message_dialog_present_details(
|
||||
GtkWindow *parent_window,
|
||||
ApplicationMessageDialogType message_type,
|
||||
const char *title,
|
||||
const char *message,
|
||||
const char *details
|
||||
)
|
||||
{
|
||||
GtkWindow *dialog_window = GTK_WINDOW(gtk_window_new());
|
||||
GtkWidget *main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
|
||||
GtkWidget *title_label = gtk_label_new(
|
||||
application_message_dialog_get_safe_title(message_type, title)
|
||||
);
|
||||
GtkWidget *message_label = gtk_label_new(
|
||||
application_message_dialog_get_safe_message(message)
|
||||
);
|
||||
GtkWidget *scrolled_window = gtk_scrolled_window_new();
|
||||
GtkWidget *details_view = gtk_text_view_new();
|
||||
GtkWidget *close_button = gtk_button_new_with_label("Fermer");
|
||||
GtkTextBuffer *details_buffer = gtk_text_view_get_buffer(
|
||||
GTK_TEXT_VIEW(details_view)
|
||||
);
|
||||
|
||||
gtk_window_set_title(
|
||||
dialog_window,
|
||||
application_message_dialog_get_safe_title(message_type, title)
|
||||
);
|
||||
gtk_window_set_default_size(dialog_window, 760, 520);
|
||||
gtk_window_set_modal(dialog_window, TRUE);
|
||||
gtk_window_set_destroy_with_parent(dialog_window, TRUE);
|
||||
if (parent_window != NULL)
|
||||
{
|
||||
gtk_window_set_transient_for(dialog_window, parent_window);
|
||||
}
|
||||
|
||||
gtk_widget_set_margin_start(main_box, 20);
|
||||
gtk_widget_set_margin_end(main_box, 20);
|
||||
gtk_widget_set_margin_top(main_box, 20);
|
||||
gtk_widget_set_margin_bottom(main_box, 20);
|
||||
|
||||
gtk_widget_set_halign(title_label, GTK_ALIGN_START);
|
||||
gtk_label_set_xalign(GTK_LABEL(title_label), 0.0F);
|
||||
gtk_widget_add_css_class(title_label, "title-2");
|
||||
|
||||
gtk_widget_set_halign(message_label, GTK_ALIGN_FILL);
|
||||
gtk_label_set_xalign(GTK_LABEL(message_label), 0.0F);
|
||||
gtk_label_set_wrap(GTK_LABEL(message_label), TRUE);
|
||||
|
||||
gtk_text_view_set_editable(GTK_TEXT_VIEW(details_view), FALSE);
|
||||
gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(details_view), TRUE);
|
||||
gtk_text_view_set_monospace(GTK_TEXT_VIEW(details_view), TRUE);
|
||||
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(details_view), GTK_WRAP_NONE);
|
||||
gtk_text_buffer_set_text(
|
||||
details_buffer,
|
||||
details != NULL && details[0] != '\0'
|
||||
? details
|
||||
: APPLICATION_MESSAGE_DIALOG_DEFAULT_MESSAGE,
|
||||
-1
|
||||
);
|
||||
|
||||
gtk_scrolled_window_set_policy(
|
||||
GTK_SCROLLED_WINDOW(scrolled_window),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC
|
||||
);
|
||||
gtk_widget_set_vexpand(scrolled_window, TRUE);
|
||||
gtk_scrolled_window_set_child(
|
||||
GTK_SCROLLED_WINDOW(scrolled_window),
|
||||
details_view
|
||||
);
|
||||
|
||||
gtk_widget_set_halign(close_button, GTK_ALIGN_END);
|
||||
g_signal_connect(
|
||||
close_button,
|
||||
"clicked",
|
||||
G_CALLBACK(application_message_dialog_on_close_clicked),
|
||||
dialog_window
|
||||
);
|
||||
|
||||
gtk_box_append(GTK_BOX(main_box), title_label);
|
||||
gtk_box_append(GTK_BOX(main_box), message_label);
|
||||
gtk_box_append(GTK_BOX(main_box), scrolled_window);
|
||||
gtk_box_append(GTK_BOX(main_box), close_button);
|
||||
gtk_window_set_child(dialog_window, main_box);
|
||||
gtk_window_present(dialog_window);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Libère le contexte associé à une fenêtre de confirmation.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -117,6 +117,12 @@ struct MainWindow
|
|||
gpointer
|
||||
add_relation_user_data;
|
||||
|
||||
MainWindowOsintActionCallback
|
||||
osint_action_callback;
|
||||
|
||||
gpointer
|
||||
osint_action_user_data;
|
||||
|
||||
MainWindowQuitCallback
|
||||
quit_callback;
|
||||
|
||||
|
|
@ -125,6 +131,29 @@ struct MainWindow
|
|||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Relaie une action OSINT vers le contrôleur applicatif.
|
||||
*/
|
||||
static void main_window_on_osint_action_requested(
|
||||
const char *action_identifier,
|
||||
const char *target_value,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
MainWindow *main_window = user_data;
|
||||
|
||||
if (main_window == NULL || main_window->osint_action_callback == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
main_window->osint_action_callback(
|
||||
action_identifier,
|
||||
target_value,
|
||||
main_window->osint_action_user_data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Ouvre dans le workspace l'entité choisie dans la sidebar.
|
||||
*/
|
||||
|
|
@ -745,6 +774,12 @@ MainWindow *main_window_new(
|
|||
main_window
|
||||
);
|
||||
|
||||
workspace_set_osint_action_callback(
|
||||
main_window->workspace,
|
||||
main_window_on_osint_action_requested,
|
||||
main_window
|
||||
);
|
||||
|
||||
workspace_widget = workspace_get_widget(
|
||||
main_window->workspace
|
||||
);
|
||||
|
|
@ -1245,6 +1280,21 @@ void main_window_set_osint_tool_state(
|
|||
);
|
||||
}
|
||||
|
||||
void main_window_set_osint_action_callback(
|
||||
MainWindow *main_window,
|
||||
MainWindowOsintActionCallback callback,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
if (main_window == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
main_window->osint_action_callback = callback;
|
||||
main_window->osint_action_user_data = user_data;
|
||||
}
|
||||
|
||||
void main_window_set_tree_selection_callback(
|
||||
MainWindow *main_window,
|
||||
InvestigationTreeViewSelectionCallback callback,
|
||||
|
|
|
|||
|
|
@ -112,6 +112,12 @@ struct Workspace
|
|||
gpointer
|
||||
add_relation_user_data;
|
||||
|
||||
WorkspaceOsintActionCallback
|
||||
osint_action_callback;
|
||||
|
||||
gpointer
|
||||
osint_action_user_data;
|
||||
|
||||
GtkWidget *node_name_label;
|
||||
GtkWidget *node_path_label;
|
||||
GtkWidget *node_type_label;
|
||||
|
|
@ -128,34 +134,62 @@ struct Workspace
|
|||
};
|
||||
|
||||
/**
|
||||
* @brief Confirme localement le déclenchement de l'action de démonstration.
|
||||
* @brief Traite localement l'aperçu ou relaie une action OSINT exécutable.
|
||||
*/
|
||||
static void workspace_on_osint_demo_action_clicked(
|
||||
static void workspace_on_osint_action_clicked(
|
||||
GtkButton *button,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
Workspace *workspace = user_data;
|
||||
const char *action_label = g_object_get_data(
|
||||
const char *action_identifier = g_object_get_data(
|
||||
G_OBJECT(button),
|
||||
"osint-action-label"
|
||||
"osint-action-identifier"
|
||||
);
|
||||
char *status_text = NULL;
|
||||
const char *target_value = NULL;
|
||||
|
||||
if (workspace == NULL || workspace->osint_tools_status_label == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
status_text = g_strdup_printf(
|
||||
"%s : démonstration locale, aucun outil exécuté.",
|
||||
action_label != NULL ? action_label : "Action OSINT"
|
||||
);
|
||||
if (g_strcmp0(action_identifier, "selection-preview") == 0)
|
||||
{
|
||||
gtk_label_set_text(
|
||||
GTK_LABEL(workspace->osint_tools_status_label),
|
||||
status_text != NULL ? status_text : "Aucun outil exécuté."
|
||||
"Aperçu local : aucun outil externe exécuté."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
target_value = osint_selection_context_get_value(
|
||||
workspace->osint_selection_context
|
||||
);
|
||||
|
||||
if (workspace->osint_action_callback == NULL ||
|
||||
action_identifier == NULL || target_value == NULL)
|
||||
{
|
||||
gtk_label_set_text(
|
||||
GTK_LABEL(workspace->osint_tools_status_label),
|
||||
"Impossible de transmettre cette action OSINT."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_label_set_text(
|
||||
GTK_LABEL(workspace->osint_tools_status_label),
|
||||
"Exécution lancée ; suivez sa progression dans les tâches."
|
||||
);
|
||||
|
||||
gtk_popover_popdown(
|
||||
GTK_POPOVER(workspace->osint_tools_popover)
|
||||
);
|
||||
|
||||
workspace->osint_action_callback(
|
||||
action_identifier,
|
||||
target_value,
|
||||
workspace->osint_action_user_data
|
||||
);
|
||||
g_free(status_text);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -282,15 +316,16 @@ static void workspace_update_osint_tools_menu(
|
|||
? osint_action_get_description(action)
|
||||
: osint_action_get_unavailable_reason(action)
|
||||
);
|
||||
g_object_set_data(
|
||||
g_object_set_data_full(
|
||||
G_OBJECT(action_button),
|
||||
"osint-action-label",
|
||||
(gpointer) osint_action_get_label(action)
|
||||
"osint-action-identifier",
|
||||
g_strdup(osint_action_get_identifier(action)),
|
||||
g_free
|
||||
);
|
||||
g_signal_connect(
|
||||
action_button,
|
||||
"clicked",
|
||||
G_CALLBACK(workspace_on_osint_demo_action_clicked),
|
||||
G_CALLBACK(workspace_on_osint_action_clicked),
|
||||
workspace
|
||||
);
|
||||
gtk_box_append(
|
||||
|
|
@ -2352,6 +2387,21 @@ void workspace_set_osint_tool_state(
|
|||
);
|
||||
}
|
||||
|
||||
void workspace_set_osint_action_callback(
|
||||
Workspace *workspace,
|
||||
WorkspaceOsintActionCallback callback,
|
||||
gpointer user_data
|
||||
)
|
||||
{
|
||||
if (workspace == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
workspace->osint_action_callback = callback;
|
||||
workspace->osint_action_user_data = user_data;
|
||||
}
|
||||
|
||||
void workspace_set_graph_loading(
|
||||
Workspace *workspace
|
||||
)
|
||||
|
|
|
|||
35
tests/test_osint_dns_query.c
Normal file
35
tests/test_osint_dns_query.c
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/******************************************************************************
|
||||
* @file test_osint_dns_query.c
|
||||
* @brief Tests de validation des cibles de résolution DNS OSINT.
|
||||
******************************************************************************/
|
||||
|
||||
#include "models/osint_dns_query.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
static void test_valid_targets(void)
|
||||
{
|
||||
g_assert_true(osint_dns_query_is_valid_target("example.org"));
|
||||
g_assert_true(osint_dns_query_is_valid_target("WWW.Example.ORG"));
|
||||
g_assert_true(osint_dns_query_is_valid_target("sub-domain.example.org"));
|
||||
g_assert_true(osint_dns_query_is_valid_target("example.org."));
|
||||
}
|
||||
|
||||
static void test_invalid_targets(void)
|
||||
{
|
||||
g_assert_false(osint_dns_query_is_valid_target(NULL));
|
||||
g_assert_false(osint_dns_query_is_valid_target(""));
|
||||
g_assert_false(osint_dns_query_is_valid_target("-x.example.org"));
|
||||
g_assert_false(osint_dns_query_is_valid_target("example..org"));
|
||||
g_assert_false(osint_dns_query_is_valid_target("example.org +trace"));
|
||||
g_assert_false(osint_dns_query_is_valid_target("example_.org"));
|
||||
g_assert_false(osint_dns_query_is_valid_target("example-.org"));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/osint-dns-query/valid", test_valid_targets);
|
||||
g_test_add_func("/osint-dns-query/invalid", test_invalid_targets);
|
||||
return g_test_run();
|
||||
}
|
||||
Loading…
Reference in a new issue