# Excelize: Unchecked pivot-cache field index in extractPivotTableFields causes unrecoverable panic

Excelize's extractPivotTableFields indexes the pivot-cache field-name slice with an unvalidated fld attribute from a separately parsed pivotTable XML part, panicking the host process.

- Payout: Research
- Severity: high
- Program: Excelize
- Found by: White0xdi3
- Disclosed: 2026-10-08
- CVE: CVE-2026-107211
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-mx22-3794-2vpv
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-mx22-3794-2vpv
- Tags: area:oss domain:oss impact:dos kind:research platform:ghsa sev:high tech:go technique:parser-differential technique:source-review vuln:dos

**Target:** Go library parsing xl/pivotTables/pivotTableN.xml and xl/pivotCache/pivotCacheDefinitionN.xml inside .xlsx
**Root cause:** extractPivotTableFields builds `order` from the pivot cache's <cacheFields> but indexes it with the raw attacker-controlled `fld` int attribute of <dataField> and with a loop index over pivotFields from a separately parsed part, with no cross-validation of counts or bounds checks.
**Steps:**
1. Build a legitimate pivot-table workbook via excelize's public AddPivotTable API and save it.
2. Rezip the .xlsx, tampering one XML part: either set `<dataField fld="1">` to `fld="99999"` in xl/pivotTables/pivotTable1.xml, or collapse the pivot cache's `<cacheFields count="2">` to `count="0"`.
3. In a fresh process call `excelize.OpenFile(path)` (succeeds) then `f.GetPivotTables("Sheet1")`.
4. Observe unrecovered panic: `index out of range [99999] with length 2` at pivotTable.go:1443, or `index out of range [0] with length 0` at pivotTable.go:1431.
**How found:** Read pivotTable.go:1425-1454 and noticed `order := pc.getPivotCacheFieldsName()` indexed by `field.Fld` and by a loop counter over a separately parsed pivotFields list with zero bounds checks; confirmed xmlPivotTable.go:271 declares Fld as a bare unchecked int attribute, and that sibling functions extractPivotTableShowValuesAs/extractPivotTableField do bounds-check the same pattern, showing a missed guard rather than a designed invariant.
**Exploitation:** Any service that opens an untrusted .xlsx and calls GetPivotTables crashes: excelize has no recover(), so the host process dies (exit status 2) or an HTTP handler returns an unhandled 500. Deterministic, single small crafted file, no user interaction beyond normal open-and-read.
**Insight:** When a parser reads two related XML parts independently, the count in one part is never validated against the other; grep for slice indexing by XML attribute ints and for loop indices over one part used to index a slice built from another part — and check whether sibling functions in the same file already bounds-check the identical pattern.

