feat(osint): add selection context
This commit is contained in:
parent
13315846e3
commit
bc70257e0a
6 changed files with 390 additions and 15 deletions
15
Makefile
15
Makefile
|
|
@ -113,6 +113,7 @@ TEST_INVESTIGATION_GRAPH_MODEL := tests/test_investigation_graph_model
|
|||
TEST_INVESTIGATION_GRAPH_LOADER := tests/test_investigation_graph_loader
|
||||
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
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
|
|
@ -528,6 +529,13 @@ $(TEST_GRAPH_NODE_POSITION_DAO): \
|
|||
$(CC) $(GRAPH_NODE_POSITION_DAO_TEST_CFLAGS) $^ -o $@ \
|
||||
$(TEST_LDFLAGS) -lsqlite3
|
||||
|
||||
$(TEST_OSINT_SELECTION_CONTEXT): \
|
||||
tests/test_osint_selection_context.c \
|
||||
src/models/osint_selection_context.c \
|
||||
src/models/entity_record.c \
|
||||
src/models/relation_record.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 \
|
||||
|
|
@ -595,7 +603,8 @@ test: \
|
|||
$(TEST_INVESTIGATION_GRAPH_MODEL) \
|
||||
$(TEST_INVESTIGATION_GRAPH_LOADER) \
|
||||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
||||
$(TEST_GRAPH_NODE_POSITION_DAO)
|
||||
$(TEST_GRAPH_NODE_POSITION_DAO) \
|
||||
$(TEST_OSINT_SELECTION_CONTEXT)
|
||||
@echo "Exécution des tests..."
|
||||
@./$(TEST_NODE)
|
||||
@./$(TEST_TREE_MODEL)
|
||||
|
|
@ -643,6 +652,7 @@ test: \
|
|||
@$(TEST_INVESTIGATION_GRAPH_LOADER)
|
||||
@$(TEST_INVESTIGATION_GRAPH_LOAD_TASK)
|
||||
@$(TEST_GRAPH_NODE_POSITION_DAO)
|
||||
@$(TEST_OSINT_SELECTION_CONTEXT)
|
||||
@echo "Tous les tests sont valides."
|
||||
|
||||
%.o: %.c
|
||||
|
|
@ -696,7 +706,8 @@ clean:
|
|||
$(TEST_INVESTIGATION_GRAPH_MODEL) \
|
||||
$(TEST_INVESTIGATION_GRAPH_LOADER) \
|
||||
$(TEST_INVESTIGATION_GRAPH_LOAD_TASK) \
|
||||
$(TEST_GRAPH_NODE_POSITION_DAO)
|
||||
$(TEST_GRAPH_NODE_POSITION_DAO) \
|
||||
$(TEST_OSINT_SELECTION_CONTEXT)
|
||||
|
||||
-include $(DEP)
|
||||
|
||||
|
|
|
|||
|
|
@ -141,7 +141,8 @@ Le socle actuel comprend notamment :
|
|||
- exécution d’outils en tâche de fond ;
|
||||
- catalogue initial d’outils ;
|
||||
- détection de présence et de version ;
|
||||
- menu OSINT contextuel préparé pour les entités et relations du graphe.
|
||||
- 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.
|
||||
|
||||
Les outils actuellement présents dans le catalogue initial sont :
|
||||
|
||||
|
|
|
|||
104
include/models/osint_selection_context.h
Normal file
104
include/models/osint_selection_context.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/******************************************************************************
|
||||
* @file osint_selection_context.h
|
||||
* @brief Contexte métier transmis aux futurs adaptateurs OSINT.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef LABFY_INVESTIGATION_OSINT_SELECTION_CONTEXT_H
|
||||
#define LABFY_INVESTIGATION_OSINT_SELECTION_CONTEXT_H
|
||||
|
||||
#include "models/entity_record.h"
|
||||
#include "models/relation_record.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* @brief Nature de la cible sélectionnée pour un outil OSINT.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
OSINT_SELECTION_CONTEXT_KIND_UNKNOWN,
|
||||
OSINT_SELECTION_CONTEXT_KIND_ENTITY,
|
||||
OSINT_SELECTION_CONTEXT_KIND_RELATION
|
||||
} OsintSelectionContextKind;
|
||||
|
||||
/**
|
||||
* @brief Contexte OSINT opaque et indépendant de GTK.
|
||||
*/
|
||||
typedef struct OsintSelectionContext OsintSelectionContext;
|
||||
|
||||
/**
|
||||
* @brief Crée un contexte depuis une entité.
|
||||
*
|
||||
* Toutes les données utiles sont copiées.
|
||||
*
|
||||
* @param entity_record Entité empruntée.
|
||||
* @param error Emplacement facultatif recevant une erreur.
|
||||
*
|
||||
* @return Nouveau contexte, ou NULL en cas d'échec.
|
||||
*/
|
||||
OsintSelectionContext *osint_selection_context_new_entity(
|
||||
const EntityRecord *entity_record,
|
||||
GError **error
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Crée un contexte depuis une relation et ses extrémités.
|
||||
*
|
||||
* @param relation_record Relation empruntée.
|
||||
* @param source_entity Entité source empruntée.
|
||||
* @param target_entity Entité cible empruntée.
|
||||
* @param error Emplacement facultatif recevant une erreur.
|
||||
*
|
||||
* @return Nouveau contexte, ou NULL en cas d'échec.
|
||||
*/
|
||||
OsintSelectionContext *osint_selection_context_new_relation(
|
||||
const RelationRecord *relation_record,
|
||||
const EntityRecord *source_entity,
|
||||
const EntityRecord *target_entity,
|
||||
GError **error
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Libère un contexte OSINT.
|
||||
*
|
||||
* Cette fonction accepte NULL.
|
||||
*
|
||||
* @param context Contexte à libérer.
|
||||
*/
|
||||
void osint_selection_context_free(OsintSelectionContext *context);
|
||||
|
||||
/** @brief Retourne la nature du contexte. */
|
||||
OsintSelectionContextKind osint_selection_context_get_kind(
|
||||
const OsintSelectionContext *context
|
||||
);
|
||||
|
||||
/** @brief Retourne l'UUID copié de la cible. */
|
||||
const char *osint_selection_context_get_identifier(
|
||||
const OsintSelectionContext *context
|
||||
);
|
||||
|
||||
/** @brief Retourne le type métier copié de la cible. */
|
||||
const char *osint_selection_context_get_type(
|
||||
const OsintSelectionContext *context
|
||||
);
|
||||
|
||||
/** @brief Retourne la valeur principale copiée de la cible. */
|
||||
const char *osint_selection_context_get_value(
|
||||
const OsintSelectionContext *context
|
||||
);
|
||||
|
||||
/** @brief Retourne la valeur source copiée d'une relation, ou NULL. */
|
||||
const char *osint_selection_context_get_source_value(
|
||||
const OsintSelectionContext *context
|
||||
);
|
||||
|
||||
/** @brief Retourne la valeur cible copiée d'une relation, ou NULL. */
|
||||
const char *osint_selection_context_get_target_value(
|
||||
const OsintSelectionContext *context
|
||||
);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
||||
152
src/models/osint_selection_context.c
Normal file
152
src/models/osint_selection_context.c
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/******************************************************************************
|
||||
* @file osint_selection_context.c
|
||||
* @brief Implémentation du contexte métier OSINT.
|
||||
******************************************************************************/
|
||||
|
||||
#include "models/osint_selection_context.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
struct OsintSelectionContext
|
||||
{
|
||||
OsintSelectionContextKind kind;
|
||||
char *identifier;
|
||||
char *type;
|
||||
char *value;
|
||||
char *source_value;
|
||||
char *target_value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Alloue et copie les champs communs d'un contexte.
|
||||
*/
|
||||
static OsintSelectionContext *osint_selection_context_new(
|
||||
OsintSelectionContextKind kind,
|
||||
const char *identifier,
|
||||
const char *type,
|
||||
const char *value,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
OsintSelectionContext *context = NULL;
|
||||
|
||||
if ((error != NULL && *error != NULL) ||
|
||||
(kind != OSINT_SELECTION_CONTEXT_KIND_ENTITY &&
|
||||
kind != OSINT_SELECTION_CONTEXT_KIND_RELATION) ||
|
||||
identifier == NULL || !g_uuid_string_is_valid(identifier) ||
|
||||
type == NULL || type[0] == '\0' ||
|
||||
value == NULL || value[0] == '\0')
|
||||
{
|
||||
g_set_error_literal(
|
||||
error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_INVALID_ARGUMENT,
|
||||
"Le contexte de sélection OSINT est invalide."
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context = g_try_new0(OsintSelectionContext, 1);
|
||||
if (context != NULL)
|
||||
{
|
||||
context->kind = kind;
|
||||
context->identifier = g_strdup(identifier);
|
||||
context->type = g_strdup(type);
|
||||
context->value = g_strdup(value);
|
||||
}
|
||||
|
||||
if (context == NULL || context->identifier == NULL ||
|
||||
context->type == NULL || context->value == NULL)
|
||||
{
|
||||
osint_selection_context_free(context);
|
||||
g_set_error_literal(
|
||||
error,
|
||||
G_IO_ERROR,
|
||||
G_IO_ERROR_NO_SPACE,
|
||||
"Impossible d'allouer le contexte de sélection OSINT."
|
||||
);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
OsintSelectionContext *osint_selection_context_new_entity(
|
||||
const EntityRecord *entity_record,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
return osint_selection_context_new(
|
||||
OSINT_SELECTION_CONTEXT_KIND_ENTITY,
|
||||
entity_record_get_identifier(entity_record),
|
||||
entity_record_get_type_identifier(entity_record),
|
||||
entity_record_get_value(entity_record),
|
||||
error
|
||||
);
|
||||
}
|
||||
|
||||
OsintSelectionContext *osint_selection_context_new_relation(
|
||||
const RelationRecord *relation_record,
|
||||
const EntityRecord *source_entity,
|
||||
const EntityRecord *target_entity,
|
||||
GError **error
|
||||
)
|
||||
{
|
||||
const char *label = relation_record_get_label(relation_record);
|
||||
OsintSelectionContext *context = osint_selection_context_new(
|
||||
OSINT_SELECTION_CONTEXT_KIND_RELATION,
|
||||
relation_record_get_identifier(relation_record),
|
||||
relation_record_get_relation_type(relation_record),
|
||||
label != NULL && label[0] != '\0'
|
||||
? label
|
||||
: relation_record_get_relation_type(relation_record),
|
||||
error
|
||||
);
|
||||
|
||||
if (context == NULL || source_entity == NULL || target_entity == NULL)
|
||||
{
|
||||
osint_selection_context_free(context);
|
||||
if (context != NULL)
|
||||
{
|
||||
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
|
||||
"Les extrémités de la relation OSINT sont invalides.");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context->source_value = g_strdup(entity_record_get_value(source_entity));
|
||||
context->target_value = g_strdup(entity_record_get_value(target_entity));
|
||||
if (context->source_value == NULL || context->target_value == NULL)
|
||||
{
|
||||
osint_selection_context_free(context);
|
||||
g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
|
||||
"Impossible de copier les extrémités de la relation OSINT.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
void osint_selection_context_free(OsintSelectionContext *context)
|
||||
{
|
||||
if (context == NULL) return;
|
||||
g_free(context->target_value);
|
||||
g_free(context->source_value);
|
||||
g_free(context->value);
|
||||
g_free(context->type);
|
||||
g_free(context->identifier);
|
||||
g_free(context);
|
||||
}
|
||||
|
||||
OsintSelectionContextKind osint_selection_context_get_kind(const OsintSelectionContext *context)
|
||||
{ return context != NULL ? context->kind : OSINT_SELECTION_CONTEXT_KIND_UNKNOWN; }
|
||||
const char *osint_selection_context_get_identifier(const OsintSelectionContext *context)
|
||||
{ return context != NULL ? context->identifier : NULL; }
|
||||
const char *osint_selection_context_get_type(const OsintSelectionContext *context)
|
||||
{ return context != NULL ? context->type : NULL; }
|
||||
const char *osint_selection_context_get_value(const OsintSelectionContext *context)
|
||||
{ return context != NULL ? context->value : NULL; }
|
||||
const char *osint_selection_context_get_source_value(const OsintSelectionContext *context)
|
||||
{ return context != NULL ? context->source_value : NULL; }
|
||||
const char *osint_selection_context_get_target_value(const OsintSelectionContext *context)
|
||||
{ return context != NULL ? context->target_value : NULL; }
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include "models/entity_record.h"
|
||||
#include "models/investigation_graph_model.h"
|
||||
#include "models/osint_selection_context.h"
|
||||
#include "models/relation_record.h"
|
||||
#include "widgets/entity_details_panel.h"
|
||||
#include "widgets/investigation_graph_view.h"
|
||||
|
|
@ -118,6 +119,8 @@ struct Workspace
|
|||
const InvestigationGraphModel *graph_model;
|
||||
const InvestigationGraphLayout *graph_layout;
|
||||
|
||||
OsintSelectionContext *osint_selection_context;
|
||||
|
||||
WorkspaceGraphState graph_state;
|
||||
};
|
||||
|
||||
|
|
@ -129,8 +132,7 @@ struct Workspace
|
|||
*/
|
||||
static void workspace_update_osint_tools_menu(
|
||||
Workspace *workspace,
|
||||
const EntityRecord *entity_record,
|
||||
const RelationRecord *relation_record
|
||||
const OsintSelectionContext *context
|
||||
)
|
||||
{
|
||||
const char *selection_kind =
|
||||
|
|
@ -150,25 +152,23 @@ static void workspace_update_osint_tools_menu(
|
|||
return;
|
||||
}
|
||||
|
||||
if (entity_record != NULL)
|
||||
if (osint_selection_context_get_kind(context) ==
|
||||
OSINT_SELECTION_CONTEXT_KIND_ENTITY)
|
||||
{
|
||||
selection_kind =
|
||||
"Entité";
|
||||
|
||||
selection_identifier =
|
||||
entity_record_get_identifier(
|
||||
entity_record
|
||||
);
|
||||
osint_selection_context_get_identifier(context);
|
||||
}
|
||||
else if (relation_record != NULL)
|
||||
else if (osint_selection_context_get_kind(context) ==
|
||||
OSINT_SELECTION_CONTEXT_KIND_RELATION)
|
||||
{
|
||||
selection_kind =
|
||||
"Relation";
|
||||
|
||||
selection_identifier =
|
||||
relation_record_get_identifier(
|
||||
relation_record
|
||||
);
|
||||
osint_selection_context_get_identifier(context);
|
||||
}
|
||||
|
||||
gtk_widget_set_sensitive(
|
||||
|
|
@ -438,6 +438,17 @@ static void workspace_on_graph_node_selected(
|
|||
return;
|
||||
}
|
||||
|
||||
g_clear_pointer(
|
||||
&workspace->osint_selection_context,
|
||||
osint_selection_context_free
|
||||
);
|
||||
|
||||
if (entity_record != NULL)
|
||||
{
|
||||
workspace->osint_selection_context =
|
||||
osint_selection_context_new_entity(entity_record, NULL);
|
||||
}
|
||||
|
||||
if (workspace->entity_details_panel != NULL)
|
||||
{
|
||||
entity_details_panel_set_entity(
|
||||
|
|
@ -468,6 +479,14 @@ static void workspace_on_graph_node_selected(
|
|||
workspace->graph_model,
|
||||
relation_record_get_target_entity_identifier(relation_record)
|
||||
);
|
||||
|
||||
workspace->osint_selection_context =
|
||||
osint_selection_context_new_relation(
|
||||
relation_record,
|
||||
source_entity,
|
||||
target_entity,
|
||||
NULL
|
||||
);
|
||||
const char *label = relation_record_get_label(relation_record);
|
||||
char *summary = g_strdup_printf(
|
||||
"%s → %s\n\nType : %s\nConfiance : %d %%\nUUID : %s",
|
||||
|
|
@ -491,8 +510,7 @@ static void workspace_on_graph_node_selected(
|
|||
|
||||
workspace_update_osint_tools_menu(
|
||||
workspace,
|
||||
entity_record,
|
||||
relation_record
|
||||
workspace->osint_selection_context
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -2614,6 +2632,11 @@ void workspace_free(Workspace *workspace)
|
|||
return;
|
||||
}
|
||||
|
||||
g_clear_pointer(
|
||||
&workspace->osint_selection_context,
|
||||
osint_selection_context_free
|
||||
);
|
||||
|
||||
investigation_graph_view_set_selection_callback(
|
||||
workspace->graph_view,
|
||||
NULL,
|
||||
|
|
|
|||
84
tests/test_osint_selection_context.c
Normal file
84
tests/test_osint_selection_context.c
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#include "models/osint_selection_context.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
static EntityRecord *create_entity(const char *identifier, const char *value)
|
||||
{
|
||||
return entity_record_new(
|
||||
identifier, "domain", value, NULL, NULL, 80,
|
||||
"2026-01-01T00:00:00Z", "2026-01-01T00:00:00Z",
|
||||
ENTITY_STATUS_ACTIVE, NULL
|
||||
);
|
||||
}
|
||||
|
||||
static void test_entity_context(void)
|
||||
{
|
||||
EntityRecord *entity = create_entity(
|
||||
"11111111-1111-4111-8111-111111111111", "example.org"
|
||||
);
|
||||
OsintSelectionContext *context =
|
||||
osint_selection_context_new_entity(entity, NULL);
|
||||
|
||||
g_assert_nonnull(context);
|
||||
g_assert_cmpint(osint_selection_context_get_kind(context), ==,
|
||||
OSINT_SELECTION_CONTEXT_KIND_ENTITY);
|
||||
g_assert_cmpstr(osint_selection_context_get_type(context), ==, "domain");
|
||||
g_assert_cmpstr(osint_selection_context_get_value(context), ==, "example.org");
|
||||
g_assert_null(osint_selection_context_get_source_value(context));
|
||||
|
||||
entity_record_free(entity);
|
||||
g_assert_cmpstr(osint_selection_context_get_value(context), ==, "example.org");
|
||||
osint_selection_context_free(context);
|
||||
}
|
||||
|
||||
static void test_relation_context(void)
|
||||
{
|
||||
EntityRecord *source = create_entity(
|
||||
"11111111-1111-4111-8111-111111111111", "source.example"
|
||||
);
|
||||
EntityRecord *target = create_entity(
|
||||
"22222222-2222-4222-8222-222222222222", "target.example"
|
||||
);
|
||||
RelationRecord *relation = relation_record_new(
|
||||
"33333333-3333-4333-8333-333333333333",
|
||||
entity_record_get_identifier(source), entity_record_get_identifier(target),
|
||||
"redirects_to", "Redirection", NULL, 90,
|
||||
"2026-01-01T00:00:00Z", "2026-01-01T00:00:00Z",
|
||||
RELATION_STATUS_ACTIVE, NULL
|
||||
);
|
||||
OsintSelectionContext *context = osint_selection_context_new_relation(
|
||||
relation, source, target, NULL
|
||||
);
|
||||
|
||||
g_assert_nonnull(context);
|
||||
g_assert_cmpint(osint_selection_context_get_kind(context), ==,
|
||||
OSINT_SELECTION_CONTEXT_KIND_RELATION);
|
||||
g_assert_cmpstr(osint_selection_context_get_value(context), ==, "Redirection");
|
||||
g_assert_cmpstr(osint_selection_context_get_source_value(context), ==,
|
||||
"source.example");
|
||||
g_assert_cmpstr(osint_selection_context_get_target_value(context), ==,
|
||||
"target.example");
|
||||
|
||||
osint_selection_context_free(context);
|
||||
relation_record_free(relation);
|
||||
entity_record_free(target);
|
||||
entity_record_free(source);
|
||||
}
|
||||
|
||||
static void test_invalid_context(void)
|
||||
{
|
||||
GError *error = NULL;
|
||||
g_assert_null(osint_selection_context_new_entity(NULL, &error));
|
||||
g_assert_error(error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT);
|
||||
g_clear_error(&error);
|
||||
osint_selection_context_free(NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/osint-selection-context/entity", test_entity_context);
|
||||
g_test_add_func("/osint-selection-context/relation", test_relation_context);
|
||||
g_test_add_func("/osint-selection-context/invalid", test_invalid_context);
|
||||
return g_test_run();
|
||||
}
|
||||
Loading…
Reference in a new issue