Discourse detectando incorretamente arquivo enviado como imagem

Tenho alguns usuários de engenharia que desejam anexar arquivos de dados com extensões de arquivo incomuns às suas postagens. Essencialmente, são arquivos de texto simples, mas incluem caracteres ASCII estendidos.

Tentei atualizar a configuração NGINX do Discourse para especificar tipos de mídia MIME para esses arquivos, mas não funcionou. Postei um tópico (How to customize MIME media type emitted for certain attachments?) sobre isso há duas semanas, mas ainda não recebi respostas. Mesmo que o NGINX não seja atualizado, ele ainda servirá tipos de arquivo desconhecidos usando o tipo MIME de fallback “application/octet-stream”. Posso conviver com isso por enquanto.

No entanto, quando os usuários tentam carregar esses arquivos de dados em uma postagem (usando o botão “Upload” ou arrastando e soltando), eles recebem um pop-up de erro do Discourse como este:

Parece que, quando os usuários carregam arquivos, o Discourse está tentando ser inteligente e detectar se é uma imagem ou algum outro tipo de arquivo. Além disso, parece estar fazendo essa determinação olhando o conteúdo do arquivo (muito parecido com o comando Unix padrão “file”). Presumo que seja para que o Discourse possa decidir se o incorpora ao conteúdo da postagem ou o coloca de lado como um anexo.

No caso desses arquivos de dados, essa verificação está identificando incorretamente os arquivos como imagens. Só por diversão, coloquei alguns desses arquivos de dados em uma caixa Ubuntu e verifiquei-os com o comando “file”, e, com certeza, eles foram identificados como “dados de imagem JPEG”.

Existe alguma maneira de os usuários carregarem arquivos sem que o Discourse tente detectar se são imagens? Ou seja, “Por favor, carregue isso como um anexo, não importa o quê, não o incorpore”?

Alternativamente, eu poderia configurar o Discourse para permitir arquivos zip e dizer aos usuários para compactarem seus arquivos antes de carregá-los, mas eu preferiria não abrir o site para uploads aleatórios de arquivos zip. Isso parece uma questão de segurança.

Obrigado antecipadamente por qualquer ajuda!

Outra solução alternativa é adotar uma extensão para esses arquivos estranhos, como .bin, .data ou realmente .anythinggoes, o que deve ser suficiente para o Discourse manter esses arquivos em paz.

Obrigado pela resposta rápida! No entanto, tentei isso e não funcionou. O Discourse está definitivamente olhando o conteúdo do arquivo, e não a extensão, para determinar se é uma imagem ou não.

1 curtida

Alguém tem alguma opinião sobre isso? Isso parece um bug bem significativo.

Investiguei esse problema.

Resumindo, seu arquivo está sendo detectado como JPEG porque começa com a mesma assinatura deste tipo de arquivo.
Corrigir esse comportamento no Discourse é possível, mas requer uma modificação. (veja no final)


Aqui algumas coisas técnicas.
Este problema começa aqui:

A biblioteca FastImage abre o arquivo e determina o tipo e o tamanho.
Como esperado, ela retorna um tipo JPEG.

Se você olhar a assinatura JPEG, ela se parece com isto:

Marcadores JPEG

Mais informações: List of file signatures - Wikipedia
JPEG - Wikipedia

Sempre começa com os seguintes bytes de marcador: FF D8

Olhando para sua amostra de arquivo com um editor hexadecimal, você verá que começa da mesma forma.

Agora, se você olhar como o FastImage detecta um JPEG, você pode ver aqui:

No entanto, você não pode extrair nenhuma informação da imagem, pois nem todos os bytes necessários estão presentes.

Como corrigimos esse problema no Discourse?
Olhando o código do FastImage, há uma opção valiosa que você pode passar.

Usando essa opção, qualquer erro (SizeNotFound, ImageFetchFailure, CannotParseImage, UnknownImageType, BadImageURI) não retornará nenhuma informação de imagem; e seu arquivo não será detectado como uma imagem.

@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}")

Então, pode funcionar agora:

Posso fazer um PR mais tarde. Usar essa opção faz sentido aqui. :+1:

2 curtidas

Uau! Esta é uma análise fenomenal! Obrigado!

Algumas perguntas rápidas:

  1. Então, com essas alterações, o arquivo não será detectado como uma imagem e será carregado como não imagem, sendo exibido à direita da postagem?

  2. Se entendi corretamente, você está sugerindo que eu faça esses ajustes na minha instância local do Discourse para experimentar e/ou usar isso até que seja incluído em uma futura versão do Discourse. Mas como faço isso? (Sou um desenvolvedor de software experiente, mas tenho experiência limitada com Docker e nenhuma com Ruby.)

  3. A chamada FastImage que precisaria ser ajustada está em models/upload.rb, certo?

  1. Sim, está correto – como na minha captura de tela acima

  2. Não estou sugerindo que você faça a modificação. No entanto, se você não puder esperar, certamente poderá testar essa alteração.

  • Para uma alteração temporária (desaparece após a reconstrução):
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
  • Para uma alteração persistente (permanece após a reconstrução):
cd /var/discourse
nano containers/app.yml  (use seu editor preferido)

Adicione os seguintes comandos personalizados no final (seção 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)"

Em seguida, reconstrua:

./launcher rebuild app
  1. Acho que sim, se você planeja carregar arquivos sem extensões. Não verifiquei se outras ocorrências exigiam a mesma alteração.
1 curtida

@Arkshine – Muito obrigado por esses detalhes. Consegui testar ambas as correções separadamente (cada uma em uma VM recém-restaurada) e ambas funcionaram!

Observações:

  1. Para a correção temporária, precisei executar “./launcher restart app” para que as alterações tivessem efeito.

  2. Parece que “spec/models/optimized_image_spec.rb” também tem uma referência a FastImage.new(). Isso também precisa ser atualizado como o resto?

Obrigado novamente pela sua ajuda!

Fico feliz que funcione. :slight_smile:

  1. Isso é apenas para fins de teste, então você não precisa se preocupar com isso.

Ótimo! Obrigado! Agora que testei isso no meu ambiente de desenvolvimento, vou implantar nos ambientes de teste e produção.

A propósito, se tiver tempo, adoraria saber sua opinião sobre um problema relacionado (How to customize MIME media type emitted for certain attachments?).

1 curtida

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