-
Notifications
You must be signed in to change notification settings - Fork 999
feat(sheet): read every Excel container in-house and drop calamine #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
34 commits
Select commit
Hold shift + click to select a range
874818b
feat(sheet): read xlsx and xlsm in-house with number format support
tomsideguide 0972fc0
feat(sheet): read ole .xls in-house instead of calamine
tomsideguide cc0f4ba
feat(sheet): read xlsb in-house, narrowing calamine to legacy xls
tomsideguide d466d45
chore(sheet): drop calamine now every workbook container is read in-h…
tomsideguide 290537f
fix(sheet): match am/pm format tokens over chars, not bytes
tomsideguide 6369125
fix(sheet): cap the grid a sparse sheet extent can materialize
tomsideguide 4c09b4e
refactor(sheet): share the rk decoder and error table between the bin…
tomsideguide 7ddc5c4
test(fuzz): reach the binary readers and the format engine with wrapp…
tomsideguide 5c933fc
fix(sheet): keep the literals decorating a general format, and drop a…
tomsideguide 41ab351
fix(sheet): resolve a workbook part that is not at its conventional path
tomsideguide 6fbed6c
fix(xls): warn when the workbook stream ends mid-record
tomsideguide 2ab6526
test(fuzz): let xlsb mutation reach the cell, style and string readers
tomsideguide 667b262
docs(limits): trim the grid slot cap comment to its statement
tomsideguide fd5f3cb
docs(sheet): trim the reader header to what it does
tomsideguide bcc40d4
fix(sheet): keep the fictitious 1900 leap day distinct from the day b…
tomsideguide a22d2b0
fix(sheet): pick the workbook reader from the part's bytes and propag…
tomsideguide 13d2db8
test(fuzz): write the second worksheet part the xlsb package declares
tomsideguide 67422e3
fix(sheet): read a byte order mark as xml and test the leap day befor…
tomsideguide 110cfde
fix(sheet): fall back to general on an empty format code, and honour …
tomsideguide baa517e
test(fuzz): seed the am/pm token path and keep whitespace in format c…
tomsideguide f3c6cd8
fix(sheet): resolve the main part before conventional paths and requi…
tomsideguide 9729235
fix(sheet): render only the date and time parts a format names
tomsideguide 3ab3314
fix(sheet): budget grid materialization across the workbook, not per …
tomsideguide 63375b8
fix(sheet): require a workbook from the conventional binary part too
tomsideguide f02155c
fix(sheet): record an elapsed bracket as a time run
tomsideguide afad634
fix(sheet): find the closest fraction by continued fractions, and dro…
tomsideguide 4fd9384
fix(sheet): open the package once and classify the main part from its…
tomsideguide 1a611b8
fix(sheet): keep the decimal point when no placeholder follows it
tomsideguide 1da773b
fix(sheet): keep the clock a combined format names on a sub-day serial
tomsideguide 30f5167
fix(sheet): saturate the fraction bound past nineteen denominator places
tomsideguide 7c4e4a5
fix(sheet): walk the continued fraction of the float's exact ratio
tomsideguide 5137971
fix(sheet): an elapsed format is a duration, never a date
tomsideguide 3a44210
refactor(sheet): borrow a general section's decorations instead of cl…
tomsideguide 3e66203
test(fuzz): make the xlsb seed's second sheet visible so its part is …
tomsideguide File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Fuzzing | ||
|
|
||
| cargo +nightly fuzz run xlsx fuzz/corpus/xlsx fuzz/seeds/xlsx | ||
|
|
||
| `fuzz/seeds/` holds checked-in starting inputs; `fuzz/corpus/` is the working | ||
| directory libfuzzer writes to and is not checked in. | ||
|
|
||
| `xls`, `xlsb` and `numfmt` wrap their input in a valid container (an OLE | ||
| compound file, an OPC package, a styles part) so mutation reaches the record | ||
| and format-code parsers rather than dying at the container gate. `xlsx` takes | ||
| a whole workbook, and its seeds cover all three containers because the | ||
| frontend picks the reader from the bytes. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| #![no_main] | ||
|
|
||
| use libfuzzer_sys::fuzz_target; | ||
| use std::io::{Cursor, Write}; | ||
|
|
||
| // A number format code, carried into the engine on a minimal workbook. The | ||
| // grammar is where the parsing complexity lives, and reaching it through a | ||
| // discovered styles part would cost the fuzzer most of its budget. Only the | ||
| // control characters XML forbids are dropped, so whitespace a code may carry | ||
| // still reaches the parser. | ||
| fuzz_target!(|data: &[u8]| { | ||
| let code = String::from_utf8_lossy(data); | ||
| let escaped: String = code | ||
| .chars() | ||
| .filter(|c| !c.is_control() || matches!(c, '\t' | '\n' | '\r')) | ||
| .map(|c| match c { | ||
| '&' => "&".to_string(), | ||
| '<' => "<".to_string(), | ||
| '>' => ">".to_string(), | ||
| '"' => """.to_string(), | ||
| c => c.to_string(), | ||
| }) | ||
| .collect(); | ||
| let styles = format!( | ||
| r#"<?xml version="1.0"?><styleSheet xmlns="{SML}"><numFmts count="1"><numFmt numFmtId="164" formatCode="{escaped}"/></numFmts><cellXfs count="2"><xf numFmtId="0"/><xf numFmtId="164" applyNumberFormat="1"/></cellXfs></styleSheet>"# | ||
| ); | ||
|
|
||
| let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new())); | ||
| let opts = zip::write::SimpleFileOptions::default(); | ||
| let parts: [(&str, &str); 6] = [ | ||
| ("[Content_Types].xml", CONTENT_TYPES), | ||
| ("_rels/.rels", RELS), | ||
| ("xl/workbook.xml", WORKBOOK), | ||
| ("xl/_rels/workbook.xml.rels", WORKBOOK_RELS), | ||
| ("xl/styles.xml", &styles), | ||
| ("xl/worksheets/sheet1.xml", SHEET), | ||
| ]; | ||
| for (name, body) in parts { | ||
| if zip.start_file(name, opts).is_err() || zip.write_all(body.as_bytes()).is_err() { | ||
| return; | ||
| } | ||
| } | ||
| let Ok(bytes) = zip.finish() else { | ||
| return; | ||
| }; | ||
| let _ = anydoc::to_markdown_bytes(bytes.into_inner().as_slice(), anydoc::Format::Excel); | ||
| }); | ||
|
|
||
| const SML: &str = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; | ||
| const CONTENT_TYPES: &str = r#"<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/></Types>"#; | ||
| const RELS: &str = r#"<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>"#; | ||
| const WORKBOOK: &str = r#"<?xml version="1.0"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="S" sheetId="1" r:id="rId1"/></sheets></workbook>"#; | ||
| const WORKBOOK_RELS: &str = r#"<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>"#; | ||
| const SHEET: &str = r#"<?xml version="1.0"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData><row r="1"><c r="A1" s="1"><v>1234.5</v></c><c r="B1" s="1"><v>0.075</v></c><c r="C1" s="1" t="inlineStr"><is><t>text</t></is></c></row></sheetData></worksheet>"#; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| #![no_main] | ||
|
|
||
| use libfuzzer_sys::fuzz_target; | ||
| use std::io::{Cursor, Write}; | ||
|
|
||
| // The BIFF record stream, wrapped in a valid OLE container so mutation | ||
| // reaches the reader instead of dying at the container gate. | ||
| fuzz_target!(|data: &[u8]| { | ||
| let Ok(mut ole) = cfb::CompoundFile::create(Cursor::new(Vec::new())) else { | ||
| return; | ||
| }; | ||
| match ole.create_stream("Workbook") { | ||
| Ok(mut stream) => { | ||
| if stream.write_all(data).is_err() { | ||
| return; | ||
| } | ||
| } | ||
| Err(_) => return, | ||
| } | ||
| let bytes = ole.into_inner().into_inner(); | ||
| let _ = anydoc::to_markdown_bytes(&bytes, anydoc::Format::Excel); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #![no_main] | ||
|
|
||
| use libfuzzer_sys::fuzz_target; | ||
| use std::io::{Cursor, Write}; | ||
|
|
||
| // The worksheet, styles and shared-string parts, wrapped in a package whose | ||
| // workbook part and relationships are already valid. Fuzzing the workbook | ||
| // part instead would strand the input there: without a resolvable worksheet | ||
| // relationship every sheet is skipped, and the cell, format and string | ||
| // readers never run. The workbook part is parsed by the same record reader | ||
| // this does reach. | ||
| fuzz_target!(|data: &[u8]| { | ||
| let mut zip = zip::ZipWriter::new(Cursor::new(Vec::new())); | ||
| let opts = zip::write::SimpleFileOptions::default(); | ||
| let parts: [(&str, &[u8]); 8] = [ | ||
| ("[Content_Types].xml", CONTENT_TYPES.as_bytes()), | ||
| ("_rels/.rels", RELS.as_bytes()), | ||
| ("xl/workbook.bin", WORKBOOK), | ||
| ("xl/_rels/workbook.bin.rels", WORKBOOK_RELS.as_bytes()), | ||
| ("xl/worksheets/sheet1.bin", data), | ||
| ("xl/worksheets/sheet2.bin", data), | ||
| ("xl/styles.bin", data), | ||
| ("xl/sharedStrings.bin", data), | ||
| ]; | ||
| for (name, body) in parts { | ||
| if zip.start_file(name, opts).is_err() || zip.write_all(body).is_err() { | ||
| return; | ||
| } | ||
| } | ||
| let Ok(bytes) = zip.finish() else { | ||
| return; | ||
| }; | ||
| let _ = anydoc::to_markdown_bytes(bytes.into_inner().as_slice(), anydoc::Format::Excel); | ||
| }); | ||
|
|
||
| const WORKBOOK: &[u8] = include_bytes!("../seeds/xlsb/workbook-bin"); | ||
|
|
||
| const CONTENT_TYPES: &str = r#"<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="bin" ContentType="application/vnd.ms-excel.sheet.binary.macroEnabled.main"/></Types>"#; | ||
| const RELS: &str = r#"<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.bin"/></Relationships>"#; | ||
| const WORKBOOK_RELS: &str = r#"<?xml version="1.0"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.bin"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.bin"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.bin"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.bin"/></Relationships>"#; | ||
|
tomsideguide marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| aaaaé | ||
|
tomsideguide marked this conversation as resolved.
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| h:mm AM/PMé |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| [>100]0.0;[<=100]0.00 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| "$"#,##0.00 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| yyyy-mm-dd hh:mm:ss AM/PM |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| [h]:mm:ss |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| #,##0.00;[Red](#,##0.00);"nil";@ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| # ??/?? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 0.0% |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 0.00E+00 |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.