ae2113b (current HEAD).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].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.
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.
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
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.
{
"cwe_ids": [
"CWE-787"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-07T20:23:08Z",
"nvd_published_at": null,
"severity": "MODERATE"
}