CVE-2026-107211

CVE-2026-107211 is a high-severity security vulnerability in github.com/xuri/excelize/v2 (go), affecting versions >= 2.8.1, < 2.11.1-0.20261003002531-6258dcebc4e2. It is fixed in 2.11.1-0.20261003002531-6258dcebc4e2.

Does this CVE actually affect you?

Kodem shows which CVEs are reachable and running in your applications, so you fix what's exploitable, not just what's listed.

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Runtime intelligence, not another scanner.

Summary

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

extractPivotTableFields builds order := pc.getPivotCacheFieldsName() from xl/pivotCache/pivotCacheDefinitionN.xml's 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 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 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 has no 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 ) 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]

Impact

Affected versions

github.com/xuri/excelize/v2 (>= 2.8.1, < 2.11.1-0.20261003002531-6258dcebc4e2)

Security releases

github.com/xuri/excelize/v2 → 2.11.1-0.20261003002531-6258dcebc4e2 (go)

Kodem intelligence

Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.

Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.

Already deployed Kodem?

See it in your environmentNew to Kodem? Get a demo →

Remediation advice

Upgrade github.com/xuri/excelize/v2 to 2.11.1-0.20261003002531-6258dcebc4e2 or later to resolve this vulnerability.

Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.

Frequently Asked Questions

  1. What is CVE-2026-107211? CVE-2026-107211 is a high-severity security vulnerability in github.com/xuri/excelize/v2 (go), affecting versions >= 2.8.1, < 2.11.1-0.20261003002531-6258dcebc4e2. It is fixed in 2.11.1-0.20261003002531-6258dcebc4e2.
  2. Which versions of github.com/xuri/excelize/v2 are affected by CVE-2026-107211? github.com/xuri/excelize/v2 (go) versions >= 2.8.1, < 2.11.1-0.20261003002531-6258dcebc4e2 is affected.
  3. Is there a fix for CVE-2026-107211? Yes. CVE-2026-107211 is fixed in 2.11.1-0.20261003002531-6258dcebc4e2. Upgrade to this version or later.
  4. Is CVE-2026-107211 exploitable, and should I be worried? Whether CVE-2026-107211 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
  5. What actually determines whether CVE-2026-107211 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
  6. How do I fix CVE-2026-107211? Upgrade github.com/xuri/excelize/v2 to 2.11.1-0.20261003002531-6258dcebc4e2 or later.

Other vulnerabilities in github.com/xuri/excelize/v2

Stop the waste.
Protect your environment with Kodem.