feat(osint): add DNS result review

This commit is contained in:
grayTerminal-sh 2026-07-22 12:06:10 +02:00
parent 6c8113dfea
commit 5a196f6ac6
8 changed files with 551 additions and 10 deletions

View file

@ -116,6 +116,7 @@ TEST_GRAPH_NODE_POSITION_DAO := tests/test_graph_node_position_dao
TEST_OSINT_SELECTION_CONTEXT := tests/test_osint_selection_context TEST_OSINT_SELECTION_CONTEXT := tests/test_osint_selection_context
TEST_OSINT_ACTION_CATALOG := tests/test_osint_action_catalog TEST_OSINT_ACTION_CATALOG := tests/test_osint_action_catalog
TEST_OSINT_DNS_QUERY := tests/test_osint_dns_query TEST_OSINT_DNS_QUERY := tests/test_osint_dns_query
TEST_OSINT_DNS_PROPOSAL := tests/test_osint_dns_proposal
all: $(TARGET) all: $(TARGET)
@ -551,6 +552,11 @@ $(TEST_OSINT_DNS_QUERY): \
src/models/osint_dns_query.c src/models/osint_dns_query.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS) $(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_OSINT_DNS_PROPOSAL): \
tests/test_osint_dns_proposal.c \
src/models/osint_dns_proposal.c
$(CC) $(TEST_CFLAGS) $^ -o $@ $(TEST_LDFLAGS)
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK): \ $(TEST_INVESTIGATION_GRAPH_LOAD_TASK): \
tests/test_investigation_graph_load_task.c \ tests/test_investigation_graph_load_task.c \
src/core/investigation_graph_load_task.c \ src/core/investigation_graph_load_task.c \
@ -621,7 +627,8 @@ test: \
$(TEST_GRAPH_NODE_POSITION_DAO) \ $(TEST_GRAPH_NODE_POSITION_DAO) \
$(TEST_OSINT_SELECTION_CONTEXT) \ $(TEST_OSINT_SELECTION_CONTEXT) \
$(TEST_OSINT_ACTION_CATALOG) \ $(TEST_OSINT_ACTION_CATALOG) \
$(TEST_OSINT_DNS_QUERY) $(TEST_OSINT_DNS_QUERY) \
$(TEST_OSINT_DNS_PROPOSAL)
@echo "Exécution des tests..." @echo "Exécution des tests..."
@./$(TEST_NODE) @./$(TEST_NODE)
@./$(TEST_TREE_MODEL) @./$(TEST_TREE_MODEL)
@ -672,6 +679,7 @@ test: \
@$(TEST_OSINT_SELECTION_CONTEXT) @$(TEST_OSINT_SELECTION_CONTEXT)
@$(TEST_OSINT_ACTION_CATALOG) @$(TEST_OSINT_ACTION_CATALOG)
@$(TEST_OSINT_DNS_QUERY) @$(TEST_OSINT_DNS_QUERY)
@$(TEST_OSINT_DNS_PROPOSAL)
@echo "Tous les tests sont valides." @echo "Tous les tests sont valides."
%.o: %.c %.o: %.c
@ -728,7 +736,8 @@ clean:
$(TEST_GRAPH_NODE_POSITION_DAO) \ $(TEST_GRAPH_NODE_POSITION_DAO) \
$(TEST_OSINT_SELECTION_CONTEXT) \ $(TEST_OSINT_SELECTION_CONTEXT) \
$(TEST_OSINT_ACTION_CATALOG) \ $(TEST_OSINT_ACTION_CATALOG) \
$(TEST_OSINT_DNS_QUERY) $(TEST_OSINT_DNS_QUERY) \
$(TEST_OSINT_DNS_PROPOSAL)
-include $(DEP) -include $(DEP)

View file

