Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹 Go
Not in CISA KEV

Excelize: Unbounded <col max> attribute is loaded with no MaxColumns check and expanded per-column by flatCols(), so any column mutator hangs or OOMs the processGHSA-fq3v-74gv-27gm

Fix: qax-os/excelize#2370

GHSA-fq3v-74gv-27gm is a CWE-789 vulnerability in github.com/xuri/excelize/v2. A fix is available for github.com/xuri/excelize/v2 — see the affected versions and patch details below.

Also known asCVE-2026-107223
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 12, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

No confirmed exploitation observed yet

  • CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.

Exploitation and automatability from CISA’s SSVC triage for GHSA-fq3v-74gv-27gm.

EPSS Exploitation Probability

via FIRST.org ↗
0.3%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs23th percentile — riskier than 23% of all scored CVEsHighest risk

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

Real-World Exposure

1 pkg affected
🐹github.com/xuri/excelize/v2

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

Summary

The max attribute of a <col> element in xl/worksheets/sheetN.xml is read verbatim on open with no check against MaxColumns, and flatCols() then walks Min..Max doing a deepcopy.Copy and append per iteration. Executed: max="300000", only about 18x past the real 16384 limit, cost 46.2 s of CPU and 122 MB on a single SetColWidth call, and the growth is linear in max up to 2^31-1.

Confirmed at ae2113b (HEAD at the time of audit).

Details

col.go:547, flatCols(), with the two loops at :549 and :564:

for i := col.Min; i <= col.Max; i++ {
...
for i := column.Min; i <= column.Max; i++ { ... fc = append(fc, deepcopy.Copy(...)) }

column.Min and column.Max are plain int attributes on xmlWorksheet.go:279-280, bound straight from the file. MaxColumns is 16384 (templates.go:176) and nothing in the parse path compares against it.

The contrast that makes this an oversight rather than a design decision is inside the callers themselves. SetColWidth validates its own column argument through parseColRange, which clamps to MaxColumns. But flatCols iterates ws.Cols.Col, the file-loaded slice, so the caller's validated argument never constrains the loop. The crafted column expands no matter which column the caller touches.

The row axis has the guard this axis is missing: GHSA-h69g-9hx6-f3v4 capped checkSheet's row allocation at TotalRows, and GHSA-q5j5-6p94-4gwc capped streaming Rows.Next. There is no column-axis equivalent.

Public entry points that flatten the existing columns: SetColWidth (col.go:498 into :534), SetColStyle (col.go:431 into :477), SetColVisible (col.go:282 into :313), SetColOutlineLevel (col.go:376 into :407).

PoC

Executed in-package: crafted a real xlsx, rezipped with an injected <cols> block before <sheetData>, opened it with OpenReader, then called SetColWidth.

<cols><col min="1" max="2147483647" width="9"/></cols>
f.SetColWidth("Sheet1", "A", "A", 12)

Measured:

  • max="300000": 46.2 s CPU, 122 MB allocated, ws.Cols.Col grew to 300000 entries, from one call.
  • max="5000000": did not complete in 110 s, killed by the test timeout.

Growth is linear in max. At max=2147483647 that is roughly two billion xlsxCol allocations, which is hundreds of gigabytes and in practice a permanent hang ending in OOM.

Impact

Any service that opens an untrusted spreadsheet and calls a column mutator. The input is a few dozen bytes of XML inside an otherwise ordinary workbook, no authentication is involved beyond whatever gates the upload, and the process is either wedged for minutes or killed by the OOM reaper. Availability only; no read or write primitive here.

Suggested fix

Validate col.Min and col.Max against MinColumns/MaxColumns when parsing the <col> element, or at the top of flatCols, returning ErrColumnNumber for out-of-range values. That mirrors what checkRowNum already does on the row axis.

Why this is not GHSA-h69g-9hx6-f3v4 or GHSA-q5j5-6p94-4gwc

Both of those are row-axis allocation bounds, and both fixes cap at TotalRows. Neither touched <col> parsing or flatCols. This is the same weakness class on the column axis, and it is arguably worse than the two that were fixed, because those had a cap that was bypassed while this one has no cap at all.

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/xuri/excelize/v2≥ 2.1.0&&< 2.11.1-0.20260807015645-a54c578af3092.11.1-0.20260807015645-a54c578af309go get github.com/xuri/excelize/v2@v2.11.1-0.20260807015645-a54c578af309

Affected Products

1 product · 1 configurations
Application
excelizeexcelize
≥ 2.1.0 && ≤ 2.11.0
range

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/xuri/excelize/v2, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update github.com/xuri/excelize/v2 to 2.11.1-0.20260807015645-a54c578af309 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-fq3v-74gv-27gm is resolved across your whole dependency graph.

  3. Workarounds

    Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.

Frequently Asked Questions

### Summary The `max` attribute of a `<col>` element in `xl/worksheets/sheetN.xml` is read verbatim on open with no check against `MaxColumns`, and `flatCols()` then walks `Min..Max` doing a `deepcopy.Copy` and `append` per iteration. Executed: `max="300000"`, only about 18x past the real 16384 limit, cost 46.2 s of CPU and 122 MB on a single `SetColWidth` call, and the growth is linear in `max` up to 2^31-1. Confirmed at `ae2113b` (HEAD at the time of audit). ### Details `col.go:547`, `flatCols()`, with the two loops at `:549` and `:564`: ```go for i := col.Min; i <= col.Max; i++ { ... f
O3 Security · Impact-Aware SCA

Is GHSA-fq3v-74gv-27gm in your dependencies?

Find it across Go, including transitive dependencies.

Excelize: Unbounded <col max> attribute is loaded with…