mimemagicに依存しなくなったmarcelのmime type判定の変化には気をつけようって話

mimemagic gemのライセンス問題で色々ありましたね。ライセンスは法的な問題なので確認も難しい問題でした。

さて、marcelからmimemagicへの依存が無くなってmime typeの判定がApache Tikaを元にしたものになりました。

hackmd.io

しかし、この変更にはmarcelのmime type判定結果が変化するものが含まれていました。
例えば、以下のsample.xlsxというファイルのmime typeの判定をMarcel: 1.0.0、0,3,3それぞれに行わせると...

 ❯ xxd sample.xlsx                                                                                          
00000000: 504b 0304 1400 0808 0800 3123 7d52 0000  PK........1#}R..
00000010: 0000 0000 0000 0000 0000 1800 0000 786c  ..............xl
00000020: 2f64 7261 7769 6e67 732f 6472 6177 696e  /drawings/drawin
00000030: 6731 2e78 6d6c 9dd0 5d6e c230 0c07 f013  g1.xml..]n.0....
00000040: ec0e 55de 695a 1813 4314 5ed0 4e30 0ee0  ..U.iZ..C.^.N0..
00000050: 256e 1b91 8fca 0ea3 dc7e d14a 3669 7b01  %n.......~.J6i{.
00000060: 1e6d cb3f f9ef cd6e 74b6 f844 6213 7c23  .m.?...nt..Db.|#

❯ file -i sample.xlsx                                                                                
sample.xlsx: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=binary 
 irb(main):001:0> require 'marcel'
=> true
irb(main):002:0> Marcel::VERSION
=> "1.0.0"
irb(main):003:0> require 'pathname'
=> true
irb(main):004:0> Marcel::MimeType.for Pathname.new('sample.xlsx')
=> "application/zip"
 
 irb(main):001:0> require 'marcel'
=> true
irb(main):002:0> Marcel::VERSION
=> "0.3.3"
irb(main):003:0> require 'pathname'
=> true
irb(main):004:0> Marcel::MimeType.for Pathname.new('sample.xlsx')
=> "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
 

このように同じファイルに対して判定結果が異なっています。

この理由はmarcelのmimemagicをreplaceした差分でわかります。

github.com

marcelはlib/marcel/mime_type/definitions.rb内にて 'require 'mimemagic/overlay'していました。 mimemagic/overlayはxlsxやらpptやらをapplication/zipと区別してくれるMimeMagic.addをしていたファイルです。
最初に貼ったmimemagic最新動向の記事でも触れられていましたね。

この'require 'mimemagic/overlay'が無くなったことによってmarcelはxlsxやらpptをapplication/zipと判定するようになりました。*1

marcelのREADMEにはもともと

By preference, the magic number data in any passed in file is used to determine the type. If this doesn't work, it uses the type gleaned from the filename, extension, and finally the declared type. If no valid type is found in any of these, "application/octet-stream" is returned.

Some types aren't easily recognised solely by magic number data. For example Adobe Illustrator files have the same magic number as PDFs (and can usually even be viewed in PDF viewers!). For these types, Marcel uses both the magic number data and the file name to work out the type:

とあるので、書いてある通りファイル名を渡すのがとりあえずの対処策になるかと思います。

 irb(main):001:0> require 'marcel'
=> true
irb(main):002:0> Marcel::VERSION
=> "1.0.0"
irb(main):003:0> require 'pathname'
=> true
irb(main):004:0> Marcel::MimeType.for Pathname.new('sample.xlsx')
=> "application/zip"
irb(main):005:0> Marcel::MimeType.for Pathname.new('sample.xlsx'), name: 'sample.xlsx'
=> "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
 

shrineにはそういったオプションもありますね。

xlsxとかpptを扱うRailsアプリはそれなりに存在するでしょうから各位気をつけていきましょう。

追記

@furish さんがPR投げてくれてますね ある場でこの人と話した内容がこの記事の発端でもあります。 github.com

*1:一部のxlsxとかpptはmarcel 1.0.0でもそのままapplication/zipと区別してくれるかもしれません。マジックナンバーにあまり詳しくないのでわかりませんが、特定のマジックナンバーを持つxlsxとかpptも存在するかも