@ -146,7 +146,8 @@ Le socle actuel comprend notamment :
- catalogue déterministe des actions compatibles avec la sélection OSINT ; - catalogue déterministe des actions compatibles avec la sélection OSINT ;
- disponibilité des actions OSINT synchronisée avec le registre doutils ; - disponibilité des actions OSINT synchronisée avec le registre doutils ;
- résolution DNS asynchrone avec `dig` depuis une entité domaine ; - résolution DNS asynchrone avec `dig` depuis une entité domaine ;
- affichage sélectionnable des sorties standard et derreur, sans persistance. - affichage sélectionnable des sorties standard et derreur, sans persistance ;
- révision des réponses DNS sous forme de propositions avant intégration.
Les outils actuellement présents dans le catalogue initial sont : Les outils actuellement présents dans le catalogue initial sont :

View file

@ -0,0 +1,80 @@
/******************************************************************************
* @file osint_dns_proposal.h
* @brief Propositions structurées extraites d'une sortie DNS brute.
******************************************************************************/
#ifndef LABFY_INVESTIGATION_OSINT_DNS_PROPOSAL_H
#define LABFY_INVESTIGATION_OSINT_DNS_PROPOSAL_H
#include <glib.h>
G_BEGIN_DECLS
/** @brief Proposition DNS opaque produite avant toute intégration. */
typedef struct OsintDnsProposal OsintDnsProposal;
/**
* @brief Extrait les enregistrements reconnus d'une sortie `dig +answer`.
*
* Le tableau retourné possède ses propositions et doit être libéré avec
* g_ptr_array_unref(). Les lignes vides, les commentaires et les lignes
* invalides sont ignorés sans modifier la sortie brute d'origine.
*
* @param target_value Cible DNS ayant produit la sortie.
* @param raw_output Sortie standard UTF-8 de dig.
*
* @return Nouveau tableau, éventuellement vide, ou NULL si les arguments
* sont invalides.
*/
GPtrArray *osint_dns_proposal_parse(
const char *target_value,
const char *raw_output
);
/**
* @brief Retourne le propriétaire de l'enregistrement.
*
* @param proposal Proposition consultée.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *osint_dns_proposal_get_owner(
const OsintDnsProposal *proposal
);
/**
* @brief Retourne le type d'enregistrement DNS.
*
* @param proposal Proposition consultée.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *osint_dns_proposal_get_record_type(
const OsintDnsProposal *proposal
);
/**
* @brief Retourne la valeur brute de l'enregistrement.
*
* @param proposal Proposition consultée.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *osint_dns_proposal_get_value(
const OsintDnsProposal *proposal
);
/**
* @brief Retourne la cible initialement interrogée.
*
* @param proposal Proposition consultée.
*
* @return Chaîne empruntée, ou NULL.
*/
const char *osint_dns_proposal_get_target(
const OsintDnsProposal *proposal
);
G_END_DECLS
#endif

View file

