From e8e4b0ba48a5458684df1182c329d2b5ec6abd49 Mon Sep 17 00:00:00 2001
From: c <c@farsight.net>
Date: Tue, 12 Nov 2024 11:23:21 +0100
Subject: [PATCH 1/3] test: add xls with malformed format

---
 tests/malformed_format.xls | Bin 0 -> 15872 bytes
 tests/test.rs              |   5 +++++
 2 files changed, 5 insertions(+)
 create mode 100644 tests/malformed_format.xls

diff --git a/tests/malformed_format.xls b/tests/malformed_format.xls
new file mode 100644
index 0000000000000000000000000000000000000000..ee87abd077e57cbb8e982052e0ba10d7eee52d6f
GIT binary patch
literal 15872
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

literal 0
HcmV?d00001

diff --git a/tests/test.rs b/tests/test.rs
index 4faf6b7..bad9c3c 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -2138,3 +2138,8 @@ fn test_string_ref() {
     // second sheet is the same with a cell reference to the first sheet
     range_eq!(xlsx.worksheet_range_at(1).unwrap().unwrap(), expected_range);
 }
+
+#[test]
+fn test_malformed_format() {
+    let _xls: Xls<_> = wb("malformed_format.xls");
+}

From 3ee78831dd8338d17b64b74968c8dc91b3208394 Mon Sep 17 00:00:00 2001
From: c <c@farsight.net>
Date: Tue, 12 Nov 2024 11:26:05 +0100
Subject: [PATCH 2/3] fix: expand length check to cover index 4 into slice,
 fixing panic with malformed formats

---
 src/xls.rs | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/xls.rs b/src/xls.rs
index a04fcbe..afa6069 100644
--- a/src/xls.rs
+++ b/src/xls.rs
@@ -910,17 +910,19 @@ fn parse_xf(r: &Record<'_>) -> Result<u16, XlsError> {
 /// Decode Format
 ///
 /// See: https://learn.microsoft.com/ru-ru/openspecs/office_file_formats/ms-xls/300280fd-e4fe-4675-a924-4d383af48d3b
+/// 2.4.126
 fn parse_format(r: &mut Record<'_>, encoding: &XlsEncoding) -> Result<(u16, CellFormat), XlsError> {
-    if r.data.len() < 4 {
+    if r.data.len() < 5 {
         return Err(XlsError::Len {
             typ: "format",
-            expected: 4,
+            expected: 5,
             found: r.data.len(),
         });
     }
 
     let idx = read_u16(r.data);
 
+    // TODO: check if this can be replaced with parse_string()
     let cch = read_u16(&r.data[2..]) as usize;
     let high_byte = r.data[4] & 0x1 != 0;
     r.data = &r.data[5..];

From b5a8bf591b254c3c00c047b4b8699d95cc8278b4 Mon Sep 17 00:00:00 2001
From: c <c@farsight.net>
Date: Tue, 12 Nov 2024 11:29:47 +0100
Subject: [PATCH 3/3] fix: skip malformed formats

---
 src/xls.rs | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/xls.rs b/src/xls.rs
index afa6069..47e080a 100644
--- a/src/xls.rs
+++ b/src/xls.rs
@@ -338,9 +338,11 @@ impl<RS: Read + Seek> Xls<RS> {
                             self.is_1904 = true
                         }
                     }
-                    // FORMATTING
+                    // 2.4.126 FORMATTING
                     0x041E => {
-                        let (idx, format) = parse_format(&mut r, &encoding)?;
+                        let Ok((idx, format)) = parse_format(&mut r, &encoding) else {
+                            continue;
+                        };
                         formats.insert(idx, format);
                     }
                     // XFS