Excelize: Streaming GetRows row-bound bypass causes attacker-controlled allocation
Collected exploitation and prediction evidence
Risk source coverageCISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.
No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.
cve-program · CVE-2026-59161
PublishedExcelize is a Go language library for reading and writing Microsoft Excel spreadsheets. Prior to 2.11.0, the streaming worksheet reader used by Rows and GetRows does not enforce the TotalRows limit on the row r attribute, allowing a small XLSX file with a row number above 1048576 and no cell coordinate to make GetRows append empty rows up to the attacker-controlled index and consume excessive memory and CPU. This issue is fixed in version 2.11.0.
- Source published
- 10 Jul 2026 15:47:38 UTC
- Source modified
- 14 Jul 2026 01:55:21 UTC
- Observed here
- 23 Oct 2026 15:48:27 UTC
- Source terms
- CVE Program Terms of Use
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 4.0 · 8.7 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
euvd · EUVD-2026-42936
PublishedExcelize is a Go language library for reading and writing Microsoft Excel spreadsheets. Prior to 2.11.0, the streaming worksheet reader used by Rows and GetRows does not enforce the TotalRows limit on the row r attribute, allowing a small XLSX file with a row number above 1048576 and no cell coordinate to make GetRows append empty rows up to the attacker-controlled index and consume excessive memory and CPU. This issue is fixed in version 2.11.0.
EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.
Original publisher date fields
datePublished: Jul 10, 2026, 3:47:38 PM
dateUpdated: Jul 14, 2026, 1:55:21 AM
- Source published
- Jul 10, 2026, 3:47:38 PM (timezone not stated)
- Source modified
- Jul 14, 2026, 1:55:21 AM (timezone not stated)
- Observed here
- 7 Nov 2026 13:24:44 UTC
- Source terms
- ENISA EUVD · source terms and attribution apply
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.
EUVD CVSS 4.0 · 8.7 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
nvd · CVE-2026-59161
PublishedExcelize is a Go language library for reading and writing Microsoft Excel spreadsheets. Prior to 2.11.0, the streaming worksheet reader used by Rows and GetRows does not enforce the TotalRows limit on the row r attribute, allowing a small XLSX file with a row number above 1048576 and no cell coordinate to make GetRows append empty rows up to the attacker-controlled index and consume excessive memory and CPU. This issue is fixed in version 2.11.0.
NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.
Source analysis status: Analyzed
Weakness classifications: CWE-400 · CWE-770
Original publisher date fields
published: 2026-07-10T17:17:02.250
lastModified: 2026-10-25T15:10:00.147
1 source CPE criteria · context only
- cpe:2.3:a:excelize:excelize:*:*:*:*:*:go:*:*
The original JSON retains the logical configuration operators, exclusions and version bounds.
- Source published
- 10 Jul 2026 17:17:02 UTC
- Source modified
- 25 Oct 2026 15:10:00 UTC
- Observed here
- 27 Oct 2026 15:15:33 UTC
- Source terms
- NIST NVD usage terms; underlying source attribution retained
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS 4.0 · [email protected] · Secondary · 8.7 CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
CVSS 3.1 · [email protected] · Primary · 7.5 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
osv · GHSA-q5j5-6p94-4gwc
Published# Streaming GetRows row-bound bypass causes attacker-controlled allocation ## Summary Excelize's prior row-bound fix for GHSA-h69g / CVE-2026-54063 protects the checked worksheet parser, but the streaming worksheet reader used by `Rows` and `GetRows` does not enforce the same `TotalRows` bound on the row `r` attribute. A small XLSX file can set a row number above Excelize's maximum row (`1048576`) and omit the cell coordinate. `GetRows` then appends empty rows up to the attacker-controlled row index and returns success. This was reproduced on the current default branch commit `1213a8bd7c5ab360554603ac5c995ccaf6eb4314` and the latest release tag `v2.10.1` (`5ad5ab3af0054c55bdce09f1530085600e9f2e45`). ## Affected package - Package: `github.com/xuri/excelize/v2` - Tested affected versions: current default branch at `1213a8bd7c5ab360554603ac5c995ccaf6eb4314`, and release `v2.10.1` - Fixed version: none known at the time of this report ## Impact An attacker who can provide an XLSX file to an application that calls `GetRows` can cause memory and CPU usage to scale with an attacker-controlled row number, even though the file itself is tiny. This is an availability issue and appears to be an incomplete coverage variant of the GHSA-h69g row-index allocation class. In the conservative PoC, `row r="2000000"` returned a `[][]string` with length 2,000,000 and allocated about 46 MB. Larger row numbers scale the allocation further. ## Root cause The checked parser path validates row numbers: - `excelize.go`: `checkRowNum(r int)` rejects negative rows and rows greater than `TotalRows`. - `excelize.go`: `checkSheet()` calls `checkRowNum(r.R)` before allocating sheet rows. - `workSheetReader()` invokes `checkSheet()` / `checkRow()` before returning a cached worksheet. The streaming path does not use that checked parser: - `rows.go`: `Rows(sheet)` opens an XML decoder directly. - `Rows.Next()` accepts the row `r` attribute and assigns it to the iterator's current row without applying `checkRowNum()`. - `Rows.Columns()` also assigns row `r` to the iterator state without applying `checkRowNum()`. - `GetRows()` appends empty row slices for the gap between the previous row and the current row. Because a cell without an `r` coordinate can still contain a value, the worksheet can avoid cell-coordinate row validation while still causing `GetRows()` to materialize rows up to the out-of-range row number. ## Minimal worksheet payload ```xml <?xml version="1.0" encoding="UTF-8"?> <worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"> <sheetData> <row r="2000000"><c t="s"><v>0</v></c></row> </sheetData> </worksheet> ``` The workbook also contains a normal `sharedStrings.xml` with one string (`ok`). ## Reproduction A minimal Go harness creates the XLSX in memory and calls `GetRows("Sheet1")`: ```go rows, err := f.GetRows("Sheet1") fmt.Println("rows_len:", len(rows)) if len(rows) > 0 { fmt.Println("last_row:", rows[len(rows)-1]) } fmt.Printf("returned error: %T %v\n", err, err) ``` Observed output on current default branch commit `1213a8bd7c5ab360554603ac5c995ccaf6eb4314`: ```text == streaming GetRows row r=2000000 cell without r == rows_len: 2000000 last_row: [ok] returned error: <nil> <nil> elapsed=21ms alloc_delta=46MB ``` Observed output on latest release tag `v2.10.1`: ```text == streaming GetRows row r=2000000 cell without r == rows_len: 2000000 last_row: [ok] returned error: <nil> <nil> elapsed=14ms alloc_delta=46MB ``` A control using the checked parser with `row r="1048577"` and `c r="A1048577"` correctly returns `row number exceeds maximum limit`, confirming this report is about inconsistent enforcement in the streaming path rather than a missing global constant. ## Expected behavior `Rows` / `GetRows` should reject row numbers greater than `TotalRows` with the same error behavior as the checked parser path. ## Suggested remediation - Apply the same row-bound validation in the streaming reader immediately after parsing a row `r` attribute. - Preserve and return row parsing errors from `GetRows()` instead of silently continuing or returning only `Rows.Close()` errors. - Add regression tests for `GetRows()` on a worksheet containing `row r="1048577"` with a cell value but no cell `r` coordinate.
- Source published
- 10 Sep 2026 15:09:09 UTC
- Source modified
- 17 Sep 2026 17:40:35 UTC
- Observed here
- 24 Oct 2026 13:19:41 UTC
- Source terms
- GitHub Advisory Database · CC-BY-4.0
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.
CVSS_V4 · CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
Publisher rating · HIGH
osv · GO-2026-6453
PublishedIn github.com/xuri/excelize/v2, the streaming worksheet reader used by Rows and GetRows does not enforce the maximum row limit (TotalRows) on row "r" attributes. A crafted spreadsheet with an out-of-bounds row index causes GetRows to allocate empty row slices up to the specified row number, leading to excessive memory consumption and denial of service.
- Source published
- 16 Sep 2026 18:00:43 UTC
- Source modified
- 17 Sep 2026 17:30:11 UTC
- Observed here
- 24 Oct 2026 13:19:41 UTC
- Source terms
- OSV · upstream source license applies
Original source record · Cite this exact observation · Complete original publisher JSON
This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · OSV · upstream source license applies. Source text is retained; field grouping and change labels are provided by ChangeIntel.