Créer le modèle opaque EvidenceRecord #45

Closed
opened 2026-07-18 16:33:29 +02:00 by fy59 · 0 comments
Owner

Créer le modèle opaque EvidenceRecord

Contexte

Labfy Investigation sait désormais créer et ouvrir une enquête, conserver une session SQLite active, afficher l’arborescence, gérer des tâches en arrière-plan et détecter les outils externes sans bloquer GTK.

L’objectif métier est l’import d’une preuve originale depuis l’interface. Cet import ne doit cependant pas être codé directement dans la vue. Il dépend d’abord d’un modèle métier indépendant de GTK, SQLite et du système de fichiers.

Pipeline prévu :

EvidenceRecord
→ schéma SQLite et DAO
→ SHA-256
→ copie sûre
→ import transactionnel
→ dialogue GTK

Objectif

Créer un type opaque EvidenceRecord représentant les informations persistantes d’une preuve numérique importée dans une enquête.

Le modèle doit :

  • posséder ses chaînes ;
  • exposer uniquement des accesseurs en lecture ;
  • valider les données obligatoires ;
  • distinguer le nom d’origine du nom interne ;
  • représenter l’état d’intégrité ;
  • rester indépendant de GTK, SQLite et des opérations de fichiers ;
  • pouvoir être utilisé ensuite par le DAO et le service d’import.

Périmètre

Créer :

include/models/evidence_record.h
src/models/evidence_record.c
tests/test_evidence_record.c

Mettre à jour le Makefile.

Ce ticket ne doit pas :

  • modifier le schéma SQLite ;
  • calculer un SHA-256 ;
  • copier un fichier ;
  • vérifier l’existence d’un fichier ;
  • ouvrir un dialogue GTK ;
  • modifier Application, MainWindow ou Workspace ;
  • générer lui-même un UUID ou une date.

1. Type opaque

Déclarer :

typedef struct EvidenceRecord EvidenceRecord;

La structure privée reste dans src/models/evidence_record.c.

Structure recommandée :

struct EvidenceRecord
{
    char *identifier;
    char *original_name;
    char *internal_name;
    char *relative_path;
    char *type_identifier;

    guint64 size_bytes;

    char *sha256;
    char *imported_at;
    char *collected_at;
    char *source;
    char *description;

    EvidenceIntegrityStatus integrity_status;
};

2. État d’intégrité

Définir :

typedef enum
{
    EVIDENCE_INTEGRITY_STATUS_UNKNOWN,
    EVIDENCE_INTEGRITY_STATUS_VALID,
    EVIDENCE_INTEGRITY_STATUS_MISSING,
    EVIDENCE_INTEGRITY_STATUS_MODIFIED,
    EVIDENCE_INTEGRITY_STATUS_ERROR
} EvidenceIntegrityStatus;

Interprétation :

UNKNOWN  → aucune vérification effectuée
VALID    → fichier présent et empreinte conforme
MISSING  → fichier attendu absent
MODIFIED → taille ou empreinte différente
ERROR    → vérification impossible

Le modèle ne réalise aucune vérification lui-même.

3. Erreurs

Définir :

typedef enum
{
    EVIDENCE_RECORD_ERROR_INVALID_ARGUMENT,
    EVIDENCE_RECORD_ERROR_INVALID_IDENTIFIER,
    EVIDENCE_RECORD_ERROR_INVALID_NAME,
    EVIDENCE_RECORD_ERROR_INVALID_PATH,
    EVIDENCE_RECORD_ERROR_INVALID_TYPE,
    EVIDENCE_RECORD_ERROR_INVALID_SHA256,
    EVIDENCE_RECORD_ERROR_INVALID_DATE,
    EVIDENCE_RECORD_ERROR_INVALID_STATUS
} EvidenceRecordError;

Ajouter :

#define EVIDENCE_RECORD_ERROR \
    evidence_record_error_quark()

GQuark evidence_record_error_quark(void);

Respecter la convention GLib :

error == NULL || *error == NULL

4. Constructeur

Déclarer :

EvidenceRecord *evidence_record_new(
    const char *identifier,
    const char *original_name,
    const char *internal_name,
    const char *relative_path,
    const char *type_identifier,
    guint64 size_bytes,
    const char *sha256,
    const char *imported_at,
    const char *collected_at,
    const char *source,
    const char *description,
    EvidenceIntegrityStatus integrity_status,
    GError **error
);

