Aller au contenu

PD-101 — Specification Review

Méta

  • Auditeur : Claude (review indépendante)
  • Documents analysés : PD-101-specification.md, PD-101-tests.md
  • Date : 2026-03-10

Écarts identifiés

E-01 — Ambiguïté sur le fichier probatoire en mode Photos picker sans transcodage

Type : Ambiguïté
Référence : Spec §5.1 étape 4, §5.2, INV-03-probatory-fidelity-default
Description : En mode par défaut, la spec indique que le fichier probatoire est « bit-à-bit identique au fichier soumis » (INV-03). Cependant, le flux 5.1 étape 4 mentionne un avertissement si transcodage détecté via Photos picker, avec possibilité de « continuer ». Si l'utilisateur continue malgré le transcodage, le fichier soumis EST le fichier transcodé par iOS. Le hash probatoire porte alors sur un fichier déjà altéré par iOS, mais `optimized` reste `false`. Il y a ambiguïté sur la sémantique de « fidèle » : fidèle au fichier reçu par l'app (post-transcodage iOS) ou fidèle au fichier original de la photothèque ?
Impact : Litige juridique potentiel sur l'intégrité probatoire si l'utilisateur conteste l'altération iOS non tracée.
Gravité : Majeur

E-02 — Contradiction sur la transition READY → CANCELLED

Type : Contradiction
Référence : Spec §5.6 (table transitions), §6 ERR-07
Description : La table §5.6 interdit explicitement la transition backend `READY → CANCELLED`. Cependant, ERR-07 (annulation utilisateur) décrit une orchestration « backend CANCELLED » sans restreindre l'état de départ. Si l'annulation survient après que le backend a transitionné vers READY (entre upload terminé et finalisation PENDING_SEAL), la machine à états interdit le passage à CANCELLED. L'état du document reste alors READY sans possibilité d'annulation.
Impact : Un document « abandonné » en état READY pourrait être scellé par le worker d'ancrage (PD-55) sans que l'utilisateur le souhaite.
Gravité : Bloquant

E-03 — Absence de définition du format/contenu de la « preuve de consentement explicite horodatée »

Type : Ambiguïté
Référence : Spec §5.2 étape 2, INV-04-explicit-optimized-consent, CA-02, TC-NOM-03
Description : La spec exige un « consentement explicite traçable » et le test TC-NOM-03 vérifie « la preuve de consentement explicite horodatée ». Ni la spec ni les tests ne définissent le format de cette preuve : s'agit-il d'un champ booléen + timestamp en DB ? D'un événement d'audit signé ? D'un log applicatif ? Le terme « journal métier » (CA-02) diffère de « journal d'audit » (§8 observabilité) sans clarification de leur relation.
Impact : Implémentation non contractualisée, auditabilité impossible à valider.
Gravité : Majeur

E-04 — Seuil multipart en MB décimaux vs. octets entiers

Type : Ambiguïté
Référence : Spec §5.4, CA-06, TC-NOM-02
Description : Le seuil multipart est « 10 MB » et le test vérifie « 9.99 MB simple / 10.00 MB multipart ». Or `file_size_bytes` est un entier en octets (§3.2). La valeur exacte en octets de « 10 MB » n'est pas définie : 10 × 1000² = 10 000 000 bytes (SI) ou 10 × 1024² = 10 485 760 bytes (binaire) ? Un fichier de 10 000 001 bytes serait traité différemment selon l'interprétation.
Impact : Comportement non déterministe au seuil exact, impossibilité de valider CA-06 sans convention octets.
Gravité : Mineur

E-05 — Hash SHA3-256 : validation fonctionnelle non spécifiée côté mobile

Type : Ambiguïté
Référence : INV-05-hash-functional-validation, CA-11, TC-INV-05
Description : INV-05 exige que le hash soit « validé fonctionnellement (recalcul contrôlé) ». Le test TC-INV-05 vérifie « corruption binaire détectée malgré format hash valide ». Mais la spec ne décrit pas QUI effectue cette validation fonctionnelle ni QUAND : le mobile recalcule-t-il après chiffrement ? Le backend recalcule-t-il après déchiffrement (impossible en zero-knowledge) ? Le seul mécanisme décrit (§5.1 étape 6) est le calcul initial du hash, pas un recalcul de vérification.
Impact : L'invariant INV-05 est potentiellement non implémentable en zero-knowledge sans mécanisme explicite.
Gravité : Bloquant

E-06 — Absence de test pour le disclaimer EXIF (flux 5.1 étape 5)

