GrooveSeek

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

View the Project on GitHub alphabet-h/grooveseek

1. Withdraw .xls (legacy BIFF) support

Context and Problem Statement

.xls was added alongside .docx / .xlsx / .pptx in v0.11.0, sharing the calamine reader with .xlsx. A security audit of the binary parsers found that the two formats do not share a memory profile, and that the comment justifying the .xls path was wrong.

.xlsx is read as a stream. .xls is not: calamine::Xls::new() parses the whole workbook eagerly, holding every sheet in a BTreeMap<String, SheetData> and calling Range::from_sparse for each, which takes the bounding rectangle of the populated cells and allocates it densely as vec![Data::default(); rows * cols].

The source claimed this was safe because BIFF caps a sheet at 65,536 × 256. That bounds a sheet, not a workbook. Measured:

Quantity Value
size_of::<calamine::Data>() 32 B
Maximal sheet (65,536 × 256) 16,777,216 cells = 512 MB
worksheet_range() returns a clone 1 GB peak per sheet in flight
Workbook limit none — (sheet count) × 512 MB

Two cell records at opposite corners are enough to make a sheet maximal, so a crafted file of a few tens of kilobytes can declare enough sheets to exhaust memory. An allocation failure aborts the process rather than returning an error, so neither the per-file skip nor the parser panic guard — both of which already protect the other formats — can contain it. A single file in a watched directory could take down the server.

Decision Drivers

Considered Options

  1. Bound the allocation with a pre-check before calamine opens the file. Walk the CFB (OLE2) container ourselves and read the BOUNDSHEET and DIMENSIONS records to learn the sheet count and declared extents, then refuse oversized workbooks before Xls::new() runs.
  2. Accept the ceiling and document it.
  3. Get a borrow-based or streaming BIFF API upstream.
  4. Withdraw the format.

Decision Outcome

Chosen option: 4 — withdraw the format, because it is the only option that closes the hole without new dependencies, and the cost to users is a file conversion.

Listing "xls" in [parsers].enabled is now rejected at startup with the reason. The recommended path for affected workbooks is conversion to .xlsx.

Why the others were not chosen:

Consequences

Confirmation

More Information