Discourse 误将上传的文件识别为图片

我的一些工程用户希望将具有不常见文件扩展名的数据文件附加到他们的帖子中。它们本质上是纯文本文件,但包含扩展的 ASCII 字符。

我曾尝试更新 Discourse 的 NGINX 配置以指定这些文件的 MIME 媒体类型,但没有成功。我两周前发布了一个主题(https://meta.discourse.org/t/how-to-customize-mime-media-type-emitted-for-certain-attachments/282111)但至今未收到任何答复。即使不更新 NGINX,它仍将使用“application/octet-stream”的备用 MIME 类型来提供未知文件类型。我现在可以接受这一点。

但是,当用户尝试将这些数据文件上传到帖子中时(无论是使用“上传”按钮还是拖放),他们会收到一个来自 Discourse 的错误弹出窗口,如下所示:

看起来当用户上传文件时,Discourse 会尝试变得智能并检测它是图像还是其他文件类型。此外,它似乎通过查看文件内容(很像标准的 Unix 命令“file”)来做出此决定。我假设这是为了让 Discourse 决定是将其内联到帖子内容中还是将其作为附件放在旁边。

对于这些数据文件,此检查错误地将文件识别为图像。只是为了好玩,我在 Ubuntu 机器上放了一些这些数据文件,并使用“file”命令进行了检查,果然,它们被识别为“JPEG image data”。

有没有办法让用户在不让 Discourse 尝试检测它们是否为图像的情况下上传文件?即,“请将此作为附件上传,无论如何,不要内联”?

或者,我可以配置 Discourse 以允许 zip 文件,并告诉用户在上传文件之前将它们压缩,但我宁愿不向站点开放随机 zip 文件上传。这似乎是一个安全问题。

感谢您的帮助!

另一种解决方法是为那些奇怪的文件采用一个扩展名,比如 .bin.data 或者干脆 .anythinggoes,这应该足以让 Discourse 不去管这些文件。

感谢您的快速回复! 但是,我尝试了您的方法,但它不起作用。 Discourse 绝对是根据文件内容而不是扩展名来确定它是否为图像。

1 个赞

有人对此有什么看法吗?这似乎是一个相当严重的错误。

我对此问题进行了一些研究。
简而言之,您的文件被检测为 JPEG,因为它以与此类文件相同的签名开头。
在 Discourse 中修复此行为是可能的,但这需要进行修改。(见文末)


这里有一些技术细节。
此问题始于:

FastImage 库打开文件并确定类型和大小。
正如您所料,它返回 JPEG 类型。

如果您查看 JPEG 签名,它看起来像这样:

JPEG 标记

更多信息:List of file signatures - Wikipedia
JPEG - Wikipedia

它总是以以下标记字节开头:FF D8

使用十六进制编辑器查看您的文件样本,您会发现它以相同的方式开头。

现在,如果您查看 FastImage 如何检测 JPEG,您可以在此处看到:

但是,您无法提取任何图像信息,因为缺少所有必需的字节。

如何在 Discourse 中修复此问题?
查看 FastImage 代码,您可以传递一个有用的选项。

使用此选项,任何错误(SizeNotFoundImageFetchFailureCannotParseImageUnknownImageTypeBadImageURI)都将返回无图像信息;并且您的文件不会被检测为图像。

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

然后,它现在可以工作了:

我稍后可以做一个 PR。使用此选项在这里是有意义的。:+1:

2 个赞

哇!这是一个非常出色的分析!谢谢!

一些快速的问题:

  1. 那么,通过这些更改,该文件将不会被识别为图像,而是会像非图像一样上传并显示在帖子的右侧吗?

  2. 如果我理解正确,您建议我在本地的 Discourse 实例中进行这些调整来尝试一下,或者在未来的 Discourse 版本中包含此功能之前使用它。但我该怎么做呢?(我是一名经验丰富的软件开发人员,但在 Docker 方面经验有限,在 Ruby 方面则完全没有经验。)

  3. 需要调整的 FastImage 调用是在 models/upload.rb 中,对吗?

  1. 是的,没错——就像我上面的截图一样

  2. 我并不是建议您进行修改。但是,如果您等不及,当然可以测试该更改。

  • 临时更改(重建后消失):
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
  • 持久更改(重建后保留):
cd /var/discourse
nano containers/app.yml  (使用您喜欢的编辑器)

在末尾(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)"

然后,重建:

./launcher rebuild app
  1. 我猜是的,如果您计划上传没有扩展名的文件。我没有检查其他地方是否需要相同的更改。
1 个赞

@Arkshine – 非常感谢您提供这些详细信息。我能够分别测试这两个修复程序(每个修复程序都在一个已恢复的 VM 上进行测试),它们都奏效了!

备注:

  1. 对于临时修复,我需要运行“./launcher restart app”才能使更改生效。

  2. 看起来“spec/models/optimized_image_spec.rb”也引用了 FastImage.new()。这个也需要像其他的一样更新吗?

再次感谢您的帮助!

很高兴它能正常工作。 :slight_smile:

  1. 这只是为了测试目的,所以您不必担心。

太好了!谢谢!既然我已经在我开发的で环境(dev env)中测试过了,接下来我会将其部署到测试和生产环境(test and prod envs)。

顺便说一句,如果你有时间,我很想听听你对一个相关问题的看法(https://meta.discourse.org/t/how-to-customize-mime-media-type-emitted-for-certain-attachments/282111)。

1 个赞

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