Le constructeur copie toutes les chaînes. Le code appelant reste propriétaire de ses arguments.

5. Champs obligatoires

Les champs suivants sont obligatoires :

identifier
original_name
internal_name
relative_path
type_identifier
sha256
imported_at

Ils doivent être non NULL, non vides et non composés uniquement d’espaces.

Le constructeur peut nettoyer des copies avec :

g_strdup()
g_strstrip()

Il ne doit jamais modifier les chaînes reçues.

6. Identifiant

identifier doit être un UUID valide :

g_uuid_string_is_valid()

Le modèle ne génère pas l’UUID.

7. Noms de fichiers

original_name représente le nom reçu lors de la collecte.

internal_name représente le nom utilisé dans l’enquête.

Refuser au minimum :

.
..
/

Refuser également toute valeur contenant un séparateur de chemin.

Le modèle ne corrige pas silencieusement un nom dangereux.

8. Chemin relatif

relative_path doit rester relatif à la racine de l’enquête.

Refuser :

  • un chemin absolu ;
  • une chaîne vide ;
  • . ou .. ;
  • toute composante .. ;
  • un chemin finissant par un séparateur ;
  • une composante vide.

Exemple accepté :

01_Preuves_Originales/capture_001.png

Le modèle ne vérifie pas l’existence du chemin et ne le transforme pas en chemin absolu.

9. Type de preuve

type_identifier est un identifiant métier stable.

Exemples futurs :

image
video
audio
email
document
conversation
bank_record
identity_document
other

Le modèle vérifie seulement que la valeur est non vide. La liste des types ne doit pas être codée en dur dans EvidenceRecord.

10. SHA-256

sha256 doit contenir exactement 64 caractères hexadécimaux.

Accepter 0-9, a-f et A-F, puis conserver la valeur en minuscules.

Une empreinte invalide produit :

EVIDENCE_RECORD_ERROR_INVALID_SHA256

Le modèle ne calcule pas l’empreinte.

11. Dates

imported_at est obligatoire.

collected_at est facultatif.

Format initial :

YYYY-MM-DDTHH:MM:SSZ

Exemple :

2026-07-18T14:30:00Z

Refuser les chaînes vides, les formats incomplets, les caractères inattendus et l’absence du suffixe Z.

Le ticket ne gère pas encore les fuseaux horaires ni les fractions de seconde.

12. Champs facultatifs

Peuvent être NULL :

collected_at
source
description

source et description contenant uniquement des espaces doivent être normalisés en NULL.

13. Taille

size_bytes représente la taille observée lors de l’import.

La valeur 0 est autorisée pour un fichier vide valide.

Le modèle ne lit pas le fichier.

14. Statut

Refuser toute valeur extérieure à EvidenceIntegrityStatus.

UNKNOWN et VALID sont tous deux des états valides.

15. Accesseurs

Ajouter :

const char *evidence_record_get_identifier(
    const EvidenceRecord *evidence_record
);

const char *evidence_record_get_original_name(
    const EvidenceRecord *evidence_record
);

const char *evidence_record_get_internal_name(
    const EvidenceRecord *evidence_record
);

const char *evidence_record_get_relative_path(
    const EvidenceRecord *evidence_record
);

const char *evidence_record_get_type_identifier(
    const EvidenceRecord *evidence_record
);

guint64 evidence_record_get_size_bytes(
    const EvidenceRecord *evidence_record
);

const char *evidence_record_get_sha256(
    const EvidenceRecord *evidence_record
);

const char *evidence_record_get_imported_at(
    const EvidenceRecord *evidence_record
);

const char *evidence_record_get_collected_at(
    const EvidenceRecord *evidence_record
);

const char *evidence_record_get_source(
    const EvidenceRecord *evidence_record
);

const char *evidence_record_get_description(
    const EvidenceRecord *evidence_record
);

EvidenceIntegrityStatus evidence_record_get_integrity_status(
    const EvidenceRecord *evidence_record
);

Pour un objet NULL :

accesseur de chaîne → NULL
taille              → 0
statut               → EVIDENCE_INTEGRITY_STATUS_UNKNOWN

