Discourse détecte incorrectement le fichier téléchargé comme une image

J’ai des utilisateurs d’ingénierie qui souhaitent joindre des fichiers de données avec des extensions de fichiers inhabituelles à leurs publications. Ce sont essentiellement des fichiers texte brut, mais ils incluent des caractères ASCII étendus.

J’ai essayé de mettre à jour la configuration NGINX de Discourse pour spécifier les types de médias MIME pour ces fichiers, mais cela n’a pas fonctionné. J’ai posté un sujet (How to customize MIME media type emitted for certain attachments?) à ce sujet il y a deux semaines, mais je n’ai reçu aucune réponse jusqu’à présent. Même si NGINX n’est pas mis à jour, il servira toujours les types de fichiers inconnus en utilisant le type MIME de repli “application/octet-stream”. Je peux vivre avec ça pour l’instant.

Cependant, lorsque les utilisateurs essaient de télécharger ces fichiers de données dans une publication (en utilisant le bouton “Télécharger” ou le glisser-déposer), ils reçoivent un message d’erreur de Discourse comme celui-ci :

Il semble que lorsque les utilisateurs téléchargent des fichiers, Discourse essaie d’être intelligent et de détecter s’il s’agit d’une image ou d’un autre type de fichier. De plus, il semble faire cette détermination en examinant le contenu du fichier (un peu comme la commande Unix standard “file”). Je suppose que c’est pour que Discourse puisse décider s’il doit l’intégrer au contenu de la publication ou le placer sur le côté en tant que pièce jointe.

Dans le cas de ces fichiers de données, cette vérification identifie incorrectement les fichiers comme des images. Juste pour le plaisir, j’ai mis certains de ces fichiers de données sur une machine Ubuntu et je les ai vérifiés avec la commande “file”, et effectivement, ils ont été identifiés comme “données d’image JPEG”.

Existe-t-il un moyen pour les utilisateurs de télécharger des fichiers sans que Discourse essaie de détecter s’il s’agit d’images ? C’est-à-dire, “Veuillez télécharger ceci en tant que pièce jointe, quoi qu’il arrive, ne l’intégrez pas” ?

Alternativement, je pourrais configurer Discourse pour autoriser les fichiers zip et dire aux utilisateurs de zipper leurs fichiers avant de les télécharger, mais je préférerais ne pas ouvrir le site aux téléchargements de fichiers zip aléatoires. Cela semble être un problème de sécurité.

Merci d’avance pour toute aide !

Une autre solution de contournement consiste à adopter une extension pour ces fichiers étranges, comme .bin, .data ou vraiment .anythinggoes, ce qui devrait suffire à Discourse pour laisser ces fichiers tranquilles.

Merci pour votre réponse rapide ! Cependant, j’ai essayé cela et cela ne fonctionne pas. Discourse examine définitivement le contenu du fichier, et non son extension, pour déterminer s’il s’agit d’une image ou non.

1 « J'aime »

Quelqu’un a-t-il des réflexions à ce sujet ? Cela semble être un bug assez important.

J’ai creusé un peu ce problème.

En bref, votre fichier est détecté comme JPEG car il commence par la même signature que ce type de fichier.
Corriger ce comportement dans Discourse est possible, mais cela nécessite une modification. (voir à la fin)


Voici quelques informations techniques.
Ce problème commence ici :

La bibliothèque FastImage ouvre le fichier et détermine le type et la taille.
Comme vous vous y attendez, elle renvoie un type JPEG.

Si vous regardez la signature JPEG, elle ressemble à ceci :

Marqueurs JPEG

Plus d’informations : List of file signatures - Wikipedia
JPEG - Wikipedia

Elle commence toujours par les octets de marqueur suivants : FF D8

En regardant votre échantillon de fichier avec un éditeur hexadécimal, vous verrez qu’il commence de la même manière.

Maintenant, si vous regardez comment FastImage détecte un JPEG, vous pouvez voir ici :

