{"id":"CVE-2026-107221","aliases":["GHSA-8mcq-6wmr-jrjv"],"url":"https://o3.security/vulnerability/CVE-2026-107221","summary":"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","details":"## Affected versions and vulnerable location\n\n- Confirmed at `ae2113b` (current HEAD).\n- Sink: `rows.go:967` `ws.SheetData.Row[rowIdx].C[colNum-1] = *colData` inside `checkRow`.\n- `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]`.\n\n## Root cause\n\nThe 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.\n\n## Attacker model and reachability\n\nAny 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.\n\n## Proof of concept (executed)\n\nCrafted `xl/worksheets/sheet1.xml` with a row whose cells are out of column order and whose earlier cell exceeds the last cell's column:\n\n```xml\n<row r=\"1\"><c r=\"D1\"><v>4</v></c><c r=\"C1\"><v>3</v></c></row>\n```\n\n`GetCellValue(\"Sheet1\",\"A1\")` (via `getCellStringFunc -> workSheetReader -> checkRow`) panicked `index out of range [3] with length 3` at `rows.go:967`.\n\nConfirming grep:\n\n```bash\nrg -n \"func checkRow|lastCol|Row\\[rowIdx\\].C\\[colNum-1\\]\" rows.go\n```\n\n## Suggested fix\n\nSize `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.","published":"2026-10-07T18:41:46.047Z","modified":"2026-10-21T02:30:48.312392004Z","cvss":{"score":6.5,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"Go","name":"github.com/xuri/excelize/v2","fixedVersion":"2.11.1-0.20260816084418-46a5eb289448"}],"fix":{"url":"https://github.com/qax-os/excelize/commit/46a5eb289448e40fbc7d3294589b03861fb822f0","label":"qax-os/excelize@46a5eb2"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/107xxx/CVE-2026-107221.json"},{"type":"ADVISORY","url":"https://github.com/qax-os/excelize/security/advisories/GHSA-8mcq-6wmr-jrjv"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-107221"},{"type":"FIX","url":"https://github.com/qax-os/excelize/commit/46a5eb289448e40fbc7d3294589b03861fb822f0"},{"type":"FIX","url":"https://github.com/qax-os/excelize/pull/2376"},{"type":"PACKAGE","url":"https://github.com/qax-os/excelize"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-21T02:30:48.312392004Z"}}