Skip to content

test(security): cargo-fuzz targets with output-safety oracle; reject XLS records that panic calamine - #48

Merged
chaliy merged 3 commits into
mainfrom
claude/project-thread-ulbh3z
Oct 27, 2026
Merged

chaliy merged 3 commits into
mainfrom
claude/project-thread-ulbh3z

Conversation

@chaliy

@chaliy chaliy commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Михайло · project thread

What changed

  • Fuzzing: eleven cargo-fuzz targets in crates/markkit/fuzz cover every parser and the renderer: html, document (sniffed raw bytes), docx_xml, xlsx_xml, xlsb_bin, ods_xml, xls_biff, pdf, markdown, csv and render_ir (arbitrary IR trees). Every target checks the S17 output-safety oracle with pulldown-cmark, so it catches more than panics: no raw HTML, no disallowed link schemes, and no text escaping its container. .github/workflows/fuzz.yml runs all of them nightly and uploads crashes. just fuzz <target> [secs] and just fuzz-build run them locally.
  • XLS hardening: the xls_biff target found calamine panicking on about a dozen truncated BIFF record shapes and in its formula decompiler. The XLS pre-scan now rejects those shapes and neutralises formula token streams (cce = 0; only cached values render). calamine then reads a container rebuilt from the exact stream we scanned.
  • Knowledge: a new security/fuzzing.md covers targets, seeds, triage and the "fix before the dependency sees it" rule, alongside T6c threat-model updates, testing.md, the AGENTS row, the log and the CHANGELOG.

Why

This is roadmap initiative 1 (extraordinary security testing). catch_unwind doesn't help in WASM or in panic = "abort" embedders, so a dependency panic has to be prevented before calamine sees the input.

Before / After

  • Before: some truncated XLS records panicked inside calamine, which aborts in WASM and panic = "abort" builds. There was no fuzzing.
  • After: those inputs get an invalid XLS error instead, with regression tests in document_security.rs. Local runs were all clean:
    • render_ir 458k execs; xls_biff (seeded) 231k execs after the fixes;
    • 300 s each: docx 69k, xlsx 64k, ods 98k, document 249k;
    • 150 s each on the new targets: xlsb_bin 56k, pdf 1.37M, markdown 589k; csv 484k in 90 s.
  • cargo test --workspace, clippy -D warnings, fmt and check-okf all pass. cargo +nightly fuzz build -O builds all eleven targets.

Risk

  • Low for the library: the only runtime change is the stricter XLS pre-scan, which rejects only record shapes that would panic calamine.
  • The fuzz crate sits outside the workspace and builds only nightly. While rebasing, I found it no longer built on main (shared fixtures now need miniz_oxide), and fixed that here.

Checklist

  • Tests added or updated
  • Backward compatibility considered

Generated by Claude Code

@chaliy
chaliy force-pushed the claude/project-thread-ulbh3z branch from 5fefeb8 to 3dae6b3 Compare October 27, 2026 03:20
The xls_biff fuzz target found calamine reading BIFF records without
bounds checks (process abort under panic = "abort").

- Pre-scan rejects truncated FILEPASS/CODEPAGE/DATEMODE/BOF/BOUNDSHEET/
  LBL/EXTERNSHEET, SST strings cut before cRun/cbExtRst or followed by
  an empty CONTINUE, MERGECELLS count overruns, full-range MULRK and
  empty-range DIMENSIONS
- Defined-name tokens checked for their fixed length
- Formula token streams neutralised (cce = 0): only cached values render
- Sheet scan follows calamine past embedded chart substreams
- calamine reads a container rebuilt from the scanned stream, closing
  the cfb vs calamine CFB reader differential
- 16-byte DIMENSIONS records accepted
… workflow

Seven targets in crates/markkit/fuzz (html, document, docx_xml, xlsx_xml,
ods_xml, xls_biff, render_ir) assert the S14 pulldown-cmark oracle, not
just no-panic. Structure-aware targets wrap fuzzed XML / BIFF in valid
containers via the tests/common builders.

- .github/workflows/fuzz.yml: nightly matrix, validated duration input,
  cached corpora, crash artifacts
- just fuzz / just fuzz-build
- knowledge/security/fuzzing.md: targets, oracle, triage, findings
@chaliy
chaliy force-pushed the claude/project-thread-ulbh3z branch from 3dae6b3 to fecdac6 Compare October 27, 2026 03:42
@chaliy
chaliy merged commit f6fd9e6 into main Oct 27, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant