blobforge

Adversarial fixtures

break your xlsx parser

13 Excel workbooks engineered to fail. Traversal paths inside the package, entity expansion in the worksheet XML, shared string indexes pointing past the end of the table, a declared dimension of 17 billion cells, and formula injection — each with the correct behaviour documented next to it.

Built in your browser. Nothing is uploaded, and no signup is required.

0Passed
0Failed
13Untested

Traversal path inside the package

paths-zip-slip.xlsx

What this tests

A well-formed spreadsheet with one extra archive entry named `../../../tmp/blobforge-xlsx-escape.txt`.

What a correct parser should do

Read only the parts the package declares, by name, and never write archive entries to disk. Tools that unpack an .xlsx to a temporary directory before parsing inherit Zip Slip in full — and because the file opens correctly in Excel, nothing looks wrong.

Bytes · 3.1 KB · first 512 shown
00000000504B030414000000000000002100A6E7PK..........!...
000000100AA0B6020000B6020000130000005B43..............[C
000000206F6E74656E745F54797065735D2E786Dontent_Types].xm
000000306C3C3F786D6C2076657273696F6E3D22l<?xml version="
00000040312E302220656E636F64696E673D22551.0" encoding="U
0000005054462D3822207374616E64616C6F6E65TF-8" standalone
000000603D22796573223F3E0A3C547970657320="yes"?>.<Types
00000070786D6C6E733D22687474703A2F2F7363xmlns="http://sc
0000008068656D61732E6F70656E786D6C666F72hemas.openxmlfor
000000906D6174732E6F72672F7061636B616765mats.org/package
000000A02F323030362F636F6E74656E742D7479/2006/content-ty
000000B0706573223E3C44656661756C74204578pes"><Default Ex
000000C074656E73696F6E3D2272656C73222043tension="rels" C
000000D06F6E74656E74547970653D226170706ContentType="appl
000000E069636174696F6E2F766E642E6F70656Eication/vnd.open
000000F0786D6C666F726D6174732D7061636B61xmlformats-packa
0000010067652E72656C6174696F6E7368697073ge.relationships
000001102B786D6C222F3E3C44656661756C7420+xml"/><Default
00000120457874656E73696F6E3D22786D6C2220Extension="xml"
00000130436F6E74656E74547970653D22617070ContentType="app
000001406C69636174696F6E2F786D6C222F3E3Clication/xml"/><
000001504F7665727269646520506172744E616DOverride PartNam
00000160653D222F786C2F776F726B626F6F6B2Ee="/xl/workbook.
00000170786D6C2220436F6E74656E7454797065xml" ContentType
000001803D226170706C69636174696F6E2F766E="application/vn
00000190642E6F70656E786D6C666F726D617473d.openxmlformats
000001A02D6F6666696365646F63756D656E742E-officedocument.
000001B073707265616473686565746D6C2E7368spreadsheetml.sh
000001C06565742E6D61696E2B786D6C222F3E3Ceet.main+xml"/><
000001D04F7665727269646520506172744E616DOverride PartNam
000001E0653D222F786C2F776F726B7368656574e="/xl/worksheet
000001F0732F7368656574312E786D6C2220436Fs/sheet1.xml" Co
Hover a byte to see why it matters.
  • NUL byte
  • Line feed
  • Control character
  • Double quote
  • Non-ASCII byte

How to use these fixtures

  1. Download a single file, or take all 13 as a ZIP — the archive includes a README explaining every file.
  2. Run each one through your own XLSX import path.
  3. Come back and record whether your parser handled it. Results are kept in your browser, so leaving the page is expected.
  4. Copy the damage report as Markdown and paste it into a pull request or an issue.

Why XLSX parsers fail

An .xlsx file is a ZIP archive containing XML parts. That single fact is the whole story: it inherits the failure modes of both formats, and adds its own on top — shared string tables that lie about their size, cells whose declared type disagrees with their content, and a dimension header that readers are tempted to allocate from.

The packaging is what makes it dangerous. These are files that pass an extension check, pass a content-type sniff, and in several cases open correctly in Excel — while carrying an entity bomb in the worksheet or a traversal path in the archive. The parser you need to harden is usually one inside a library you did not write.

Questions

Why does XLSX need its own pack when you already have ZIP and XML?
Because an .xlsx file is a ZIP archive of XML parts, it inherits every failure mode of both — a traversal path works here exactly as in a plain archive, and an entity expansion exactly as in plain XML. What changes is the context: a developer who would never hand untrusted XML to a lenient parser will happily pass an uploaded spreadsheet to a library, because a spreadsheet feels like structured data rather than an archive full of attacker-controlled markup. It is both.
Will these open in Excel?
Several will, which is the point. The Zip Slip fixture is a completely valid workbook that happens to carry one extra archive entry, so a file-type check, a virus scan and Excel itself all pass it. The damage happens in whatever unpacks it to a temporary directory before parsing.
Are these files dangerous?
No. They are inert test files for checking your own import pipeline. Nothing here executes on its own — the fixtures that model injection or traversal exist so you can verify your own code rejects them, and the resource-exhaustion ones are deliberately scaled down so they demonstrate the behaviour without taking down the machine you are testing from.
Why does the page show raw bytes?
Because most of these cases are invisible in a text preview. A byte order mark renders as nothing, a bare carriage return looks identical to a CRLF, and a NUL looks like the end of the string. The hex view highlights exactly which bytes matter and explains each one when you hover it.
Does my pass/fail progress get saved?
Yes, in your own browser only, and separately for each format. The workflow expects you to leave the page, run a fixture through your parser, and come back — so results persist locally. Nothing is uploaded, and clearing site data clears them.
Is this free?
Yes. Every fixture is generated in your browser, with no signup and no upload.