{"id":"GHSA-rxcj-4pj5-74gr","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 `\u003ccfRule\u003e` 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 `\u003cformula\u003e` 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 `\u003ccfRule type=\"colorScale\"\u003e` element has no `\u003ccolorScale\u003e` 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 `\u003cdataBar\u003e\u003c/dataBar\u003e` 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 `\u003cformula\u003e` child:\n\n```xml\n\u003cconditionalFormatting sqref=\"A1\"\u003e\u003ccfRule type=\"cellIs\" operator=\"equal\" priority=\"1\" dxfId=\"0\"/\u003e\u003c/conditionalFormatting\u003e\n```\n\nfor `dataBar`:\n\n```xml\n\u003cconditionalFormatting sqref=\"A1\"\u003e\u003ccfRule type=\"dataBar\" priority=\"1\"\u003e\u003cdataBar\u003e\u003c/dataBar\u003e\u003c/cfRule\u003e\u003c/conditionalFormatting\u003e\n```\n\nand for `colorScale`:\n\n```xml\n\u003cconditionalFormatting sqref=\"A1\"\u003e\u003ccfRule type=\"colorScale\" priority=\"1\"/\u003e\u003c/conditionalFormatting\u003e\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.","aliases":["CVE-2026-107222"],"modified":"2026-10-07T20:30:05.876273601Z","published":"2026-10-07T20:22:54Z","database_specific":{"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-10-07T20:22:54Z","nvd_published_at":null,"cwe_ids":["CWE-129","CWE-476"]},"references":[{"type":"WEB","url":"https://github.com/qax-os/excelize/security/advisories/GHSA-rxcj-4pj5-74gr"},{"type":"WEB","url":"https://github.com/qax-os/excelize/pull/2375"},{"type":"WEB","url":"https://github.com/qax-os/excelize/commit/be7a16390fa69c71d3ca618c741d1a2b5ed362cd"},{"type":"PACKAGE","url":"https://github.com/qax-os/excelize"}],"affected":[{"package":{"name":"github.com/xuri/excelize/v2","ecosystem":"Go","purl":"pkg:golang/github.com/xuri/excelize/v2"},"ranges":[{"type":"SEMVER","events":[{"introduced":"2.7.0"},{"fixed":"2.11.1-0.20260812075026-be7a16390fa6"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-rxcj-4pj5-74gr/GHSA-rxcj-4pj5-74gr.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H"}]}