CVE-2026-59161: Excelize Streaming GetRows Row-bound Bypass Causes Vulnerability
HIGHCVE-2026-59161 is a HIGH streaming getrows row-bound bypass causes vulnerability (CWE-400: Uncontrolled Resource Consumption) affecting Excelize libraries in the Go ecosystem. The issue involves streaming getrows row-bound bypass causes attacker-controlled allocation. Remediation is available by upgrading affected packages to version 2.11.0.
CVE-2026-59161 at a Glance
How to Fix CVE-2026-59161
To remediate this vulnerability, upgrade all affected Excelize dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade github.com/xuri/excelize/v2 to 2.11.0
Upgrade github.com/xuri/excelize to Latest secure release
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for github.com/xuri/excelize/v2
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-59161, inspect your project dependencies for Excelize:
inspect dependencies for github.com/xuri/excelize/v2
Analyze the terminal output to locate any version of Excelize in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.11.0), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-400: Uncontrolled Resource Consumption, where the product does not properly control the allocation and maintenance of a limited resource. In Excelize, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-59161 Affected Excelize Versions
2 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
github.com/xuri/excelize/v2
|
Go |
< 2.11.0
|
2.11.0
|
|
github.com/xuri/excelize
|
Go |
≤ 1.4.0
|
|
References
Frequently Asked Questions
What is CVE-2026-59161?
Which Excelize versions are affected?
What version fixes CVE-2026-59161?
Is github.com/xuri/excelize/v2 affected?
Is github.com/xuri/excelize affected?
What CWE is associated with CVE-2026-59161?
How can I check Go dependencies for CVE-2026-59161?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-q5j5-6p94-4gwc",
"modified" : "2026-09-10T15:09:09Z",
"published" : "2026-09-10T15:09:09Z",
"aliases" : [ "CVE-2026-59161" ],
"summary" : "Excelize: Streaming GetRows row-bound bypass causes attacker-controlled allocation",
"details" : "# Streaming GetRows row-bound bypass causes attacker-controlled allocation\n\n## Summary\n\nExcelize'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.\n\nThis was reproduced on the current default branch commit `1213a8bd7c5ab360554603ac5c995ccaf6eb4314` and the latest release tag `v2.10.1` (`5ad5ab3af0054c55bdce09f1530085600e9f2e45`).\n\n## Affected package\n\n- Package: `github.com/xuri/excelize/v2`\n- Tested affected versions: current default branch at `1213a8bd7c5ab360554603ac5c995ccaf6eb4314`, and release `v2.10.1`\n- Fixed version: none known at the time of this report\n\n## Impact\n\nAn 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.\n\nIn 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.\n\n## Root cause\n\nThe checked parser path validates row numbers:\n\n- `excelize.go`: `checkRowNum(r int)` rejects negative rows and rows greater than `TotalRows`.\n- `excelize.go`: `checkSheet()` calls `checkRowNum(r.R)` before allocating sheet rows.\n- `workSheetReader()` invokes `checkSheet()` / `checkRow()` before returning a cached worksheet.\n\nThe streaming path does not use that checked parser:\n\n- `rows.go`: `Rows(sheet)` opens an XML decoder directly.\n- `Rows.Next()` accepts the row `r` attribute and assigns it to the iterator's current row without applying `checkRowNum()`.\n- `Rows.Columns()` also assigns row `r` to the iterator state without applying `checkRowNum()`.\n- `GetRows()` appends empty row slices for the gap between the previous row and the current row.\n\nBecause 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.\n\n## Minimal worksheet payload\n\n```xml\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">\n <sheetData>\n <row r=\"2000000\"><c t=\"s\"><v>0</v></c></row>\n </sheetData>\n</worksheet>\n```\n\nThe workbook also contains a normal `sharedStrings.xml` with one string (`ok`).\n\n## Reproduction\n\nA minimal Go harness creates the XLSX in memory and calls `GetRows(\"Sheet1\")`:\n\n```go\nrows, err := f.GetRows(\"Sheet1\")\nfmt.Println(\"rows_len:\", len(rows))\nif len(rows) > 0 {\n fmt.Println(\"last_row:\", rows[len(rows)-1])\n}\nfmt.Printf(\"returned error: %T %v\\n\", err, err)\n```\n\nObserved output on current default branch commit `1213a8bd7c5ab360554603ac5c995ccaf6eb4314`:\n\n```text\n== streaming GetRows row r=2000000 cell without r ==\nrows_len: 2000000\nlast_row: [ok]\nreturned error: <nil> <nil>\nelapsed=21ms alloc_delta=46MB\n```\n\nObserved output on latest release tag `v2.10.1`:\n\n```text\n== streaming GetRows row r=2000000 cell without r ==\nrows_len: 2000000\nlast_row: [ok]\nreturned error: <nil> <nil>\nelapsed=14ms alloc_delta=46MB\n```\n\nA 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.\n\n## Expected behavior\n\n`Rows` / `GetRows` should reject row numbers greater than `TotalRows` with the same error behavior as the checked parser path.\n\n## Suggested remediation\n\n- Apply the same row-bound validation in the streaming reader immediately after parsing a row `r` attribute.\n- Preserve and return row parsing errors from `GetRows()` instead of silently continuing or returning only `Rows.Close()` errors.\n- Add regression tests for `GetRows()` on a worksheet containing `row r=\"1048577\"` with a cell value but no cell `r` coordinate.",
"severity" : [ {
"type" : "CVSS_V4",
"score" : "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"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Go",
"name" : "github.com/xuri/excelize/v2"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "2.11.0"
} ]
} ]
}, {
"package" : {
"ecosystem" : "Go",
"name" : "github.com/xuri/excelize"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"last_affected" : "1.4.0"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/qax-os/excelize/security/advisories/GHSA-q5j5-6p94-4gwc"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-59161"
}, {
"type" : "WEB",
"url" : "https://github.com/qax-os/excelize/pull/2331"
}, {
"type" : "WEB",
"url" : "https://github.com/qax-os/excelize/commit/93f0b3caed37f21ef5079e3259c6c21dcfe68453"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/qax-os/excelize"
}, {
"type" : "WEB",
"url" : "https://github.com/qax-os/excelize/releases/tag/v2.11.0"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-400", "CWE-770" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-09-10T15:09:09Z",
"nvd_published_at" : "2026-07-10T17:17:02Z"
}
}