@ -32,6 +32,15 @@ typedef void (*ApplicationMessageDialogConfirmationCallback)(
gpointer user_data gpointer user_data
); );
/**
* @brief Callback appelé par l'action facultative d'un dialogue détaillé.
*
* @param user_data Données associées à l'action.
*/
typedef void (*ApplicationMessageDialogActionCallback)(
gpointer user_data
);
/** /**
* @brief Affiche une fenêtre modale contenant un message. * @brief Affiche une fenêtre modale contenant un message.
* *
@ -69,6 +78,34 @@ void application_message_dialog_present_details(
const char *details const char *details
); );
/**
* @brief Affiche un message détaillé avec une action facultative.
*
* Le dialogue devient propriétaire de action_data lorsque l'affichage est
* créé. Les données sont libérées avec action_data_destroy à sa fermeture.
*
* @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.
* @param action_label Libellé de l'action, ou NULL.
* @param action_callback Callback de l'action, ou NULL.
* @param action_data Données transmises au callback.
* @param action_data_destroy Destructeur des données, ou NULL.
*/
void application_message_dialog_present_details_action(
GtkWindow *parent_window,
ApplicationMessageDialogType message_type,
const char *title,
const char *message,
const char *details,
const char *action_label,
ApplicationMessageDialogActionCallback action_callback,
gpointer action_data,
GDestroyNotify action_data_destroy
);
/** /**
* @brief Affiche un dialogue modal demandant une confirmation. * @brief Affiche un dialogue modal demandant une confirmation.
* *

View file

@ -14,6 +14,7 @@
#include "models/investigation_graph_model.h" #include "models/investigation_graph_model.h"
#include "models/investigation_record.h" #include "models/investigation_record.h"
#include "models/osint_dns_query.h" #include "models/osint_dns_query.h"
#include "models/osint_dns_proposal.h"
#include "core/investigation_tree_builder.h" #include "core/investigation_tree_builder.h"
#include "views/create_investigation_dialog.h" #include "views/create_investigation_dialog.h"
#include "views/folder_dialog.h" #include "views/folder_dialog.h"
@ -162,6 +163,76 @@ typedef struct
char *target_value; char *target_value;
} ApplicationOsintActionContext; } ApplicationOsintActionContext;
/** @brief Données possédées par l'étape de révision OSINT. */
typedef struct
{
Application *application;
char *proposal_summary;
} ApplicationOsintReviewContext;
/** @brief Libère les données de révision possédées par le dialogue. */
static void application_osint_review_context_free(gpointer user_data)
{
ApplicationOsintReviewContext *context = user_data;
if (context == NULL) return;
g_free(context->proposal_summary);
g_free(context);
}
/** @brief Affiche les propositions sans les intégrer à l'enquête. */
static void application_on_osint_prepare_integration(gpointer user_data)
{
ApplicationOsintReviewContext *context = user_data;
if (context == NULL || context->application == NULL ||
context->application->main_window == NULL)
{
return;
}
application_message_dialog_present_details(
main_window_get_window(context->application->main_window),
APPLICATION_MESSAGE_DIALOG_INFORMATION,
"Propositions DNS à réviser",
"Aucune donnée n'a été enregistrée dans l'enquête.",
context->proposal_summary
);
}
/** @brief Construit le résumé lisible des propositions DNS. */
static char *application_build_dns_proposal_summary(GPtrArray *proposals)
{
GString *summary = g_string_new(NULL);
if (summary == NULL) return NULL;
g_string_append_printf(
summary,
"Propositions détectées : %u\n\n",
proposals != NULL ? proposals->len : 0U
);
for (guint index = 0; proposals != NULL && index < proposals->len; index++)
{
const OsintDnsProposal *proposal = g_ptr_array_index(proposals, index);
g_string_append_printf(
summary,
"%u. Cible : %s\n Propriétaire : %s\n Type : %s\n"
" Valeur : %s\n\n",
index + 1U,
osint_dns_proposal_get_target(proposal),
osint_dns_proposal_get_owner(proposal),
osint_dns_proposal_get_record_type(proposal),
osint_dns_proposal_get_value(proposal)
);
}
g_string_append(
summary,
"Ces propositions sont uniquement préparées pour révision. "
"Elles ne sont pas enregistrées dans SQLite."
);
return g_string_free(summary, FALSE);
}
/** /**
* @brief Libère le contexte d'une action OSINT. * @brief Libère le contexte d'une action OSINT.
*/ */
@ -989,6 +1060,8 @@ static void application_on_dns_lookup_completed(
char *stderr_text = NULL; char *stderr_text = NULL;
char *details = NULL; char *details = NULL;
char *message = NULL; char *message = NULL;
GPtrArray *proposals = NULL;
ApplicationOsintReviewContext *review_context = NULL;
GError *error = NULL; GError *error = NULL;
if (task == NULL || context == NULL || context->application == NULL) if (task == NULL || context == NULL || context->application == NULL)
@ -1002,6 +1075,17 @@ static void application_on_dns_lookup_completed(
return; return;
} }
if (background_task_get_state(task) == BACKGROUND_TASK_STATE_CANCELLED)
{
application_message_dialog_present(
main_window_get_window(application->main_window),
APPLICATION_MESSAGE_DIALOG_INFORMATION,
"Résolution DNS annulée",
"La tâche a été annulée. Aucun résultat n'a été enregistré."
);
return;
}
if (background_task_get_state(task) != BACKGROUND_TASK_STATE_COMPLETED) if (background_task_get_state(task) != BACKGROUND_TASK_STATE_COMPLETED)
{ {
error = background_task_dup_error(task); error = background_task_dup_error(task);
@ -1055,20 +1139,45 @@ static void application_on_dns_lookup_completed(
details = g_strdup("Aucun enregistrement DNS retourné."); details = g_strdup("Aucun enregistrement DNS retourné.");
} }
proposals = osint_dns_proposal_parse(context->target_value, stdout_text);
if (tool_process_result_is_success(process_result) &&
proposals != NULL && proposals->len > 0U)
{
review_context = g_try_new0(ApplicationOsintReviewContext, 1);
if (review_context != NULL)
{
review_context->application = application;
review_context->proposal_summary =
application_build_dns_proposal_summary(proposals);
}
if (review_context == NULL || review_context->proposal_summary == NULL)
{
application_osint_review_context_free(review_context);
review_context = NULL;
}
}
message = g_strdup_printf( message = g_strdup_printf(
"Cible : %s — code de sortie : %d", "Cible : %s\nOutil : dns.dig\nCode de sortie : %d\n"
"Résultat brut non enregistré dans l'enquête.",
context->target_value, context->target_value,
tool_process_result_get_exit_status(process_result) tool_process_result_get_exit_status(process_result)
); );
application_message_dialog_present_details( application_message_dialog_present_details_action(
main_window_get_window(application->main_window), main_window_get_window(application->main_window),
tool_process_result_is_success(process_result) tool_process_result_is_success(process_result)
? APPLICATION_MESSAGE_DIALOG_INFORMATION ? APPLICATION_MESSAGE_DIALOG_INFORMATION
: APPLICATION_MESSAGE_DIALOG_WARNING, : APPLICATION_MESSAGE_DIALOG_WARNING,
"Résultat de la résolution DNS", "Résultat de la résolution DNS",
message, message,
details details,
review_context != NULL ? "Préparer l'intégration" : NULL,
review_context != NULL
? application_on_osint_prepare_integration
: NULL,
review_context,
application_osint_review_context_free
); );
g_clear_pointer(&stdout_bytes, g_bytes_unref); g_clear_pointer(&stdout_bytes, g_bytes_unref);
@ -1077,6 +1186,7 @@ static void application_on_dns_lookup_completed(
g_free(stderr_text); g_free(stderr_text);
g_free(details); g_free(details);
g_free(message); g_free(message);
g_clear_pointer(&proposals, g_ptr_array_unref);
} }
/** /**

View file

@ -0,0 +1,157 @@
/******************************************************************************
* @file osint_dns_proposal.c
* @brief Propositions structurées extraites d'une sortie DNS brute.
******************************************************************************/
#include "models/osint_dns_proposal.h"
#include <string.h>
struct OsintDnsProposal
{
char *owner;
char *record_type;
char *value;
char *target;
};
/** @brief Libère une proposition possédée par un tableau. */
static void osint_dns_proposal_free(gpointer data)
{
OsintDnsProposal *proposal = data;
if (proposal == NULL) return;
g_free(proposal->target);
g_free(proposal->value);
g_free(proposal->record_type);
g_free(proposal->owner);
g_free(proposal);
}
/** @brief Indique si un type DNS produit une proposition structurée. */
static gboolean osint_dns_proposal_type_is_recognized(const char *record_type)
{
static const char *recognized_types[] = {
"A", "AAAA", "CAA", "CNAME", "MX", "NS", "PTR", "SOA", "SRV",
"TXT", NULL
};
for (gsize index = 0; recognized_types[index] != NULL; index++)
{
if (g_strcmp0(record_type, recognized_types[index]) == 0) return TRUE;
}
return FALSE;
}
/** @brief Extrait le prochain champ séparé par des espaces ASCII. */
static char *osint_dns_proposal_take_field(char **cursor)
{
char *field_start = NULL;
char *field_end = NULL;
if (cursor == NULL || *cursor == NULL) return NULL;
field_start = *cursor;
while (*field_start != '\0' && g_ascii_isspace(*field_start)) field_start++;
if (*field_start == '\0')
{
*cursor = field_start;
return NULL;
}
field_end = field_start;
while (*field_end != '\0' && !g_ascii_isspace(*field_end)) field_end++;
*cursor = field_end;
return g_strndup(field_start, (gsize) (field_end - field_start));
}
/** @brief Parse une ligne de réponse DNS, ou retourne NULL. */
static OsintDnsProposal *osint_dns_proposal_parse_line(
const char *target_value,
const char *line
)
{
char *line_copy = g_strdup(line);
char *cursor = line_copy;
char *owner = NULL;
char *time_to_live = NULL;
char *record_class = NULL;
char *record_type = NULL;
char *value = NULL;
OsintDnsProposal *proposal = NULL;
if (line_copy == NULL) return NULL;
g_strstrip(line_copy);
if (line_copy[0] == '\0' || line_copy[0] == ';') goto cleanup;
owner = osint_dns_proposal_take_field(&cursor);
time_to_live = osint_dns_proposal_take_field(&cursor);
record_class = osint_dns_proposal_take_field(&cursor);
record_type = osint_dns_proposal_take_field(&cursor);
while (cursor != NULL && *cursor != '\0' && g_ascii_isspace(*cursor)) cursor++;
value = cursor != NULL ? g_strdup(cursor) : NULL;
if (value != NULL) g_strstrip(value);
if (owner == NULL || time_to_live == NULL || record_class == NULL ||
record_type == NULL || value == NULL || value[0] == '\0' ||
!g_ascii_string_to_unsigned(time_to_live, 10, 0, G_MAXUINT32, NULL, NULL) ||
g_strcmp0(record_class, "IN") != 0 ||
!osint_dns_proposal_type_is_recognized(record_type))
{
goto cleanup;
}
proposal = g_try_new0(OsintDnsProposal, 1);
if (proposal != NULL)
{
proposal->owner = g_strdup(owner);
proposal->record_type = g_strdup(record_type);
proposal->value = g_strdup(value);
proposal->target = g_strdup(target_value);
}
if (proposal == NULL || proposal->owner == NULL ||
proposal->record_type == NULL || proposal->value == NULL ||
proposal->target == NULL)
{
osint_dns_proposal_free(proposal);
proposal = NULL;
}
cleanup:
g_free(value);
g_free(record_type);
g_free(record_class);
g_free(time_to_live);
g_free(owner);
g_free(line_copy);
return proposal;
}
GPtrArray *osint_dns_proposal_parse(
const char *target_value,
const char *raw_output
)
{
GPtrArray *proposals = NULL;
char **lines = NULL;
if (target_value == NULL || target_value[0] == '\0' || raw_output == NULL)
return NULL;
proposals = g_ptr_array_new_with_free_func(osint_dns_proposal_free);
lines = g_strsplit(raw_output, "\n", -1);
for (gsize index = 0; lines != NULL && lines[index] != NULL; index++)
{
OsintDnsProposal *proposal = osint_dns_proposal_parse_line(
target_value, lines[index]
);
if (proposal != NULL) g_ptr_array_add(proposals, proposal);
}
g_strfreev(lines);
return proposals;
}
const char *osint_dns_proposal_get_owner(const OsintDnsProposal *proposal)
{ return proposal != NULL ? proposal->owner : NULL; }
const char *osint_dns_proposal_get_record_type(const OsintDnsProposal *proposal)
{ return proposal != NULL ? proposal->record_type : NULL; }
const char *osint_dns_proposal_get_value(const OsintDnsProposal *proposal)
{ return proposal != NULL ? proposal->value : NULL; }
const char *osint_dns_proposal_get_target(const OsintDnsProposal *proposal)
{ return proposal != NULL ? proposal->target : NULL; }

