Excelize: Unchecked pivot-cache field index in extractPivotTableFields causes unrecoverable panic
Collected exploitation and prediction evidence
Risk source coverageCISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.
No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.
cve-program · CVE-2026-107211
PublishedExcelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.8.1 to 2.11.0, separately parsed pivot-table field indices are used to index the pivot-cache field-name slice without bounds checks. extractPivotTableFields uses getPivotCacheFieldsName output while processing GetPivotTables and trusts the dataField fld attribute as an index. When a crafted workbook supplies a pivot-field count mismatch or an out-of-range dataField fld value before GetPivotTables is called, the unchecked index causes a Go slice-bounds panic that escapes the library, allowing an attacker to crash the process or request worker. No fixed version is available as of this review.
- Source published
- 7 Oct 2026 17:11:17 UTC
- Source modified
- 7 Oct 2026 19:03:18 UTC
- Observed here
- 23 Oct 2026 15:48:27 UTC
- Source terms
- CVE Program Terms of Use
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 4.0 · 8.7 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
euvd · EUVD-2026-94526
PublishedExcelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.8.1 to 2.11.0, separately parsed pivot-table field indices are used to index the pivot-cache field-name slice without bounds checks. extractPivotTableFields uses getPivotCacheFieldsName output while processing GetPivotTables and trusts the dataField fld attribute as an index. When a crafted workbook supplies a pivot-field count mismatch or an out-of-range dataField fld value before GetPivotTables is called, the unchecked index causes a Go slice-bounds panic that escapes the library, allowing an attacker to crash the process or request worker. No fixed version is available as of this review.
EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.
Original publisher date fields
datePublished: Oct 7, 2026, 5:11:17 PM
dateUpdated: Oct 7, 2026, 7:03:18 PM
- Source published
- Oct 7, 2026, 5:11:17 PM (timezone not stated)
- Source modified
- Oct 7, 2026, 7:03:18 PM (timezone not stated)
- Observed here
- 7 Nov 2026 12:38:33 UTC
- Source terms
- ENISA EUVD · source terms and attribution apply
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.
EUVD CVSS 4.0 · 8.7 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
nvd · CVE-2026-107211
PublishedExcelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.8.1 to 2.11.0, separately parsed pivot-table field indices are used to index the pivot-cache field-name slice without bounds checks. extractPivotTableFields uses getPivotCacheFieldsName output while processing GetPivotTables and trusts the dataField fld attribute as an index. When a crafted workbook supplies a pivot-field count mismatch or an out-of-range dataField fld value before GetPivotTables is called, the unchecked index causes a Go slice-bounds panic that escapes the library, allowing an attacker to crash the process or request worker. No fixed version is available as of this review.
NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.
Source analysis status: Analyzed
Weakness classifications: CWE-129
Original publisher date fields
published: 2026-10-07T18:17:18.490
lastModified: 2026-10-13T14:24:41.017
1 source CPE criteria · context only
- cpe:2.3:a:excelize:excelize:*:*:*:*:*:go:*:*
The original JSON retains the logical configuration operators, exclusions and version bounds.
- Source published
- 7 Oct 2026 18:17:18 UTC
- Source modified
- 13 Oct 2026 14:24:41 UTC
- Observed here
- 25 Oct 2026 04:17:41 UTC
- Source terms
- NIST NVD usage terms; underlying source attribution retained
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 4.0 · [email protected] · Secondary · 8.7 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1 · [email protected] · Primary · 5.5 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
osv · GHSA-mx22-3794-2vpv
Published### Summary extractPivotTableFields builds `order := pc.getPivotCacheFieldsName()` from xl/pivotCache/pivotCacheDefinitionN.xml's <cacheFields> list, then indexes it with values taken from a *separately parsed* xl/pivotTables/pivotTableN.xml part with zero cross-validation: `order[field.Fld]` where `field.Fld` is a raw attacker-controlled `int` from the `<dataField fld="N">` attribute, and `order[fieldIdx]` where fieldIdx is a loop index over `pt.PivotFields.PivotField` that need not match the cache's field count. I executed two independent PoCs against the current HEAD: (1) trimmed the pivot cache's <cacheFields> to count=0 while leaving the pivot table's 2 pivotFields untouched -> `runtime error: index out of range [0] with length 0` at pivotTable.go:1431; (2) left the 2 cache fields completely intact and only changed one attribute, `fld="1"` -> `fld="99999"`, in xl/pivotTables/pivotTable1.xml -> `runtime error: index out of range [99999] with length 2` at pivotTable.go:1443. Both stack traces confirmed via non-recovered `go test` runs: extractPivotTableFields -> getPivotTable -> GetPivotTables. ### Reachability / who can trigger this Unauthenticated: any application that opens an untrusted .xlsx containing a pivot table and calls the public `File.GetPivotTables(sheet)` API. excelize has no recover() anywhere, so this crashes the host process (CLI/worker) or surfaces as an unhandled 500 in a request-scoped-recover HTTP service. Deterministic, single small crafted file, no user interaction beyond the service's normal open-and-read flow. ### Proof of Concept / Reproduction **Method:** 1) Verified source at HEAD (commit e81f0500, 2026-09-27, freshly cloned/up to date; `git status` clean except the new PoC files I added): read pivotTable.go:1425-1454 and confirmed verbatim: `order := pc.getPivotCacheFieldsName()` (1428), loop `for fieldIdx, field := range pt.PivotFields.PivotField` indexing `order[fieldIdx]` at axisRow/axisCol/axisPage (1431/1434/1437), and `Data: order[field.Fld]` (1443) inside the `pt.DataFields.DataField` loop -- zero bounds checks anywhere. Confirmed xmlPivotTable.go:271 `Fld int `xml:"fld,attr"`` on xlsxDataField (raw attacker-controlled int, no validation) and the sibling Fld field at line 252. Ran `go build ./...` -- compiles clean. Both citations are accurate. 2) Found the repo already contained an untracked PoC test file from earlier work (pivot_fld_poc_test.go) implementing this exact finding via a byte-level zip-tamper technique (build a legitimate workbook with excelize's own public AddPivotTable API, then hex-edit one raw XML part inside the saved .xlsx's zip bytes -- not a hypothetical reimplementation, uses the real unmodified excelize package). Did not just trust it: ran it myself with `go test -run 'TestPivotTableDataFieldFldIndexPanic|TestPivotTableDataFieldFldAttributeOutOfRangePanic' -v .` and observed real PASS output with exact matching panic strings. 3) For stronger, independent evidence beyond a recover-wrapped test, I wrote my own standalone non-test Go program from scratch, cmd/pivotcrash/main.go (module github.com/xuri/excelize/v2, go1.27.0 toolchain), that imports the real compiled excelize package (no reimplementation) with two subcommands: `gen`/`gen-trim` (attacker: build a legit pivot-table workbook via the public API, then rezip with one XML part tampered -- either dataField fld="1"->fld="99999", or the pivot cache's <cacheFields count="2"> collapsed to count="0" -- writing the crafted .xlsx to disk) and `run` (victim: a fresh OS process that does exactly what any consumer service does -- `excelize.OpenFile(path)` then `f.GetPivotTables("Sheet1")` -- with zero recover() anywhere in the file or the call chain). Executed as four separate `go run` process invocations: gen, run, gen-trim, run. **Evidence:** Test run (pre-existing PoC file, re-executed by me, both PASS): `--- PASS: TestPivotTableDataFieldFldIndexPanic (0.00s)` logging `CONFIRMED: GetPivotTables panicked ... runtime error: index out of range [0] with length 0` `--- PASS: TestPivotTableDataFieldFldAttributeOutOfRangePanic (0.00s)` logging `CONFIRMED: GetPivotTables panicked ... runtime error: index out of range [99999] with length 2` `ok github.com/xuri/excelize/v2 0.114s` Standalone process-crash run #1 (`go run ./cmd/pivotcrash run pivotcrash_malicious.xlsx`, fld="99999" tamper, cache untouched with 2 fields): [victim] file opened successfully, workbook parsed with no errors [victim] now calling f.GetPivotTables("Sheet1") on the untrusted workbook... panic: runtime error: index out of range [99999] with length 2 goroutine 1 [running]: github.com/xuri/excelize/v2.(*File).extractPivotTableFields(...) .../pivotTable.go:1443 +0xb9b github.com/xuri/excelize/v2.(*File).getPivotTable(...) .../pivotTable.go:1379 +0x7f6 github.com/xuri/excelize/v2.(*File).GetPivotTables(...) .../pivotTable.go:1278 +0x2f7 main.runVictim(...) / main.main() -> exit status 2 Standalone process-crash run #2 (`go run ./cmd/pivotcrash run pivotcrash_malicious_trim.xlsx`, cacheFields trimmed to count=0, pivot table's 2 pivotFields untouched): panic: runtime error: index out of range [0] with length 0 goroutine 1 [running]: github.com/xuri/excelize/v2.(*File).extractPivotTableFields(...) .../pivotTable.go:1431 +0xc15 github.com/xuri/excelize/v2.(*File).getPivotTable(...) .../pivotTable.go:1379 +0x7f6 github.com/xuri/excelize/v2.(*File).GetPivotTables(...) .../pivotTable.go:1278 +0x2f7 main.runVictim(...) / main.main() -> exit status 2 In both standalone runs the "GetPivotTables RETURNED NORMALLY" print statement that follows the call was never reached -- the OS process itself terminated via an unrecovered Go panic (exit status 2), for a crafted .xlsx that `excelize.OpenFile` accepted without any error. Both stack traces terminate at the exact claimed sink lines (pivotTable.go:1443 and :1431) via the exact claimed call chain (extractPivotTableFields -> getPivotTable -> GetPivotTables). This is a real, unhandled crash of a real process running unmodified excelize code -- not a mock, not a simulated/hypothetical trace. ### Novelty Ran all 5 requested checks in full, plus extra verification. (1) Pulled the complete, paginated GHSA list via gh api (16 advisories, all state=published, no hidden drafts) and grepped full descriptions, not just summaries, for "pivot" -- only one incidental, non-overlapping hit (GHSA-wp2g-vpjj-g53r cites pivotTable.go:538 as an AddPivotTable call site for an unrelated formula-recursion stack-overflow bug). (2) Ran ~15 targeted GitHub issue searches (extractPivotTableFields, getPivotCacheFieldsName, GetPivotTables panic, dataField fld, BaseField, ShowValuesAs, "index out of range", cacheFields count mismatch, PivotFields panic, DataField, plus a broad "pivot" query returning 30 issues/PRs all individually triaged by title) and full-text-read the 5 most plausible (#2161, #1937, #2183, #1954, #1945) -- none match; the closest (#2161) is a nil-pointer bug in a different function/field, detailed above. (3) Checked PRs both by keyword (is:pr pivot = 10, is:pr "pivotTable.go" fld = 0, all reviewed; read PR #2168's full diff) and exhaustively (pulled all 31 currently-open PRs on the repo and reviewed every title) -- none touch pivot field indexing. (4) Ran 5 WebSearch queries combining the mechanism with "excelize"/"pivot table"/"CVE", and independently cross-checked OSV.dev's API (which mirrors NVD/GHSA/GO vuln DB) -- turned up only the already-ruled-out GHSA-fx5j (shared-string) and GHSA-h69g (row allocation) advisories, and a non-upstream automated "task"-tracking repo (windyswe/q ...[truncated for brevity] Closest prior art considered: qax-os/excelize issue #2161 + merged PR #2168 ("This closes #2161, fix panic on read unsupported pivot table cache source types", merged 2025-07-05). That bug: pivotCacheDefinition.xml's <cacheSource type="external"/> has no <worksheetSource> child, so pc.CacheSource.WorksheetSource is a nil pointer, and getPivotTable() (then pivotTable.go:875) dereferenced .Sheet/.Ref on it -> nil-pointer-dereference panic, reached via GetPivotTables (then line 803). Fix was an early type-validation error return in getPivotTable, not any bounds check. SAME-FIX TEST: would that fix also close the candidate bug? No. The candidate's panic is index-out-of-range (not nil-deref) inside a different function, extractPivotTableFields, on a different data structure: `order := pc.getPivotCacheFieldsName()` (length = count of <cacheFields>) indexed by (a) a loop counter over pt.PivotFields.PivotField (a separate, unrelated count from pivotTableN.xml) at lines 1431/1434/1437, and (b) the raw unvalidated int field. ...[truncated for brevity] ### Independent skeptical review Tried hard to kill this and it holds. Independently re-read current HEAD (e81f05008b3, fetched and diffed against origin/master to confirm it's current) rather than trusting the report: pivotTable.go:1427-1454 (extractPivotTableFields) has zero bounds checks on order[fieldIdx] (axisRow/Col/Page branches, lines 1431/1434/1437) or order[field.Fld] (line 1443) — confirmed by direct Read, matching the claim's line citations exactly. This is a genuine oversight, not a designed invariant: the very next function in the same file, extractPivotTableShowValuesAs (lines 1476, 1486) and extractPivotTableField (line 1508), DO bounds-check structurally identical order/slice indices before using them — the developers clearly know this pattern needs guarding and simply missed it in extractPivotTableFields. Confirmed xmlPivotTable.go:271 Fld is a bare unchecked `int` XML attribute, and confirmed getPivotTable/pivotCacheReader/pivotTableReader perform no count-vs-actual-length cross-validation between the independently-parsed pivotTable and pivotCache XML parts. Grepped the entire repo for recover() — zero hits outside the researcher's own test file, confirming "no recover anywhere" is accurate. Re-ran both provided PoCs myself in a throwaway test file against this exact HEAD (go test -run TestPoC_PivotCache -v): both panic exactly as claimed, with byte-identical messages — "index out of range [0] with length 0" (truncated-cache-fields PoC, line 1431) and "index out of range [99999] with lengt ...[truncated for brevity]
- Source published
- 8 Oct 2026 17:52:17 UTC
- Source modified
- 19 Oct 2026 21:41:17 UTC
- Observed here
- 24 Oct 2026 13:19:41 UTC
- Source terms
- GitHub Advisory Database · CC-BY-4.0
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS_V4 · CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Publisher rating · HIGH
osv · GO-2026-6695
PublishedPanic via unchecked pivot-cache field index in github.com/xuri/excelize/v2
- Source published
- 19 Oct 2026 21:08:21 UTC
- Source modified
- 19 Oct 2026 21:31:39 UTC
- Observed here
- 24 Oct 2026 13:19:41 UTC
- Source terms
- OSV · upstream source license applies
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · OSV · upstream source license applies. Source text is retained; field grouping and change labels are provided by ChangeIntel.