All vulnerabilities
CVE-2026-107221GHSA-8mcq-6wmr-jrjv

github.com/xuri/excelize/v2: Excelize: a row whose earlier cell has a higher column reference than its last cell panics index out of range on almost every worksheet read API

qax-os github.com/xuri/excelize/v2goLibraries & packages
CVSS
6.5Medium
ExploitationPublic exploit codeCISA's assessment says proof of concept code is public, assessed 7 Oct 2026. Not in the KEV catalogCISA via CVE List
Exploit odds (EPSS)0.27%Chance of exploitation activity in the next 30 days, higher than 18% of CVEs. Scored 23 Oct 2026FIRST EPSS
Fixed in2.11.1-0.20260816084418-46a5eb289448Fixed versionGitHub
Published7 Oct 2026Updated 7 Oct 2026CVE List

What fixes it

  • 2.11.1-0.20260816084418-46a5eb289448github.com/xuri/excelize/v2, goGitHubgo get github.com/xuri/excelize/v2@v2.11.1-0.20260816084418-46a5eb289448
  • 2.11.1-0.20260816084418-46a5eb289448github.com/xuri/excelize/v2, GoOSVgo get github.com/xuri/excelize/v2@v2.11.1-0.20260816084418-46a5eb289448

A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.

Description

Affected versions and vulnerable location

  • Confirmed at ae2113b (current HEAD).
  • Sink: rows.go:967 ws.SheetData.Row[rowIdx].C[colNum-1] = *colData inside checkRow.
  • checkRow computes lastCol from the column of the last cell in document order (rows.go:940), allocates targetList of that length, then re-scatters every source cell into C[colNum-1].

Root cause

The slice is sized from the last cell's column, but cells are not required to be column-sorted in the XML. A cell that appears earlier in the row but references a higher column than the last cell has colNum-1 >= len(targetList), so the assignment writes out of range. MaxColumns/TotalRows do not help, every individual column is valid; the bug is the ordering assumption, not magnitude.

Attacker model and reachability

Any service that opens an untrusted spreadsheet and calls a worksheet API that goes through workSheetReader -> checkRow (excelize.go:332): GetCellValue, GetCellFormula, CalcCellValue, GetMergeCells, SetCellValue, and essentially every non-streaming worksheet call. (The streaming GetRows/Rows() SAX path does not trigger it.) Unauthenticated, deterministic, unrecovered panic -> process crash.

Proof of concept (executed)

Crafted xl/worksheets/sheet1.xml with a row whose cells are out of column order and whose earlier cell exceeds the last cell's column:

<row r="1"><c r="D1"><v>4</v></c><c r="C1"><v>3</v></c></row>

GetCellValue("Sheet1","A1") (via getCellStringFunc -> workSheetReader -> checkRow) panicked index out of range [3] with length 3 at rows.go:967.

Confirming grep:

rg -n "func checkRow|lastCol|Row\[rowIdx\].C\[colNum-1\]" rows.go

Suggested fix

Size targetList from the maximum cell column in the row (not the last cell in document order), or bounds-check colNum-1 against len(targetList) and grow the slice as needed before the assignment.

Text from GitHub Advisory Database

Affected versions 3

  • github.com/xuri/excelize/v2, go>= 2.0.0, < 2.11.1-0.20260816084418-46a5eb289448GitHub
  • excelizeaffected: >= 2.0.0, <= 2.11.0CVE List
  • github.com/xuri/excelize/v2, Gofrom 2.0.0, fixed in 2.11.1-0.20260816084418-46a5eb289448OSV

References 4