{"id":"CVE-2026-107222","aliases":["GHSA-rxcj-4pj5-74gr"],"url":"https://o3.security/vulnerability/CVE-2026-107222","summary":"Excelize: GetConditionalFormats indexes conditional-formatting rule sub-elements with no length or nil check","details":"## Summary\n\n`GetConditionalFormats` reads sub-elements of a `<cfRule>` straight out of `xl/worksheets/sheetN.xml` and indexes them without checking length, and in one case without checking for nil. Three rule types are affected: `cellIs`, `dataBar` and `colorScale`. A workbook with a rule that is missing a child a real Excel file would always have panics the call.\n\n## Where it is\n\nAll three sinks are in `styles.go`, at the same line numbers in v2.11.0 and on master `d552a7e`. All three are reached from `GetConditionalFormats` through `styles.go:3271`.\n\n`styles.go:3003`, in `extractCondFmtCellIs`:\n\n```go\n\tformat.Value = c.Formula[0]\n```\n\nThe branch above it handles `len(c.Formula) == 2`; this one is the fallback and does not check that there is a formula at all, so a `cellIs` rule with no `<formula>` child indexes an empty slice.\n\n`styles.go:3132`, in the colorScale extractor:\n\n```go\n\tvalues := len(c.ColorScale.Cfvo)\n```\n\n`c.ColorScale` is a `*xlsxColorScale` and is nil when the `<cfRule type=\"colorScale\">` element has no `<colorScale>` child. Lines 3148 and 3153 then index `Cfvo[1]` and `Cfvo[2]` in the three-colour branch with no length check either.\n\n`styles.go:3186`, `:3188` and `:3190`, in the dataBar extractor:\n\n```go\n\t\tformat.MinType = c.DataBar.Cfvo[0].Type\n\t\t...\n\t\tformat.BarColor = \"#\" + f.getThemeColor(c.DataBar.Color[0])\n```\n\nThe guard here is `c.DataBar != nil`, which says nothing about the length of `Cfvo` or `Color`, so an empty `<dataBar></dataBar>` element reaches all three.\n\n## Who the attacker is\n\nAnyone who can hand a spreadsheet to a service that opens it and calls `GetConditionalFormats`. No authentication, no user interaction beyond the service doing its normal job, and the file is small.\n\n## Reproduction\n\nThree minimal `.xlsx` files were built, each a real zip with `[Content_Types].xml`, `_rels/.rels`, `xl/workbook.xml`, `xl/_rels/workbook.xml.rels` and one worksheet, opened each with the public `excelize.OpenReader` and called `GetConditionalFormats(\"Sheet1\")`. Nothing internal is touched.\n\nThe worksheet fragment for the `cellIs` case, note there is no `<formula>` child:\n\n```xml\n<conditionalFormatting sqref=\"A1\"><cfRule type=\"cellIs\" operator=\"equal\" priority=\"1\" dxfId=\"0\"/></conditionalFormatting>\n```\n\nfor `dataBar`:\n\n```xml\n<conditionalFormatting sqref=\"A1\"><cfRule type=\"dataBar\" priority=\"1\"><dataBar></dataBar></cfRule></conditionalFormatting>\n```\n\nand for `colorScale`:\n\n```xml\n<conditionalFormatting sqref=\"A1\"><cfRule type=\"colorScale\" priority=\"1\"/></conditionalFormatting>\n```\n\nObserved against master `d552a7e` on go1.26.5:\n\n```text\ncellIs      panic: runtime error: index out of range [0] with length 0\n              styles.go:3003\n              styles.go:1217\n              styles.go:3271\n\ndataBar     panic: runtime error: index out of range [0] with length 0\n              styles.go:3186\n              styles.go:1262\n              styles.go:3271\n\ncolorScale  panic: runtime error: invalid memory address or nil pointer dereference\n              styles.go:3132\n              styles.go:1259\n              styles.go:3271\n```\n\nOne honesty note on impact. These are ordinary Go panics, not fatal errors, so a caller that wraps the call in `recover` survives them.\n\n## Suggested fix\n\nLength-check `c.Formula` before line 3003 and return the rule with an empty value when there is no formula. Nil-check `c.ColorScale` before line 3132 and length-check `ColorScale.Cfvo` before indexing 1 and 2. Length-check `DataBar.Cfvo` and `DataBar.Color` alongside the existing nil check at 3186 to 3190. Skipping the malformed rule rather than erroring would keep `GetConditionalFormats` usable on files that are merely sloppy.\n\n## Affected versions\n\n`github.com/xuri/excelize/v2` up to and including v2.11.0, and master at `d552a7e`. I read the three sinks at tag v2.11.0 and at master and ran the reproducers against master.","published":"2026-10-07T18:45:20.249Z","modified":"2026-10-21T02:31:00.177721303Z","cvss":{"score":6.5,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H"},"epss":null,"cisaKev":null,"exploitsKnown":null,"affectedPackages":[{"ecosystem":"Go","name":"github.com/xuri/excelize/v2","fixedVersion":"2.11.1-0.20260812075026-be7a16390fa6"}],"fix":{"url":"https://github.com/qax-os/excelize/commit/be7a16390fa69c71d3ca618c741d1a2b5ed362cd","label":"qax-os/excelize@be7a163"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/107xxx/CVE-2026-107222.json"},{"type":"ADVISORY","url":"https://github.com/qax-os/excelize/security/advisories/GHSA-rxcj-4pj5-74gr"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-107222"},{"type":"FIX","url":"https://github.com/qax-os/excelize/commit/be7a16390fa69c71d3ca618c741d1a2b5ed362cd"},{"type":"FIX","url":"https://github.com/qax-os/excelize/pull/2375"},{"type":"PACKAGE","url":"https://github.com/qax-os/excelize"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-21T02:31:00.177721303Z"}}