Les chaînes retournées sont empruntées.

16. Destruction

Ajouter :

void evidence_record_free(
    EvidenceRecord *evidence_record
);

La fonction accepte NULL et libère toutes les données possédées.

17. Immutabilité

Aucun setter public ne doit être ajouté.

Un EvidenceRecord représente un état cohérent chargé depuis SQLite ou produit après un import réussi.

18. Tests attendus

Ajouter au minimum :

  1. création valide avec tous les champs ;
  2. création valide avec champs facultatifs à NULL ;
  3. copie indépendante des chaînes ;
  4. nettoyage des espaces ;
  5. UUID invalide ;
  6. nom original invalide ;
  7. nom interne invalide ;
  8. chemin absolu refusé ;
  9. chemin contenant .. refusé ;
  10. chemin relatif valide accepté ;
  11. type vide refusé ;
  12. SHA-256 trop court ;
  13. SHA-256 contenant un caractère invalide ;
  14. SHA-256 majuscule normalisé en minuscule ;
  15. date d’import invalide ;
  16. date de collecte invalide ;
  17. statut invalide ;
  18. taille nulle acceptée ;
  19. accesseurs avec NULL ;
  20. evidence_record_free(NULL) ;
  21. GError facultatif ;
  22. anciens tests toujours valides.

Utiliser GLib Test :

g_test_init()
g_test_add_func()
g_test_run()

19. Mémoire

Chaque sortie d’échec du constructeur doit libérer :

  • les copies temporaires ;
  • les chaînes déjà affectées ;
  • la structure partiellement construite ;
  • les erreurs locales.

Aucun champ ne doit pointer vers une chaîne détenue par l’appelant.

20. Makefile

Ajouter :

tests/test_evidence_record

Compiler avec :

-std=c17
-Wall
-Wextra
-Wpedantic
-Werror

Inclure le test dans make test et make clean.

Le module ne dépend que de GLib.

21. Audit d’architecture

Ces commandes ne doivent rien afficher :

rg -n \
    '#include <gtk|sqlite3_|Database|Gtk' \
    include/models/evidence_record.h \
    src/models/evidence_record.c
rg -n \
    'g_file_|GFile|open\\(|read\\(|write\\(|stat\\(' \
    include/models/evidence_record.h \
    src/models/evidence_record.c

22. Critères d’acceptation

  • EvidenceRecord est opaque.
  • Toutes les chaînes sont copiées.
  • Les champs obligatoires sont validés.
  • L’identifiant est un UUID valide.
  • Les noms dangereux sont refusés.
  • Le chemin reste relatif.
  • Les composantes .. sont refusées.
  • Le SHA-256 contient 64 caractères hexadécimaux.
  • Le SHA-256 est stocké en minuscules.
  • La date d’import est obligatoire et validée.
  • La date de collecte est facultative et validée lorsqu’elle existe.
  • Les champs facultatifs vides sont normalisés.
  • La taille nulle est autorisée.
  • Le statut est validé.
  • Aucun setter générique n’est exposé.
  • Aucun accès GTK, SQLite ou fichier n’est ajouté.
  • Les accesseurs avec NULL sont sûrs.
  • La destruction avec NULL est sûre.
  • Les tests ciblés passent.
  • Tous les anciens tests passent.
  • make réussit sans warning.
  • git diff --check ne remonte aucune erreur.
  • Valgrind ne détecte aucune perte directe ou indirecte.

23. Validation finale

make clean
make
make test
git diff --check

Puis :

G_DEBUG=gc-friendly \
G_SLICE=always-malloc \
valgrind \
    --leak-check=full \
    --show-leak-kinds=definite,indirect \
    --errors-for-leak-kinds=definite,indirect \
    --track-origins=yes \
    --error-exitcode=1 \
    ./tests/test_evidence_record

24. Fichiers concernés

include/models/evidence_record.h
src/models/evidence_record.c
tests/test_evidence_record.c
Makefile

Aucun autre fichier ne doit être modifié sans justification.

Résultat attendu

À la fin du ticket, Labfy dispose d’un modèle métier fiable pour représenter une preuve.

Le ticket suivant créera le schéma SQLite et le DAO des preuves. L’import GTK viendra après la persistance, le SHA-256, la copie sûre et le service transactionnel.

