CVE-2026-107225: Excelize: GetStyle panics on a negative fillId, borderId or fontId in styles.xml

Published Oct 7, 2026
·
Updated

Summary

File.GetStyle indexes the fill, border and font tables with values taken straight out of xl/styles.xml, and the conditions gating those lookups check only the upper bound. A workbook whose cellXfs entry carries fillId="-1", borderId="-1" or fontId="-1" reaches a negative slice index and panics. excelize has no recover(), so the panic leaves GetStyle and takes the calling process with it.

Same defect class as GHSA-48hm-4h8j-58fg, the negative shared-string index, in a different file. That one was fixed by adding the missing lower bound; these three sites still lack it.

Where it is

styles.go, in GetStyle, lines 1683, 1686 and 1689:

go xf := s.CellXfs.Xf[idx] if extractStyleCondFuncs"fill" { f.extractFills(s.Fills.Fill[xf.FillID], s, style) } if extractStyleCondFuncs"border" { f.extractBorders(s.Borders.Border[xf.BorderID], s, style) } if extractStyleCondFuncs"font" { style.Font = extractFont(s.Fonts.Font[xf.FontID]) }

The conditions, at lines 1171 to 1185, bound only the top:

go "fill": func(xf xlsxXf, s xlsxStyleSheet) bool { return (xf.ApplyFill == nil || (xf.ApplyFill != nil && xf.ApplyFill)) && xf.FillID != nil && s.Fills != nil && xf.FillID < len(s.Fills.Fill) },

xf.FillID < len(...) is satisfied by any negative value. FillID, BorderID and FontID are int unmarshalled directly from the fillId, borderId and fontId attributes, so the value is whatever the file says. The border and font conditions have the same shape.

The correct pattern is already in the same function, six lines above at 1677:

go if idx < 0 || s.CellXfs == nil || len(s.CellXfs.Xf) <= idx { return style, newInvalidStyleID(idx) }

and again at 1783. So the style index itself is guarded on both sides while the three ids it leads to are not.

Proof of concept

Executed against master at f98df08, which is the merge of #2366, so this is current rather than historical. The harness builds a normal styled workbook with excelize, rewrites one attribute of the cellXfs entry inside the zip to -1, reopens it and calls GetCellStyle then GetStyle:

control (all >= 0) ok style=true err=<nil> fillId=-1 PANIC: runtime error: index out of range [-1] borderId=-1 PANIC: runtime error: index out of range [-1] fontId=-1 PANIC: runtime error: index out of range [-1]

The control confirms the harness reads a valid file correctly, so the three panics are the negative ids rather than a broken fixture. Worth mentioning because my first attempt at this harness patched only fillId and appeared to show the other two were fine; they are not, the replacement had simply missed them.

Impact

Any application that opens an untrusted .xlsx and reads cell styling crashes. GetStyle is reached through GetCellStyle and from the style-copying and rendering paths, so it sits on the ordinary read path. With no recover() anywhere in excelize, a CLI or worker exits and a service returns 500 per request unless the caller installed its own recovery middleware.

No memory corruption and no information disclosure. Availability only.

Suggested fix

Add the lower bound to each of the three conditions, matching what line 1677 already does for the style index:

go xf.FillID >= 0 && xf.FillID < len(s.Fills.Fill)

and the same for BorderID and FontID. Three one-line changes in extractStyleCondFuncs.

Other sources

Excelize is a Go language library for reading and writing Microsoft Excel spreadsheets. From 2.8.0 to 2.11.0, GetStyle's fill, border, and font extraction predicates check only upper bounds for attacker-controlled style-table indices. File.GetStyle relies on extractStyleCondFuncs predicates that allow negative FillID, BorderID, and FontID values to reach slice indexing. When a crafted styles.xml supplies a negative fillId, borderId, or fontId and the application reads the style, a negative identifier passes the upper-bound-only predicate and becomes a negative slice index, allowing an attacker to panic while reading cell styling. No fixed version is available as of this review.

— MITRE

Affected Software

2 affected componentsFixes available
Excelize Excelize>=2.8.0<=2.11.0
go/github.com/xuri/excelize/v2>=2.8.0<2.11.1-0.20260731010303-ae2113b410e5
2.11.1-0.20260731010303-ae2113b410e5

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.20260731010303-ae2113b410e5
  2. Compensating control

    In excelize's styles.go GetStyle extractStyleCondFuncs, add lower-bound checks for FillID, BorderID, and FontID so each predicate requires the identifier to be >= 0 as well as below the corresponding table length, matching the existing style-index guard.

  3. Compensating control

    Install caller-side recovery middleware when opening untrusted .xlsx files to prevent a GetStyle panic from terminating the process or producing an unhandled 500 response.

Event History

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

Frequently Asked Questions

1

Which deployments are exposed?

Applications using Excelize versions 2.8.0 through 2.11.0 are affected when they read workbook styling from attacker-controlled or otherwise untrusted Excel files.

2

What must an attacker provide to trigger the issue?

The attacker needs a crafted styles.xml containing a negative fillId, borderId, or fontId, and the application must read a cell style that causes GetStyle to process that identifier. This can panic the application while it reads cell styling.

3

Is a fixed release available?

No fixed version was available as of the review. Until a fix is available, avoid processing untrusted spreadsheets with affected Excelize versions or isolate spreadsheet parsing so a panic cannot disrupt the primary service.

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