View file

@ -35,6 +35,36 @@ typedef struct
gboolean completed; gboolean completed;
} ApplicationMessageDialogConfirmationContext; } ApplicationMessageDialogConfirmationContext;
/** @brief Données possédées par l'action d'un dialogue détaillé. */
typedef struct
{
ApplicationMessageDialogActionCallback callback;
gpointer user_data;
GDestroyNotify user_data_destroy;
} ApplicationMessageDialogActionContext;
/** @brief Libère les données possédées par une action détaillée. */
static void application_message_dialog_action_context_free(gpointer data)
{
ApplicationMessageDialogActionContext *context = data;
if (context == NULL) return;
if (context->user_data_destroy != NULL)
context->user_data_destroy(context->user_data);
g_free(context);
}
/** @brief Déclenche l'action facultative du dialogue détaillé. */
static void application_message_dialog_on_action_clicked(
GtkButton *button,
gpointer user_data
)
{
ApplicationMessageDialogActionContext *context = user_data;
(void) button;
if (context != NULL && context->callback != NULL)
context->callback(context->user_data);
}
/** /**
* @brief Retourne le libellé correspondant au type de message. * @brief Retourne le libellé correspondant au type de message.
* *
@ -355,12 +385,16 @@ void application_message_dialog_present(
); );
} }
void application_message_dialog_present_details( void application_message_dialog_present_details_action(
GtkWindow *parent_window, GtkWindow *parent_window,
ApplicationMessageDialogType message_type, ApplicationMessageDialogType message_type,
const char *title, const char *title,
const char *message, const char *message,
const char *details const char *details,
const char *action_label,
ApplicationMessageDialogActionCallback action_callback,
gpointer action_data,
GDestroyNotify action_data_destroy
) )
{ {
GtkWindow *dialog_window = GTK_WINDOW(gtk_window_new()); GtkWindow *dialog_window = GTK_WINDOW(gtk_window_new());
@ -374,6 +408,9 @@ void application_message_dialog_present_details(
GtkWidget *scrolled_window = gtk_scrolled_window_new(); GtkWidget *scrolled_window = gtk_scrolled_window_new();
GtkWidget *details_view = gtk_text_view_new(); GtkWidget *details_view = gtk_text_view_new();
GtkWidget *close_button = gtk_button_new_with_label("Fermer"); GtkWidget *close_button = gtk_button_new_with_label("Fermer");
GtkWidget *button_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
GtkWidget *action_button = NULL;
ApplicationMessageDialogActionContext *action_context = NULL;
GtkTextBuffer *details_buffer = gtk_text_view_get_buffer( GtkTextBuffer *details_buffer = gtk_text_view_get_buffer(
GTK_TEXT_VIEW(details_view) GTK_TEXT_VIEW(details_view)
); );
@ -426,7 +463,7 @@ void application_message_dialog_present_details(
details_view details_view
); );
gtk_widget_set_halign(close_button, GTK_ALIGN_END); gtk_widget_set_halign(button_box, GTK_ALIGN_END);
g_signal_connect( g_signal_connect(
close_button, close_button,
"clicked", "clicked",
@ -434,14 +471,65 @@ void application_message_dialog_present_details(
dialog_window dialog_window
); );
if (action_label != NULL && action_label[0] != '\0' &&
action_callback != NULL)
{
action_context = g_new0(ApplicationMessageDialogActionContext, 1);
action_context->callback = action_callback;
action_context->user_data = action_data;
action_context->user_data_destroy = action_data_destroy;
g_object_set_data_full(
G_OBJECT(dialog_window),
"application-message-dialog-action-context",
action_context,
application_message_dialog_action_context_free
);
action_button = gtk_button_new_with_label(action_label);
gtk_widget_add_css_class(action_button, "suggested-action");
g_signal_connect(
action_button,
"clicked",
G_CALLBACK(application_message_dialog_on_action_clicked),
action_context
);
gtk_box_append(GTK_BOX(button_box), action_button);
}
else if (action_data_destroy != NULL)
{
action_data_destroy(action_data);
}
gtk_box_append(GTK_BOX(button_box), close_button);
gtk_box_append(GTK_BOX(main_box), title_label); 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), message_label);
gtk_box_append(GTK_BOX(main_box), scrolled_window); gtk_box_append(GTK_BOX(main_box), scrolled_window);
gtk_box_append(GTK_BOX(main_box), close_button); gtk_box_append(GTK_BOX(main_box), button_box);
gtk_window_set_child(dialog_window, main_box); gtk_window_set_child(dialog_window, main_box);
gtk_window_present(dialog_window); gtk_window_present(dialog_window);
} }
void application_message_dialog_present_details(
GtkWindow *parent_window,
ApplicationMessageDialogType message_type,
const char *title,
const char *message,
const char *details
)
{
application_message_dialog_present_details_action(
parent_window,
message_type,
title,
message,
details,
NULL,
NULL,
NULL,
NULL
);
}
/** /**
* @brief Libère le contexte associé à une fenêtre de confirmation. * @brief Libère le contexte associé à une fenêtre de confirmation.
*/ */

