GrooveSeek

Semantic search over a Markdown knowledge base, served over MCP.

View the Project on GitHub alphabet-h/grooveseek

1. .xls (レガシー BIFF) サポートの取り下げ

背景と問題

.xls は v0.11.0 で .docx / .xlsx / .pptx と一緒に追加され、 calamine の reader を .xlsx と共有していた。 バイナリ parser のセキュリティ監査で、この 2 形式はメモリ特性を共有しておらず、 .xls 経路を正当化していたコメントが誤りであることが判明した。

.xlsx はストリームとして読まれる。.xls は違う: calamine::Xls::new() が workbook を eager に parse し、全シートを BTreeMap<String, SheetData> に保持した上で 各シートに Range::from_sparse を呼ぶ。これは値の入ったセルの bounding rectangle を求め、 vec![Data::default(); rows * cols] として密に確保する。

ソースは「BIFF がシートを 65,536 × 256 に制限しているから安全」と主張していた。 それが縛るのはシートであって workbook ではない。 実測:

項目
size_of::<calamine::Data>() 32 B
最大シート (65,536 × 256) 16,777,216 セル = 512 MB
worksheet_range() は clone を返す 読み出し中のシートはピーク 1 GB
workbook の上限 無し — (シート数) × 512 MB

対角 2 セル分のレコードだけでシートを最大矩形にできるので、数十 KB の細工ファイルで 十分な数のシートを宣言すればメモリを使い切れる。しかも割り当て失敗はエラーを返さず プロセスを異常終了させるため、他形式を守っている per-file skip も parser の panic guard も 構造的に効かない。監視ディレクトリに 1 ファイル置かれるだけでサーバが落ちうる。

決定を左右した要因

検討した選択肢

  1. calamine がファイルを開く前の pre-check で確保量を縛る。 CFB (OLE2) コンテナを自前で辿って BOUNDSHEET / DIMENSIONS レコードを読み、シート数と 宣言された範囲を得て、Xls::new() の実行前に過大な workbook を拒否する。
  2. 上限を受け入れて docs に明記する。
  3. upstream に borrow ベース / ストリーミングの BIFF API を入れてもらう。
  4. 形式を取り下げる。

決定

選択肢 4 (取り下げ) を採用する。新規依存を増やさずに穴を塞げる唯一の選択肢であり、 ユーザに生じるコストがファイル変換で済むから。

[parsers].enabled"xls" を書くと、起動時に理由付きで拒否される。 該当する workbook の推奨経路は .xlsx への変換。

他を採らなかった理由:

結果と代償

確認方法

参考