diff --git a/src/xlsb/mod.rs b/src/xlsb/mod.rs index d5d93a59..cab7ab54 100644 --- a/src/xlsb/mod.rs +++ b/src/xlsb/mod.rs @@ -340,7 +340,11 @@ impl Xlsb { } } 0x0090 => break, // BrtEndBundleShs - _ => (), + _ => { + // Consume the body of any unmatched workbook-global record + // to avoid reading its trailing bytes in the next read. + let _ = iter.fill_buffer(&mut buf)?; + } } buf.clear(); } diff --git a/tests/issue_666_lost_sheets.xlsb b/tests/issue_666_lost_sheets.xlsb new file mode 100644 index 00000000..5ce3891c Binary files /dev/null and b/tests/issue_666_lost_sheets.xlsb differ diff --git a/tests/issue_666_panic.xlsb b/tests/issue_666_panic.xlsb new file mode 100644 index 00000000..486242ba Binary files /dev/null and b/tests/issue_666_panic.xlsb differ diff --git a/tests/test.rs b/tests/test.rs index 2e3b1dba..abdb83e6 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -3550,6 +3550,27 @@ fn test_whitespace_trim_shared_strings() { ); } +// An unmatched workbook-global record whose body is left unconsumed desyncs the +// BIFF12 record stream: the next `read_type()` misreads the record's size-varint +// bytes as the next record id. Both fixtures hold a single "Sheet1" worksheet +// preceded by such a record; without consuming its body the sheet is either lost +// or the reader panics. See issue #666. +#[test] +fn test_xlsb_unmatched_workbook_record_does_not_lose_sheet() { + // The unmatched record's body size varint starts with `0x90`, which the + // desynced reader misreads as `BrtEndBundleShs` and stops before any sheet. + let xlsb: Xlsb<_> = wb("issue_666_lost_sheets.xlsb"); + assert_eq!(xlsb.sheet_names(), vec!["Sheet1".to_string()]); +} + +#[test] +fn test_xlsb_unmatched_workbook_record_does_not_panic() { + // The unmatched record's body size varint starts with `0x9C`, which the + // desynced reader misreads as `BrtBundleSh` and decodes from an empty buffer. + let xlsb: Xlsb<_> = wb("issue_666_panic.xlsb"); + assert_eq!(xlsb.sheet_names(), vec!["Sheet1".to_string()]); +} + #[test] fn too_small_xls() { let path = test_path("too_small.xls");