# Créer le modèle opaque `EvidenceRecord` ## Contexte Labfy Investigation sait désormais créer et ouvrir une enquête, conserver une session SQLite active, afficher l’arborescence, gérer des tâches en arrière-plan et détecter les outils externes sans bloquer GTK. L’objectif métier est l’import d’une preuve originale depuis l’interface. Cet import ne doit cependant pas être codé directement dans la vue. Il dépend d’abord d’un modèle métier indépendant de GTK, SQLite et du système de fichiers. Pipeline prévu : ```text EvidenceRecord → schéma SQLite et DAO → SHA-256 → copie sûre → import transactionnel → dialogue GTK ``` ## Objectif Créer un type opaque `EvidenceRecord` représentant les informations persistantes d’une preuve numérique importée dans une enquête. Le modèle doit : - posséder ses chaînes ; - exposer uniquement des accesseurs en lecture ; - valider les données obligatoires ; - distinguer le nom d’origine du nom interne ; - représenter l’état d’intégrité ; - rester indépendant de GTK, SQLite et des opérations de fichiers ; - pouvoir être utilisé ensuite par le DAO et le service d’import. ## Périmètre Créer : ```text include/models/evidence_record.h src/models/evidence_record.c tests/test_evidence_record.c ``` Mettre à jour le `Makefile`. Ce ticket ne doit pas : - modifier le schéma SQLite ; - calculer un SHA-256 ; - copier un fichier ; - vérifier l’existence d’un fichier ; - ouvrir un dialogue GTK ; - modifier `Application`, `MainWindow` ou `Workspace` ; - générer lui-même un UUID ou une date. --- ## 1. Type opaque Déclarer : ```c typedef struct EvidenceRecord EvidenceRecord; ``` La structure privée reste dans `src/models/evidence_record.c`. Structure recommandée : ```c struct EvidenceRecord { char *identifier; char *original_name; char *internal_name; char *relative_path; char *type_identifier; guint64 size_bytes; char *sha256; char *imported_at; char *collected_at; char *source; char *description; EvidenceIntegrityStatus integrity_status; }; ``` ## 2. État d’intégrité Définir : ```c typedef enum { EVIDENCE_INTEGRITY_STATUS_UNKNOWN, EVIDENCE_INTEGRITY_STATUS_VALID, EVIDENCE_INTEGRITY_STATUS_MISSING, EVIDENCE_INTEGRITY_STATUS_MODIFIED, EVIDENCE_INTEGRITY_STATUS_ERROR } EvidenceIntegrityStatus; ``` Interprétation : ```text UNKNOWN → aucune vérification effectuée VALID → fichier présent et empreinte conforme MISSING → fichier attendu absent MODIFIED → taille ou empreinte différente ERROR → vérification impossible ``` Le modèle ne réalise aucune vérification lui-même. ## 3. Erreurs Définir : ```c typedef enum { EVIDENCE_RECORD_ERROR_INVALID_ARGUMENT, EVIDENCE_RECORD_ERROR_INVALID_IDENTIFIER, EVIDENCE_RECORD_ERROR_INVALID_NAME, EVIDENCE_RECORD_ERROR_INVALID_PATH, EVIDENCE_RECORD_ERROR_INVALID_TYPE, EVIDENCE_RECORD_ERROR_INVALID_SHA256, EVIDENCE_RECORD_ERROR_INVALID_DATE, EVIDENCE_RECORD_ERROR_INVALID_STATUS } EvidenceRecordError; ``` Ajouter : ```c #define EVIDENCE_RECORD_ERROR \ evidence_record_error_quark() GQuark evidence_record_error_quark(void); ``` Respecter la convention GLib : ```c error == NULL || *error == NULL ``` ## 4. Constructeur Déclarer : ```c EvidenceRecord *evidence_record_new( const char *identifier, const char *original_name, const char *internal_name, const char *relative_path, const char *type_identifier, guint64 size_bytes, const char *sha256, const char *imported_at, const char *collected_at, const char *source, const char *description, EvidenceIntegrityStatus integrity_status, GError **error ); ``` Le constructeur copie toutes les chaînes. Le code appelant reste propriétaire de ses arguments. ## 5. Champs obligatoires Les champs suivants sont obligatoires : ```text identifier original_name internal_name relative_path type_identifier sha256 imported_at ``` Ils doivent être non `NULL`, non vides et non composés uniquement d’espaces. Le constructeur peut nettoyer des copies avec : ```c g_strdup() g_strstrip() ``` Il ne doit jamais modifier les chaînes reçues. ## 6. Identifiant `identifier` doit être un UUID valide : ```c g_uuid_string_is_valid() ``` Le modèle ne génère pas l’UUID. ## 7. Noms de fichiers `original_name` représente le nom reçu lors de la collecte. `internal_name` représente le nom utilisé dans l’enquête. Refuser au minimum : ```text . .. / ``` Refuser également toute valeur contenant un séparateur de chemin. Le modèle ne corrige pas silencieusement un nom dangereux. ## 8. Chemin relatif `relative_path` doit rester relatif à la racine de l’enquête. Refuser : - un chemin absolu ; - une chaîne vide ; - `.` ou `..` ; - toute composante `..` ; - un chemin finissant par un séparateur ; - une composante vide. Exemple accepté : ```text 01_Preuves_Originales/capture_001.png ``` Le modèle ne vérifie pas l’existence du chemin et ne le transforme pas en chemin absolu. ## 9. Type de preuve `type_identifier` est un identifiant métier stable. Exemples futurs : ```text image video audio email document conversation bank_record identity_document other ``` Le modèle vérifie seulement que la valeur est non vide. La liste des types ne doit pas être codée en dur dans `EvidenceRecord`. ## 10. SHA-256 `sha256` doit contenir exactement 64 caractères hexadécimaux. Accepter `0-9`, `a-f` et `A-F`, puis conserver la valeur en minuscules. Une empreinte invalide produit : ```c EVIDENCE_RECORD_ERROR_INVALID_SHA256 ``` Le modèle ne calcule pas l’empreinte. ## 11. Dates `imported_at` est obligatoire. `collected_at` est facultatif. Format initial : ```text YYYY-MM-DDTHH:MM:SSZ ``` Exemple : ```text 2026-07-18T14:30:00Z ``` Refuser les chaînes vides, les formats incomplets, les caractères inattendus et l’absence du suffixe `Z`. Le ticket ne gère pas encore les fuseaux horaires ni les fractions de seconde. ## 12. Champs facultatifs Peuvent être `NULL` : ```text collected_at source description ``` `source` et `description` contenant uniquement des espaces doivent être normalisés en `NULL`. ## 13. Taille `size_bytes` représente la taille observée lors de l’import. La valeur `0` est autorisée pour un fichier vide valide. Le modèle ne lit pas le fichier. ## 14. Statut Refuser toute valeur extérieure à `EvidenceIntegrityStatus`. `UNKNOWN` et `VALID` sont tous deux des états valides. ## 15. Accesseurs Ajouter : ```c const char *evidence_record_get_identifier( const EvidenceRecord *evidence_record ); const char *evidence_record_get_original_name( const EvidenceRecord *evidence_record ); const char *evidence_record_get_internal_name( const EvidenceRecord *evidence_record ); const char *evidence_record_get_relative_path( const EvidenceRecord *evidence_record ); const char *evidence_record_get_type_identifier( const EvidenceRecord *evidence_record ); guint64 evidence_record_get_size_bytes( const EvidenceRecord *evidence_record ); const char *evidence_record_get_sha256( const EvidenceRecord *evidence_record ); const char *evidence_record_get_imported_at( const EvidenceRecord *evidence_record ); const char *evidence_record_get_collected_at( const EvidenceRecord *evidence_record ); const char *evidence_record_get_source( const EvidenceRecord *evidence_record ); const char *evidence_record_get_description( const EvidenceRecord *evidence_record ); EvidenceIntegrityStatus evidence_record_get_integrity_status( const EvidenceRecord *evidence_record ); ``` Pour un objet `NULL` : ```text accesseur de chaîne → NULL taille → 0 statut → EVIDENCE_INTEGRITY_STATUS_UNKNOWN ``` Les chaînes retournées sont empruntées. ## 16. Destruction Ajouter : ```c void evidence_record_free( EvidenceRecord *evidence_record ); ``` La fonction accepte `NULL` et libère toutes les données possédées. ## 17. Immutabilité Aucun setter public ne doit être ajouté. Un `EvidenceRecord` représente un état cohérent chargé depuis SQLite ou produit après un import réussi. ## 18. Tests attendus Ajouter au minimum : 1. création valide avec tous les champs ; 2. création valide avec champs facultatifs à `NULL` ; 3. copie indépendante des chaînes ; 4. nettoyage des espaces ; 5. UUID invalide ; 6. nom original invalide ; 7. nom interne invalide ; 8. chemin absolu refusé ; 9. chemin contenant `..` refusé ; 10. chemin relatif valide accepté ; 11. type vide refusé ; 12. SHA-256 trop court ; 13. SHA-256 contenant un caractère invalide ; 14. SHA-256 majuscule normalisé en minuscule ; 15. date d’import invalide ; 16. date de collecte invalide ; 17. statut invalide ; 18. taille nulle acceptée ; 19. accesseurs avec `NULL` ; 20. `evidence_record_free(NULL)` ; 21. `GError` facultatif ; 22. anciens tests toujours valides. Utiliser GLib Test : ```c g_test_init() g_test_add_func() g_test_run() ``` ## 19. Mémoire Chaque sortie d’échec du constructeur doit libérer : - les copies temporaires ; - les chaînes déjà affectées ; - la structure partiellement construite ; - les erreurs locales. Aucun champ ne doit pointer vers une chaîne détenue par l’appelant. ## 20. Makefile Ajouter : ```text tests/test_evidence_record ``` Compiler avec : ```text -std=c17 -Wall -Wextra -Wpedantic -Werror ``` Inclure le test dans `make test` et `make clean`. Le module ne dépend que de GLib. ## 21. Audit d’architecture Ces commandes ne doivent rien afficher : ```bash rg -n \ '#include <gtk|sqlite3_|Database|Gtk' \ include/models/evidence_record.h \ src/models/evidence_record.c ``` ```bash rg -n \ 'g_file_|GFile|open\\(|read\\(|write\\(|stat\\(' \ include/models/evidence_record.h \ src/models/evidence_record.c ``` ## 22. Critères d’acceptation - [x] `EvidenceRecord` est opaque. - [x] Toutes les chaînes sont copiées. - [x] Les champs obligatoires sont validés. - [x] L’identifiant est un UUID valide. - [x] Les noms dangereux sont refusés. - [x] Le chemin reste relatif. - [x] Les composantes `..` sont refusées. - [x] Le SHA-256 contient 64 caractères hexadécimaux. - [x] Le SHA-256 est stocké en minuscules. - [x] La date d’import est obligatoire et validée. - [x] La date de collecte est facultative et validée lorsqu’elle existe. - [x] Les champs facultatifs vides sont normalisés. - [x] La taille nulle est autorisée. - [x] Le statut est validé. - [x] Aucun setter générique n’est exposé. - [x] Aucun accès GTK, SQLite ou fichier n’est ajouté. - [x] Les accesseurs avec `NULL` sont sûrs. - [x] La destruction avec `NULL` est sûre. - [x] Les tests ciblés passent. - [x] Tous les anciens tests passent. - [x] `make` réussit sans warning. - [x] `git diff --check` ne remonte aucune erreur. - [x] Valgrind ne détecte aucune perte directe ou indirecte. ## 23. Validation finale ```bash make clean make make test git diff --check ``` Puis : ```bash G_DEBUG=gc-friendly \ G_SLICE=always-malloc \ valgrind \ --leak-check=full \ --show-leak-kinds=definite,indirect \ --errors-for-leak-kinds=definite,indirect \ --track-origins=yes \ --error-exitcode=1 \ ./tests/test_evidence_record ``` ## 24. Fichiers concernés ```text include/models/evidence_record.h src/models/evidence_record.c tests/test_evidence_record.c Makefile ``` Aucun autre fichier ne doit être modifié sans justification. ## Résultat attendu À la fin du ticket, Labfy dispose d’un modèle métier fiable pour représenter une preuve. Le ticket suivant créera le schéma SQLite et le DAO des preuves. L’import GTK viendra après la persistance, le SHA-256, la copie sûre et le service transactionnel.
fy59 closed this issue 2026-07-18 17:06:40 +02:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: fy59/labfy-investigation#45
No description provided.