Skip to content

🔧 fix(TiffPreviewParser): écarter un candidat sans magic FFD8 + outil d'audit#28

Merged
ronan-develop merged 3 commits into
mainfrom
fix/#27-candidat-sans-magic
Jul 16, 2026
Merged

🔧 fix(TiffPreviewParser): écarter un candidat sans magic FFD8 + outil d'audit#28
ronan-develop merged 3 commits into
mainfrom
fix/#27-candidat-sans-magic

Conversation

@ronan-develop

Copy link
Copy Markdown
Owner

Closes #27

Le bug

Un Canon PowerShot G12 levait CorruptedFileException alors que son CR2 contient une preview parfaitement valide.

IFD Compression Taille Contenu réel
IFD0 6 455 Ko ✅ ffd8ffdb — vrai JPEG
IFD2 6 936 Ko ❌ 3a025c02… — données brutes

L'IFD2 ment sur son contenu. Comme il est le plus gros, le tri par taille le présentait en premier — et le parseur levait, sans jamais essayer l'IFD0.

Incohérence : le parseur écartait déjà gracieusement un candidat en JPEG lossless, mais levait sur un candidat sans magic. Les deux cas sont identiques — un candidat inexploitable dans un fichier sain. Un tag qui ment est courant dans les RAW ; CorruptedFileException reste réservée à l'offset hors bornes, l'IFD tronqué, la valeur plus grande que le fichier.

L'outil qui l'a trouvé

bin/audit-cameras.php teste le package contre le catalogue raw.pixls.us — 400+ modèles. Il télécharge, extrait, valide par imagecreatefromstring(), supprime, rapporte.

Deux choix de conception :

  • l'Ă©chantillon est rĂ©parti sur toute la gamme, pas pris en tĂŞte de liste : les N premiers modèles alphabĂ©tiques sont souvent la mĂŞme gĂ©nĂ©ration, or c'est la dĂ©rive entre gĂ©nĂ©rations qui casse les parseurs ;
  • les fichiers sont tĂ©lĂ©chargĂ©s entiers. Une première version n'en prenait que 8 Mo : le parseur refusait alors des offsets tronquĂ©s — Ă  juste titre — et l'audit comptait ces refus comme des bugs. Un auditeur qui ment sur ses Ă©checs coĂ»te plus cher en enquĂŞte qu'il n'Ă©conomise de bande passante.

Résultat

Audit Canon, 21 modèles : 66,7 % → 76,2 %, les deux CorruptedFileException disparues.

Tous les boîtiers EOS passent — du 30D (2006) au R5. Les 5 échecs restants sont des compacts dont les fichiers ne contiennent aucun JPEG (vérifié : zéro FFD8FF) : DNG générés par CHDK avec une vignette 128×96 non compressée, ou .CRW d'avant le CR2, hors périmètre. PreviewNotFoundException y est la bonne réponse.

OK (138 tests, 224 assertions)

… de lever

Un Canon PowerShot G12 levait CorruptedFileException alors que son CR2 contient
une preview parfaitement valide.

Son IFD2 ment : il déclare Compression = 6 sur des données brutes (3a025c02…
au lieu de ffd8). Comme c'est le plus gros bloc (935 Ko contre 455), le tri par
taille le présentait en premier — et le parseur levait, sans jamais essayer
l'IFD0 qui portait le vrai JPEG.

Incohérence de traitement : le parseur écartait déjà gracieusement un candidat
en JPEG lossless, mais levait sur un candidat sans magic. Les deux cas sont
pourtant identiques — un candidat inexploitable dans un fichier sain.

Un tag qui ment sur son contenu est courant dans les RAW. CorruptedFileException
reste réservée à ce qui est structurellement invalide : offset hors bornes, IFD
tronqué, valeur plus grande que le fichier.

Découvert par bin/audit-cameras.php sur 21 modèles Canon.

Closes #27
…ls.us

Valider à la main ne passe pas l'échelle : 406 modèles pour les seules marques
du périmètre. Cet outil parcourt le catalogue, télécharge, extrait, valide par
imagecreatefromstring(), supprime, et rapporte.

  php bin/audit-cameras.php Canon 20     # 20 modèles Canon
  php bin/audit-cameras.php all 500      # tout le catalogue

Rien n'est conservé : seuls les échecs comptent, chacun désignant une structure
que le parseur ne sait pas encore lire.

Deux choix qui comptent :

- l'échantillon est réparti sur toute la gamme, pas pris en tête de liste : les
  N premiers modèles alphabétiques sont souvent la même génération, or c'est la
  dérive entre générations qui casse les parseurs ;
- les fichiers sont téléchargés entiers. Une première version n'en prenait que
  8 Mo : le parseur refusait alors des offsets tronqués — à juste titre — et
  l'audit comptait ces refus comme des bugs. Un auditeur qui ment sur ses
  échecs coûte plus cher en enquête qu'il n'économise de bande passante.

L'outil vit dans bin/, exclu de l'archive publiée par .gitattributes.

Il a trouvé son premier vrai bug dès le premier passage (#27, PowerShot G12).
Le tableau des 9 appareils validés à la main ne dit pas tout : l'auditeur teste
le catalogue raw.pixls.us — 400+ modèles. Le README donne la commande et le
résultat du dernier passage Canon.

Lister les 25 modèles serait illisible et masquerait l'essentiel : tous les
boîtiers EOS passent, du 30D de 2006 au R5. Les échecs restants sont des
compacts dont les fichiers ne contiennent réellement aucune preview JPEG — des
DNG générés par CHDK avec une vignette 128x96 non compressée, ou des .CRW
d'avant le CR2, hors périmètre. PreviewNotFoundException y est la bonne réponse.
@ronan-develop
ronan-develop merged commit a98f2f0 into main Jul 16, 2026
6 checks passed
@ronan-develop
ronan-develop deleted the fix/#27-candidat-sans-magic branch July 16, 2026 20:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🔧 fix(TiffPreviewParser): écarter un candidat sans magic FFD8 au lieu de lever

1 participant