WMV/WMAのMIMEタイプ誤検出をUnicode文字列対応で修正

rails/marcel

WMVおよびWMAファイルのマジックバイト検出ロジックをUTF-16エンコーディングに対応させ、PDFやMP3がWMV/WMAとして誤判定されるバグを修正しました。

背景

WMVおよびWMAのマジックバイト検出がASCII文字列パターンを使用していたため、PDFファイルやMP3ファイルが誤ってvideo/x-ms-wmvaudio/x-ms-wmaと判定されるケースが存在していました。

具体的には、#77で報告されたPDFファイル(内部にASCIIとしてwmv2の文字列を含む)がvideo/x-ms-wmvと誤識別される問題と、#125で報告されたLAMEやffmpegでトランスコードされたMP3ファイル(audio2.mp3)がvideo/x-ms-wmvとして誤識別される問題が存在していました。WMVやWMAのメタデータ文字列(Windows Media Videowmv2Windows Media Audioなど)は、実際のASF/WMV/WMAコンテナフォーマットではUTF-16LE(Little Endian)エンコーディングで格納されています。ASCII文字列として検索していた旧来のパターンは、このエンコーディングを考慮していませんでした。

その結果、WMV/WMAとは無関係なファイルがたまたまASCII範囲にこれらの文字列を含む場合に誤検出が発生し、反対に正規のWMV/WMAファイルがUTF-16LEで格納された文字列を検出できないケースも生じていました。

技術的な変更

lib/marcel/tables.rbのマジックバイト定義と、テーブル生成スクリプトscript/generate_tables.rbの2箇所が変更されました。

script/generate_tables.rbでは、unicodeLEおよびunicodeBE型への対応が追加されました。これはshared-mime-infoのマジックデータベースが定義するUnicode文字列型で、従来のstring型と同様の前処理(16進数エスケープの展開)を行った後、Ruby標準のencodeメソッドでUTF-16LE/BEにエンコードし、さらにforce_encoding(Encoding::BINARY)でバイナリ列として扱います。

    when 'unicodeLE', 'unicodeBE' # Unicode string types (UTF-16 Little/Big Endian)
      value.gsub!(/\A0x([0-9a-f]+)\z/i) { [$1].pack('H*') }
      encoding = type == 'unicodeLE' ? Encoding::UTF_16LE : Encoding::UTF_16BE
      value = value.encode(encoding).force_encoding(Encoding::BINARY)

lib/marcel/tables.rbでは、video/x-ms-wmvaudio/x-ms-wmaのマッチパターンが更新されています。変更前はASCII文字列としてそのまま記述されていましたが、変更後はUTF-16LEのバイト列(各文字の後にヌルバイト\000が続く形式)に置き換えられています。

変更前:

['video/x-ms-wmv', [[0..8192, b['Windows Media Video']], [0..8192, b['VC-1 Advanced Profile']], [0..8192, b['wmv2']]]],
['audio/x-ms-wma', [[0..8192, b['Windows Media Audio']]]],

変更後:

['video/x-ms-wmv', [[0..8192, b["W\000i\000n\000d\000o\000w\000s\000 \000M\000e\000d\000i\000a\000 \000V\000i\000d\000e\000o\000"]], [0..8192, b["V\000C\000-\0001\000 \000A\000d\000v\000a\000n\000c\000e\000d\000 \000P\000r\000o\000f\000i\000l\000e\000"]], [0..8192, b["w\000m\000v\0002\000"]]]],
['audio/x-ms-wma', [[0..8192, b["W\000i\000n\000d\000o\000w\000s\000 \000M\000e\000d\000i\000a\000 \000A\000u\000d\000i\000o\000"]]]],

テストフィクスチャとしてtest/fixtures/magic/video/x-ms-wmv/sample.wmvtest/fixtures/magic/audio/x-ms-wma/sample.wmatest/fixtures/magic/audio/mpeg/audio2.mp3、および問題を再現するPDFファイルtest/fixtures/name/application/pdf/wmv.pdfが追加されています。

設計判断

テーブルの事前生成(pre-generation)方式を採用していることが、この変更のポイントです。tables.rbには実行時にエンコード処理を行うコードではなく、生成済みのバイト列が直接書き込まれています。script/generate_tables.rbでUnicode変換ロジックを実装し、その出力結果をコミットする構成になっているため、実行時のオーバーヘッドが発生せず、マッチング処理はバイナリ比較のみで完結します。

また、ASCII文字列パターンからUTF-16LEバイト列への変更は、ASFコンテナフォーマットの仕様に沿った修正です。ASF(Advanced Systems Format)ではメタデータ文字列がUTF-16LEで格納されることが規定されており、今回の変更はその仕様に準拠したものといえます。これにより、WMV/WMAとは無関係なファイルがこれらのMIMEタイプに誤判定されるリスクが大幅に低減されます。

まとめ

ASCII文字列パターンをASFフォーマット仕様に準拠したUTF-16LEバイト列に修正したことで、WMV/WMAの誤検出問題が根本から解消されます。テーブル生成スクリプトレベルでUnicode型を処理する仕組みを導入したことで、今後同様のUnicode文字列マッチングが必要になった場合にも同じアプローチを適用できる基盤が整いました。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
1f0ab332

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→背景・技術詳細・設計判断(各論)→まとめ(結論)という理想的な3部構成が明確に適用されています。各セクションの役割が明確で、非常に分かりやすい構成です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```ruby:filepath)とGitHubのIssue/PRリンク記法([#123](URL))が、ガイドライン通りに正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

MIMEタイプ、マジックバイト、文字エンコーディングといった専門用語を前提として使用しており、専門知識を持つエンジニアという対象読者に適合した内容になっています。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論で構成され、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が徹底されています。これにより、非常に高い可読性が実現されています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されている`script/generate_tables.rb`と`lib/marcel/tables.rb`のコードは、提供されたDiffの内容と完全に一致しており、正確に引用されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

UTF-16LE, マジックバイト, ASFコンテナフォーマット, 事前生成(pre-generation)など、PRの変更内容を解説する上で適切かつ正確な技術用語が選択されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

ASCII文字列検索が誤検出の原因であること、修正がUTF-16LEバイト列への置換であることなど、技術的な説明はすべてDiffと論理的に整合性が取れており、正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内の主張はすべてPRのDescription、Diff、または技術的に自明な設計(テーブルの事前生成)から裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#138)、Issue番号(#77, #125)など、記事に含まれる数値や固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

PRタイトル「add unicode string support」の内容を、「WMV/WMAのMIMEタイプ誤検出をUnicode文字列対応で修正」という具体的で分かりやすい記事タイトルに昇華させており、内容との整合性も完璧です。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

「ASFコンテナフォーマットではメタデータがUTF-16LEで格納される」というPRにない外部知識が補足されていますが、これは変更の技術的根拠を説明する上で不可欠かつ正確な情報であり、記事の品質を高める適切な補足です。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

記事には時間表現に関する記述がほとんどなく、PRの情報との間で時間的な歪曲は発生していません。