View file

@ -0,0 +1,59 @@
/******************************************************************************
* @file test_osint_dns_proposal.c
* @brief Tests des propositions structurées extraites de dig.
******************************************************************************/
#include "models/osint_dns_proposal.h"
#include <glib.h>
static void test_parse_recognized_records(void)
{
const char *output =
"example.org. 300 IN A 93.184.216.34\n"
"example.org. 300 IN MX 10 mail.example.org.\n"
"example.org. 300 IN TXT \"texte avec espaces\"\n";
GPtrArray *proposals = osint_dns_proposal_parse("example.org", output);
const OsintDnsProposal *first = g_ptr_array_index(proposals, 0);
const OsintDnsProposal *third = g_ptr_array_index(proposals, 2);
g_assert_cmpuint(proposals->len, ==, 3);
g_assert_cmpstr(osint_dns_proposal_get_owner(first), ==, "example.org.");
g_assert_cmpstr(osint_dns_proposal_get_record_type(first), ==, "A");
g_assert_cmpstr(osint_dns_proposal_get_value(first), ==, "93.184.216.34");
g_assert_cmpstr(osint_dns_proposal_get_target(first), ==, "example.org");
g_assert_cmpstr(
osint_dns_proposal_get_value(third), ==, "\"texte avec espaces\""
);
g_ptr_array_unref(proposals);
}
static void test_ignore_unrecognized_lines(void)
{
const char *output =
";; comment\n"
"invalid line\n"
"example.org. nope IN A 93.184.216.34\n"
"example.org. 60 CH A 93.184.216.34\n"
"example.org. 60 IN TYPE999 opaque\n";
GPtrArray *proposals = osint_dns_proposal_parse("example.org", output);
g_assert_nonnull(proposals);
g_assert_cmpuint(proposals->len, ==, 0);
g_ptr_array_unref(proposals);
}
static void test_invalid_arguments(void)
{
g_assert_null(osint_dns_proposal_parse(NULL, ""));
g_assert_null(osint_dns_proposal_parse("", ""));
g_assert_null(osint_dns_proposal_parse("example.org", NULL));
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/osint-dns-proposal/recognized", test_parse_recognized_records);
g_test_add_func("/osint-dns-proposal/ignored", test_ignore_unrecognized_lines);
g_test_add_func("/osint-dns-proposal/invalid", test_invalid_arguments);
return g_test_run();
}