Files
vuln-osv/data/Go/GHSA-g27h-8qhm-6pff.json
T

1 line
5.0 KiB
JSON

{"schema_version":"1.9.0","id":"GHSA-g27h-8qhm-6pff","published":"2026-10-07T20:23:17Z","modified":"2026-11-05T23:26:10.486510397Z","aliases":["CVE-2026-107220","GO-2026-6680"],"summary":"Excelize: Panic in cellInRange on a worksheet with an empty mergeCell ref","details":"### Summary\n\nA worksheet containing `<mergeCell ref=\"\"/>` makes `mergeCellsParser` leave the cached rectangle empty, and `cellInRange` then indexes that empty slice without a length check. Every non-streaming cell API panics with `index out of range [0] with length 0` on the first cell read after opening the file.\n\n### Where it is\n\n`cell.go`, `cellInRange`\n\n func cellInRange(cell, ref []int) bool {\n \treturn cell[0] >= ref[0] && cell[0] <= ref[2] && cell[1] >= ref[1] && cell[1] <= ref[3]\n }\n\n`ref` is indexed at four positions with no bounds check.\n\nThe caller that can hand it an empty slice, `mergeCellsParser`\n\n if ref := ws.MergeCells.Cells[i].Ref; len(ws.MergeCells.Cells[i].rect) == 0 && ref != \"\" {\n \tif strings.Count(ref, \":\") != 1 {\n \t\tref += \":\" + ref\n \t}\n \trect, err := rangeRefToCoordinates(ref)\n \tif err != nil {\n \t\treturn cell, err\n \t}\n \t_ = sortCoordinates(rect)\n \tws.MergeCells.Cells[i].rect = rect\n }\n if cellInRange([]int{col, row}, ws.MergeCells.Cells[i].rect) {\n\nThe `ref != \"\"` condition means an empty `ref` skips the block that populates `rect`, so `rect` stays nil. The `cellInRange` call on the next line is unconditional and receives that nil slice.\n\n`Ref` comes straight from `xl/worksheets/sheetN.xml` through `encoding/xml`, so an empty string is entirely attacker-controlled.\n\n### Impact\n\nAny consumer that opens an untrusted workbook and reads a cell panics. The loop scans every merged cell, so any cell reference triggers it, not a specific one. Affected entry points include `GetCellValue`, `GetCellType`, `GetCellFormula`, `SetCellValue` and the in-cell branch of `GetPictures`. The streaming `Rows` and `GetRows` use the SAX path and do not go through this parser, and `GetMergeCells` routes through `Rect()` which errors cleanly, which is probably why this has not surfaced before.\n\nThere is no option or flag involved; opening the file succeeds and the panic fires on the first cell read. Unless the caller wraps the call in `recover()` it takes the process down.\n\nThis is a regression. Commit a34c81e (PR #1500, 2023-03-20) replaced `checkCellInRangeRef`, whose `len(rng) != 2` guard returned cleanly for an empty ref, with the cached-rect fast path above, and the guard did not come along. `git merge-base --is-ancestor` confirms that commit is an ancestor of v2.11.0, so released versions are affected as well as HEAD.\n\n### Proof of concept\n\nExecuted at HEAD.\n\nBuild a minimal xlsx whose `xl/worksheets/sheet1.xml` contains:\n\n <mergeCells count=\"1\"><mergeCell ref=\"\"></mergeCell></mergeCells>\n\nThen:\n\n f, err := excelize.OpenReader(bytes.NewReader(data))\n if err != nil { t.Fatal(err) }\n _, _ = f.GetCellValue(\"Sheet1\", \"A1\")\n\nObserved, running against the repository at HEAD:\n\n panic: runtime error: index out of range [0] with length 0\n excelize.cellInRange cell.go:1691\n excelize.(*xlsxWorksheet).mergeCellsParser cell.go:1660\n excelize.(*File).getCellStringFunc cell.go:1512\n excelize.(*File).GetCellValue cell.go:72\n\n`OpenReader` itself returns no error; the file is 1656 bytes. A1, B1 and A2 all reproduce it.\n\nFor context on how targeted this is, I ran a battery of 38 crafted files covering data validation, conditional formatting, cell and row references, number formats, cols, hyperlinks, dimension, shared strings and tables. Only the empty-ref merge cell panicked; everything else returned a clean error. The other parsing paths look well guarded.\n\n### Suggested fix\n\nSkip the entry when the rectangle is empty, before the range test:\n\n if len(ws.MergeCells.Cells[i].rect) == 0 {\n \tcontinue\n }\n\nCredit goes to arpitjain099.","affected":[{"package":{"name":"github.com/xuri/excelize/v2","ecosystem":"Go","purl":"pkg:golang/github.com/xuri/excelize/v2"},"ranges":[{"type":"SEMVER","events":[{"introduced":"2.7.1"},{"fixed":"2.11.1-0.20260820023833-99903a3240e5"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-g27h-8qhm-6pff/GHSA-g27h-8qhm-6pff.json"}}],"references":[{"type":"WEB","url":"https://github.com/qax-os/excelize/security/advisories/GHSA-g27h-8qhm-6pff"},{"type":"WEB","url":"https://github.com/qax-os/excelize/pull/2379"},{"type":"WEB","url":"https://github.com/qax-os/excelize/commit/99903a3240e58a47ff28fb8e05a03bd9d496ec24"},{"type":"PACKAGE","url":"https://github.com/qax-os/excelize"}],"database_specific":{"cwe_ids":["CWE-125","CWE-129"],"github_reviewed":true,"github_reviewed_at":"2026-10-07T20:23:17Z","nvd_published_at":null,"severity":"MODERATE"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H"}]}