CVE-2026-107223: 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 process

Published Oct 7, 2026
·
Updated

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++ { ... 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.

xml <cols><col min="1" max="2147483647" width="9"/></cols>

go 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.

Other sources

Excelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.1.0 to 2.11.0, flatCols expands file-loaded column ranges without validating Min and Max against the worksheet column limit. SetColWidth reaches flatCols, which expands xlsxCol.Min through xlsxCol.Max without enforcing MaxColumns. When a crafted worksheet supplies an oversized col max attribute and the application invokes a column mutator, flatCols performs a deep copy and append for every attacker-selected column number, allowing an attacker to consume excessive CPU and memory or trigger OOM. No fixed version is available as of this review.

— MITRE

Affected Software

2 affected componentsFixes available
Excelize Excelize>=2.1.0<=2.11.0
go/github.com/xuri/excelize/v2>=2.1.0<2.11.1-0.20260807015645-a54c578af309
2.11.1-0.20260807015645-a54c578af309

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade go/github.com/xuri/excelize/v2 to a version that resolves this vulnerability.

    Fixed in 2.11.1-0.20260807015645-a54c578af309
  2. Compensating control

    Validate col.Min and col.Max against MinColumns and MaxColumns when parsing the <col> element or at the start of flatCols; return ErrColumnNumber for out-of-range values.

Event History

Oct 7, 2026
CVE Published
via MITRE·06:48 PM
Data Sourced
via MITRE·06:48 PM
DescriptionWeakness
Data Sourced
via NVD·07:17 PM
DescriptionSeverityWeakness
Advisory Published
via GitHub·08:22 PM
Data Sourced
via GitHub·08:22 PM
DescriptionWeaknessAffected Software

Frequently Asked Questions

1

Which applications are exposed to this denial-of-service condition?

Applications using Excelize versions 2.1.0 through 2.11.0 are exposed when they process an attacker-crafted worksheet and invoke a column mutator. The impact is excessive CPU or memory consumption, potentially ending in an out-of-memory process failure.

2

Does processing a crafted workbook alone trigger the issue?

The vulnerable expansion occurs when a column mutator reaches SetColWidth and calls flatCols. The provided information specifically identifies invocation of a column mutator as the condition that causes the oversized column range to be expanded.

3

Is a fixed Excelize release available?

No fixed version was available as of the review. The affected range is identified as Excelize 2.1.0 through 2.11.0.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203