Cependant, vous ne pouvez pas extraire d’informations d’image car tous les octets requis ne sont pas présents.

Comment résoudre ce problème dans Discourse ?
En regardant le code de FastImage, il y a une option précieuse que vous pouvez passer.

En utilisant cette option, toute erreur (SizeNotFound, ImageFetchFailure, CannotParseImage, UnknownImageType, BadImageURI) ne renverra aucune information d’image ; et votre fichier ne sera pas détecté comme une image.

@image_info =
  begin
    FastImage.new(@file, raise_on_failure: true)
  rescue StandardError
    nil
  end
...
is_image ||= @image_info && FileHelper.is_supported_image?("test.#{@image_info.type}")

Alors, cela peut fonctionner maintenant :

Je pourrai faire une PR plus tard. Utiliser cette option a du sens ici. :+1:

2 « J'aime »

Wow ! C’est une analyse phénoménale ! Merci !

Quelques questions rapides :

  1. Avec ces changements, le fichier ne sera pas détecté comme une image et sera téléchargé comme un non-image et affiché à droite de la publication ?

  2. Si je comprends bien, vous suggérez que je fasse ces ajustements dans mon instance Discourse locale pour essayer cela et/ou l’utiliser jusqu’à ce qu’il soit inclus dans une future version de Discourse. Mais comment faire ? (Je suis un développeur logiciel expérimenté mais j’ai une expérience limitée avec Docker et aucune avec Ruby.)

  3. L’appel FastImage qui devrait être modifié se trouve dans models/upload.rb, n’est-ce pas ?

  1. Oui, c’est exact – comme dans ma capture d’écran ci-dessus

  2. Je ne suggère pas que vous fassiez la modification. Cependant, si vous ne pouvez pas attendre, vous pourriez certainement tester ce changement.

  • Pour un changement temporaire (disparu après reconstruction) :
cd /var/discourse
./launcher enter app
sed -i "s/FastImage.new(@file)/FastImage.new(@file, :raise_on_failure=true)/" lib/upload_creator.rb
sed -i "s/FastImage.new(original_path)/FastImage.new(original_path, :raise_on_failure=true)/" app/models/upload.rb
exit
  • Pour un changement persistant (reste après reconstruction) :
cd /var/discourse
nano containers/app.yml  (utilisez votre éditeur préféré)

Ajoutez les commandes personnalisées suivantes à la fin (section run) :

  - replace:
      filename: "/var/www/discourse/lib/upload_creator.rb"
      from: "FastImage.new(@file)"
      to: "FastImage.new(@file, :raise_on_failure=true)"
  - replace:
      filename: "/var/www/discourse/app/models/upload.rb"
      from: "FastImage.new(original_path)"
      to: "FastImage.new(original_path, :raise_on_failure=true)"

Ensuite, reconstruisez :

./launcher rebuild app
  1. Je suppose que oui si vous prévoyez de télécharger des fichiers sans extensions. Je n’ai pas vérifié si d’autres occurrences nécessitaient le même changement.
1 « J'aime »

@Arkshine – Merci beaucoup pour ces détails. J’ai pu tester les deux correctifs séparément (chacun sur une VM fraîchement restaurée) et ils ont tous deux fonctionné !

Notes :

  1. Pour le correctif temporaire, j’ai dû exécuter « ./launcher restart app » pour que les changements prennent effet.

  2. Il semble que « spec/models/optimized_image_spec.rb » contienne également une référence à FastImage.new(). Faut-il également mettre à jour celui-ci comme les autres ?

Merci encore pour votre aide !

Je suis content que ça fonctionne. :slight_smile:

  1. Ceci est juste à des fins de test, vous n’avez donc pas à vous en soucier.

Super ! Merci ! Maintenant que j’ai testé cela dans mon environnement de développement, je vais déployer cela dans les environnements de test et de production.

Au fait, si vous avez le temps, j’aimerais avoir votre avis sur un problème connexe (How to customize MIME media type emitted for certain attachments?).

1 « J'aime »

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.