CVE-2026-107217 is a high-severity (CVSS 7.5) CWE-129 vulnerability in github.com/xuri/excelize/v2. A fix is available for github.com/xuri/excelize/v2 — see the affected versions and patch details below.
Excelize ColumnNameToNumber: int64 overflow yields an out-of-domain coordinate with nil error, causing negative slice index panic on r="0" rows
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-107217.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
CVE-2026-107217 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 394,803 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
github.com/xuri/excelize/v2🐹github.com/xuri/excelizeReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Summary
ColumnNameToNumber (lib.go:220-237) accumulates the bijective base-26 value of a column name in an int64 with only an upper-bound check (col > MaxColumns) applied after the loop and no overflow detection. A 14-letter column name whose true value is 3·2⁶⁴ — e.g. VGWQHXLSDVIKWV — wraps to col = 0 and passes the guard, so CellNameToCoordinates("VGWQHXLSDVIKWV1") returns (col=0, row=1, err=nil).
When normalizing a r="0" row, checkSheetR0 (excelize.go:417-443, called from checkSheet at excelize.go:405) runs its checkRow closure with col = 0: colIdx := col - 1 becomes -1, and sheetData.Row[rowIdx].C[-1] (excelize.go:424) raises an unrecovered panic: runtime error: index out of range [-1], killing the host process.
Details
- The row side of the same coordinate gate is enforced (
checkRowNumat excelize.go:342-350 boundsrbefore themake([]xlsxRow, row)allocation; this includes the fix for GHSA-h69g-9hx6-f3v4 / CVE-2026-54063, present in the audited commit). The column side is not:checkSheet/lastRowNum/checkSheetR0treaterr == nilfromCellNameToCoordinatesas proof of an in-domain coordinate (excelize.go:356, :438), which is unsound because of the wrap-around above. - The same unsound gate also feeds
ws.SheetData.Row[rowIdx].C[colNum-1]inxlsxWorksheet.checkRow(rows.go:969): a row combining a valid large column (e.g.XFD1) with an overflowed column panics identically. - Reachable from any
workSheetReader-based API on an attacker-supplied worksheet:GetCellValue,GetCellFormula,SetCellValue,GetMergeCells,GetSheetDimension,GetColWidth,AddTable, etc. - Probe on pristine master:
ColumnNameToNumber("VGWQHXLSDVIKWV")returns(0, nil). - This is a distinct root cause from GHSA-h69g-9hx6-f3v4 (row-index allocation): different mechanism (int64 wrap-around → negative index, not oversized allocation), different sink, different fix.
PoC
A standalone program (public API only) was provided to the maintainer by email (3-column-overflow): it builds a workbook in memory whose xl/worksheets/sheet1.xml contains <sheetData><row r="0"><c r="VGWQHXLSDVIKWV1" t="inlineStr"><is><t>pwn</t></is></c></row></sheetData> (<1 KB of attacker XML), calls OpenReader, then GetCellValue("Sheet1", "A1") → PANIC_REPRODUCED: runtime error: index out of range [-1] on master ecd99d761fe0 (2026-09-08). With the proposed patch the same program prints NO_PANIC_BLOCKED.
Impact
A <1 KB crafted .xlsx crashes any service that opens a user-supplied spreadsheet and reads it — upload processing, mail-scanning pipelines, spreadsheet conversion endpoints. Remote, unauthenticated, no privileges.
Proposed fix
Bound the accumulated value inside the loop: check col > MaxColumns after each digit. Every digit is at least 1, so any name whose true value exceeds MaxColumns crosses the bound inside the loop, before the accumulation can wrap or overflow — this provably covers all cases, including wraps that would land back inside [1, MaxColumns]. A complete patch has been provided to the maintainer.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/xuri/excelize/v2 | ≥ 2.0.0&&< 2.11.1-0.20260910071107-696050fbf14e | 2.11.1-0.20260910071107-696050fbf14ego get github.com/xuri/excelize/v2@v2.11.1-0.20260910071107-696050fbf14e |
| 🐹Go | github.com/xuri/excelize | ≥ 1.1.0 | No fix |
Affected Products
excelizeexcelizeDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/xuri/excelize/v2, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update github.com/xuri/excelize/v2 to 2.11.1-0.20260910071107-696050fbf14e or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-107217 is resolved across your whole dependency graph.
Workarounds
Stop feeding it untrusted input: reject or quarantine files and payloads from unverified sources until you can upgrade, restrict accepted formats to the ones you actually need, and run the parsing or decoding step in a least-privileged sandbox or short-lived worker so a crash or corrupted read cannot reach the rest of the process.
Frequently Asked Questions
Is CVE-2026-107217 in your dependencies?
Find it across Go, including transitive dependencies.