Type : Incohérence Spec↔Tests
Référence : Spec §5.1 étape 5, Tests §2 matrice couverture
Description : Le flux 5.1 étape 5 exige « Affichage disclaimer métadonnées EXIF et confirmation explicite ». Aucun test (TC-NOM-*, TC-ERR-*, TC-INV-*, TC-NEG-*) ne couvre ce comportement. La matrice de couverture §2 ne le mentionne pas.
Impact : Exigence spécifiée sans preuve contractuelle, risque RGPD sur métadonnées non consenties.
Gravité : Majeur

E-07 — Contradiction terminologique : « journal métier » vs « journal d'audit » vs « journaux techniques »

Type : Ambiguïté (terminologique)
Référence : CA-02 (« journal métier »), §8 observabilité (« journal d'audit »), TC-NOM-06 (« journaux techniques »)
Description : Trois termes distincts sont utilisés pour désigner des systèmes de journalisation sans définition ni distinction claire. CA-02 mentionne « journal métier + metadata backend cohérents ». L'observabilité §8 définit un « journal d'audit ». TC-NOM-06 parle de « journaux techniques ». S'agit-il du même système ou de trois systèmes distincts ? La distinction affecte directement la testabilité.
Impact : Ambiguïté sur les observables de test ; un implémenteur pourrait satisfaire un journal mais pas les autres.
Gravité : Mineur

E-08 — Hypothèse dangereuse : atomicité de l'annulation multipart S3

Type : Hypothèse dangereuse
Référence : Spec §5.6, ERR-07, CA-08, TC-ERR-07
Description : L'annulation orchestrée suppose que `AbortMultipartUpload` S3 est atomique et supprime toutes les parts actives. En réalité, des parts déjà uploadées peuvent persister si l'abort intervient pendant un transfert concurrent. La spec ne mentionne aucun mécanisme de réconciliation pour les parts orphelines (lifecycle policy S3, cleanup asynchrone). TC-ERR-07 vérifie « parts multipart S3 restantes absentes/inactives après orchestration » sans définir le délai acceptable.
Impact : Parts S3 orphelines = coût stockage + risque de fuite de données chiffrées résiduelles.
Gravité : Majeur

E-09 — INV-08 (envelope encryption) non rattaché à un critère d'acceptation

Type : Incohérence Spec↔Tests
Référence : INV-08-envelope-encryption, Tests §2 matrice (colonne CA = « — »)
Description : L'invariant INV-08 est couvert par TC-INV-08 mais n'est rattaché à aucun critère d'acceptation (CA). La matrice montre « — » en colonne CA. Or chaque invariant non négociable devrait avoir au moins un critère d'acceptation observable. Le test TC-INV-08 est décrit dans la table §5 mais aucun scénario détaillé (Given/When/Then) n'est fourni.
Impact : Invariant non négociable sans critère d'acceptation formel ni scénario de test détaillé.
Gravité : Majeur

E-10 — INV-10 (no-sensitive-logs) non rattaché à un critère d'acceptation

Type : Incohérence Spec↔Tests
Référence : INV-10-no-sensitive-logs, Tests §2 matrice (colonne CA = « — »)
Description : Même problème que E-09. INV-10 est couvert par TC-INV-10 mais sans critère d'acceptation (CA) ni scénario détaillé Given/When/Then.
Impact : Invariant non négociable sans critère d'acceptation formel.
Gravité : Majeur

E-11 — Absence de scénarios détaillés pour TC-INV-01 à TC-INV-10

Type : Incohérence Spec↔Tests
Référence : Tests §5 (table invariants), Tests §3-§4 (scénarios détaillés)
Description : Les tests d'invariants TC-INV-01 à TC-INV-10 sont listés dans la table §5 avec un observable mais aucun scénario détaillé Given/When/Then n'est fourni (contrairement aux TC-NOM-* et TC-ERR-* en §3-§4). TC-INV-03 et TC-INV-04 renvoient vers TC-NOM-01/TC-NOM-03 qui couvrent partiellement, mais TC-INV-01, TC-INV-02, TC-INV-05, TC-INV-06, TC-INV-08, TC-INV-09, TC-INV-10 n'ont pas de scénario autonome.
Impact : Tests non reproductibles par une équipe tierce sans interprétation.
Gravité : Majeur

E-12 — Hypothèse dangereuse : Hermes et crypto en mémoire

Type : Hypothèse dangereuse
Référence : INV-07-sensitive-purge, INV-08-envelope-encryption, Spec §5.4 (mémoire claire 0..100 MB)
Description : La spec exige « purge immédiate » des artefacts sensibles et zéro persistance en clair. Or l'hypothèse H-01 confirme React Native + Expo SDK 54 (Hermes engine). Hermes ne supporte pas WebAssembly, les strings JavaScript sont immutables, et la zéroisation mémoire est best-effort uniquement (REX PD-97). La spec mentionne « 0..100 MB mémoire claire en RAM » comme contrainte mais Q-06 reconnaît l'absence de critère d'audit mesurable. L'invariant INV-07 est donc partiellement non satisfaisable sur Hermes pour la composante RAM.
Impact : Faux sentiment de conformité sur la purge mémoire ; risque de non-conformité audit sécurité.
Gravité : Majeur

E-13 — Absence de spécification du comportement retry après FAILED → UPLOADING

Type : Ambiguïté
Référence : Spec §5.6 (transition UI FAILED → UPLOADING)
Description : La table §5.6 autorise la transition `FAILED → UPLOADING` (« nouvelle tentative utilisateur »). Cependant, aucun flux nominal ni cas d'erreur ne décrit ce qui se passe concrètement : le flux reprend-il depuis le début (nouveau `doc_id`) ? Depuis le dernier chunk réussi ? Le hash est-il recalculé ? Le backend conserve-t-il l'enregistrement PENDING ou un nouveau est-il créé ? Aucun test ne couvre cette transition.
Impact : Comportement d'implémentation laissé entièrement à l'interprétation.
Gravité : Majeur

E-14 — Absence de test TC-ERR-06

Type : Incohérence Spec↔Tests
Référence : Tests §4 (numérotation TC-ERR-01 à TC-ERR-07, TC-ERR-06 absent)
Description : La numérotation des tests d'erreur saute de TC-ERR-05 à TC-ERR-07. TC-ERR-06 n'existe pas. Ce n'est pas un simple trou de numérotation : aucun test ne couvre explicitement le cas ERR-06 (HTTP 4xx fonctionnel non retryable → échec immédiat FAILED). TC-ERR-04 couvre ERR-06/ERR-09 pour la validation de métadonnées, mais pas le comportement « échec immédiat, état FAILED, message explicite » sur un 4xx fonctionnel en cours d'upload.
Impact : Cas d'erreur spécifié sans test dédié.
Gravité : Mineur

E-15 — Risque sécurité : base64 du nonce et auth_tag en transport sans intégrité du canal

Type : Risque sécu/conformité
Référence : Spec §3.2, H-05
Description : Le nonce AES-GCM et l'auth_tag sont transportés en base64 vers le backend. La spec ne mentionne aucune protection d'intégrité de ces métadonnées cryptographiques en transit (HMAC, signature). Si un attaquant MITM altère le nonce ou l'auth_tag transmis au backend, les métadonnées stockées seront corrompues, rendant le déchiffrement impossible sans que le système ne le détecte. La seule protection implicite est TLS, mais cela n'est pas contractualisé.
Impact : Corruption silencieuse des métadonnées cryptographiques si TLS compromis ou mal configuré.
Gravité : Mineur

E-16 — Ambiguïté sur la granularité de « unique par document (doc_id) » pour le nonce

Type : Ambiguïté
Référence : INV-06-nonce-uniqueness, §3.2, TC-INV-06
Description : INV-06 exige « nonce unique par document (doc_id) ». En upload multipart, chaque chunk est-il chiffré avec le même nonce (interdit en AES-GCM — réutilisation de nonce = catastrophique) ou avec un nonce distinct par chunk ? Si le fichier entier est chiffré avant chunking, un seul nonce suffit. Si le chiffrement est streamé par chunk, chaque chunk nécessite son propre nonce. La spec ne précise pas l'architecture de chiffrement (file-level vs chunk-level).
Impact : Si chiffrement chunk-level avec nonce unique → vulnérabilité cryptographique critique (nonce reuse en GCM).
Gravité : Bloquant

Synthèse

Gravité Nombre
Bloquant 3
Majeur 9
Mineur 4
Total 16

Bloquants

ID Résumé
E-02 Contradiction transition READY → CANCELLED interdite vs annulation post-upload
E-05 Validation fonctionnelle du hash non implémentable en zero-knowledge sans mécanisme décrit
E-16 Granularité du nonce (file-level vs chunk-level) non spécifiée — risque nonce reuse GCM

Majeurs

ID Résumé
E-01 Ambiguïté fichier probatoire post-transcodage iOS en mode fidèle
E-03 Format preuve de consentement non défini
E-06 Disclaimer EXIF sans test associé
E-08 Atomicité annulation multipart S3 non contractualisée
E-09 INV-08 sans critère d'acceptation
E-10 INV-10 sans critère d'acceptation
E-11 TC-INV-01..10 sans scénarios Given/When/Then
E-12 Purge mémoire Hermes best-effort vs invariant strict
E-13 Transition FAILED → UPLOADING non spécifiée