{"schema":"threatline.record/2","record":{"id":"CVE-2026-107222","aliases":["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 `<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.","categories":["Libraries & packages"],"product":"github.com/xuri/excelize/v2","vendor":"qax-os","ecosystem":"go","affected_ranges":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-rxcj-4pj5-74gr","source_modified_at":"2026-10-07T20:22:57Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"go","name":"github.com/xuri/excelize/v2"},"range":">= 2.7.0, < 2.11.1-0.20260812075026-be7a16390fa6"},{"source":"CVE List","source_url":"https://www.cve.org/CVERecord?id=CVE-2026-107222","source_modified_at":"2026-10-08T16:09:37.888Z","fetched_at":"2026-10-23T18:45:31.882637Z","product":"excelize","vendor":"qax-os","versions":[{"version":">= 2.7.0, <= 2.11.0","status":"affected"}],"defaultStatus":null},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-rxcj-4pj5-74gr","source_modified_at":"2026-10-07T20:30:05.876273601Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"Go","name":"github.com/xuri/excelize/v2","purl":"pkg:golang/github.com/xuri/excelize/v2"},"ranges":[{"events":[{"introduced":"2.7.0"},{"fixed":"2.11.1-0.20260812075026-be7a16390fa6"}],"type":"SEMVER"}],"versions":[]}],"fixed_versions":[{"source":"GitHub Advisory Database","source_url":"https://github.com/advisories/GHSA-rxcj-4pj5-74gr","source_modified_at":"2026-10-07T20:22:57Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"go","name":"github.com/xuri/excelize/v2"},"version":"2.11.1-0.20260812075026-be7a16390fa6"},{"source":"OSV","source_url":"https://osv.dev/vulnerability/GHSA-rxcj-4pj5-74gr","source_modified_at":"2026-10-07T20:30:05.876273601Z","fetched_at":"2026-10-23T18:42:43.628194Z","package":{"ecosystem":"Go","name":"github.com/xuri/excelize/v2","purl":"pkg:golang/github.com/xuri/excelize/v2"},"version":"2.11.1-0.20260812075026-be7a16390fa6"}],"cvss":6.5,"severity":"Medium","kev":{"listed":false},"epss":{"probability":0.00252,"percentile":0.15747,"score_date":"2026-10-23"},"published_at":"2026-10-07T18:45:20.249Z","modified_at":"2026-10-08T16:09:37.888Z","provenance":[{"source":"GitHub Advisory Database","url":"https://github.com/advisories/GHSA-rxcj-4pj5-74gr","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-07T20:22:57Z"},{"source":"CVE List","url":"https://www.cve.org/CVERecord?id=CVE-2026-107222","fetched_at":"2026-10-23T18:45:31.882637Z","source_modified_at":"2026-10-08T16:09:37.888Z"},{"source":"OSV","url":"https://osv.dev/vulnerability/GHSA-rxcj-4pj5-74gr","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-07T20:30:05.876273601Z"},{"source":"EPSS","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-107222","fetched_at":"2026-10-23T18:42:43.628194Z","source_modified_at":"2026-10-23"}],"sources":["GitHub Advisory Database","CVE List","OSV","EPSS"],"references":[{"source":"CVE List","url":"https://github.com/qax-os/excelize/security/advisories/GHSA-rxcj-4pj5-74gr","tags":["exploit"]},{"source":"CVE List","url":"https://github.com/qax-os/excelize/pull/2375","tags":["x_refsource_MISC"]},{"source":"CVE List","url":"https://github.com/qax-os/excelize/commit/be7a16390fa69c71d3ca618c741d1a2b5ed362cd","tags":["x_refsource_MISC"]},{"url":"https://github.com/advisories/GHSA-rxcj-4pj5-74gr","source":"GitHub Advisory Database"}],"locations":[],"motoko_assessment":{"status":"requires_architecture_review","automatic_remediation":false},"cve_state":"PUBLISHED","ssvc":{"exploitation":"poc","automatable":"no","technical_impact":"partial","assessed_at":"2026-10-08T16:09:18.703933Z"},"classification":{"method":"product-first software-layer rules","rules_version":"2.1","matched_on":"ecosystem","confidence":"heuristic","reviewed":false},"change_timestamp":"2026-10-08T16:09:37.888Z","content_hash":"d65de217ddf575210a5cbedc99b6e5d52097b95df6fed79e16be83b73d8348c7"}}
