BabelDOCがPDF処理をRustに移し、文書の読み込みに使うリソースに標準で上限を設けた新バージョンを公開した。外部から受け取ったPDFを翻訳するサービスは、小さな悪意あるファイルが大量のメモリを消費して処理を停止させるリスクを減らせる。

1. PDFの読み込みと書き出しをRustに移行

今回のバージョンではPDF解析だけでなく、スキャン文書の判別、文章と画像の配置、段落・スタイル処理、翻訳調整、ページ要素を印刷形式に並べる組版、PDFの書き出しまでRustで行う。レイアウトモデル、文書要素の位置を決めるモデル、翻訳サービス連携はPythonに残り、Pythonは引き続き利用の入り口を担う。

プロジェクトの発表によると、PDFの読み込みは0.6.4より高速になり、メモリ使用量も減った。標準の処理経路では文書出力を従来版と同じに保つことを目指しており、意図した違いは透かしの変更と新しいリソース上限だ。

2. 小さな入力がメモリを使い果たす経路を防ぐ

0.7.0は入力PDFの読み込み中にリソース消費が過度に増えるのを防ぐ上限を標準で有効にする。上限を超えると処理を止め、超えた制限の名前と値、発生箇所を含む例外を出す。制限は一括で無効にできるが、その場合は警告が記録される。

1件目のセキュリティ勧告は、圧縮されたPDFデータの展開時に出力サイズを制限せず、メモリを使い果たす問題だ。圧縮ファイルを開くと、小さな包みが巨大な荷物へ膨らむようなものだ。

勧告に掲載された再現記録によると、Python 3.11のスリムコンテナ上のBabelDOC 0.6.4は1,043,644バイトを1,073,741,824バイトに展開した。展開には2.70秒かかり、プロセスの最大メモリは1,068,544KiBから2,116,024KiBへ増えた。

再現の中核となるコードの一部は次のとおりだ。

stream = PDFStream(attrs={"Filter": LIT("FlateDecode")}, rawdata=payload)

mem_before = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
print(f"\nProcess RSS before decode(): {mem_before} KiB")

start = time.time()
stream.decode()  # <-- the real, unmodified vulnerable call: zlib.decompress(data), no max size
elapsed = time.time() - start

2件目の勧告は、フォントの文字コードを実際の文字に対応付けるToUnicodeマッピングの問題だ。文字コード範囲を1つずつ展開して保存することでリソースを消費した。BabelDOC 0.6.4で従来のPythonパーサーと後継のPythonパーサーに同じ入力を渡した再現記録は次のとおりだ。

表:ToUnicode範囲展開の再現結果

条件:BabelDOC 0.6.4、Docker内のPython、ネットワーク遮断、入力バイト列でパーサーを直接呼び出し(2026年10月16日の実行結果)

単位:時間は秒、項目数は個

小さな入力がメモリを使い果たす経路を防ぐ
パーサー・範囲の終端値時間項目数
従来のPythonパーサー・000000FF0.0004—
従来のPythonパーサー・000FFFFF0.41—
従来のPythonパーサー・00FFFFFF6.72—
後継のPythonパーサー・000000FF0.0256
後継のPythonパーサー・000FFFFF0.541048576
後継のPythonパーサー・00FFFFFF18.4416777216

表の範囲はすべて00000000から始まる。同じパーサーで範囲を広げると処理時間が増えた。別途、00000000~FFFFFFFFの範囲をメモリ1GiB制限下で展開した試験は終了コード137で強制終了した。新しいRustパーサーは範囲を個別項目ではなく区間として保存し、保持するマッピングデータにも標準で上限を適用する。

3. セキュリティ改善の代償は上限超過文書の処理失敗

両勧告とも脆弱なバージョンは0.6.4以下と記載され、修正は0.7.0に含まれた。2026年11月7日公開の今回のバージョンは、入力処理中にリソースが大幅に増えるリソース増幅を引き起こすPythonパーサー実装をRust実装に置き換えた。保守担当者は、セキュリティ修正は最新リリースのみに提供し、修正を旧版へ適用する公式のバックポートは行わないと述べた。

リソース上限は悪意ある入力による過剰なメモリ使用のリスクを下げる一方、正常な文書でも上限を超えれば翻訳を拒否する。サービス運用者は失敗理由を利用者に伝える手順を用意する必要がある。必ず処理しなければならない文書には、上限を無効にして外部の制限を適用する方法が案内された。

再現の数値はパーサーを直接呼び出した結果であり、翻訳全体や新しいRust実装の性能測定値ではない。保守担当者は、信頼できないPDFを処理する際には翻訳プロセスの時間制限とコンテナのメモリ制限も併用するよう勧告した。

4. ビルド済みパッケージを使い、自前の文書で結果を確認する

あらかじめ作成され、そのままインストールできるビルド済みパッケージは、Linuxのx86-64・ARM64、Windows x86-64、MacのIntel・Apple Siliconの5プラットフォームに提供される。対応するPythonバージョンは3.10~3.13だ。他のプラットフォームでソースコードとビルド設定をまとめたソース配布物をインストールする場合や、リポジトリから直接ビルドする場合は、コンパイルとビルドに必要なRustツールチェーンが必要だ。

公開エントリーポイントは維持されるが、削除された内部Pythonモジュールを直接読み込む外部コードは修正が必要だ。保守担当者は、本番環境をアップグレードする前に自前の文書で結果を確認するよう案内した。

最初のページに透かしは追加されなくなる。関連設定は引き続き受け付けるが、出力ファイルの名前と数だけを変えるため、成果物の確認時に併せて点検すべき変更だ。

0.7.0はPDFパーサーの危険なリソース増幅経路を制限する一方、一部の文書の処理が失敗する可能性があるため、アップグレードの要点は上限を超えた処理を運用でどう扱うか準備することにある。