{"id":"GHSA-2rqm-h6hv-g79v","kind":"vulnerability","aliases":["GHSA-2rqm-h6hv-g79v"],"related":[],"title":"odf-kit has HTML injection leading to XSS in odsToHtml() via unescaped style attribute values","description":"### Summary\n\nODF style values are interpolated into `style=\"...\"` attributes without HTML-escaping in odf-kit's ODS to-HTML renderer (`odsToHtml()`). A double-quote character inside an attacker-controlled style value breaks out of the attribute, allowing arbitrary HTML/attribute injection — and therefore script execution (XSS) — from a crafted `.ods` file wherever the rendered output is inserted as HTML, which is the documented purpose of the function.\n\n### Details\n\nTwo emission sites in `src/ods/to-html/html-renderer.ts` (before 0.14.0, `src/ods-reader/html-renderer.ts`) write document-controlled values into a `style` attribute without escaping:\n\n- **Cell style.** `buildInlineStyle()` assembles a CSS string from the cell's formatting properties (`fo:color`, `fo:font-size`, `fo:font-family`, `fo:background-color`, `fo:text-align`, etc.) with no escaping, and `renderCell` emits it raw as the cell's `style` attribute.\n- **Row height.** `row.height` (from `style:row-height`) is emitted raw into the row element's `style` attribute.\n\nThe same defect in the ODT renderer (`odtToHtml()`) is tracked as GHSA-3cgg-c5pp-57h6 (CVE-2026-113787).\n\n### Impact\n\nAny consumer that renders `odsToHtml()` output as HTML gets XSS from a crafted `.ods` file they convert: untrusted file → arbitrary tags and attributes → script execution in the host page with the page's origin.\n\n### Patches\n\nFixed in 0.14.2. Both sites now route document-controlled values through a shared attribute-escaping helper:\n\n- [`1424227`](https://github.com/GitHubNewbie0/odf-kit/commit/1424227d1ca2716a249b623d1456575ff0331dcc): escape style attribute values in the ODS HTML renderer\n- [`aa64c73`](https://github.com/GitHubNewbie0/odf-kit/commit/aa64c734ebd7e7be40e35fcda83b1144193c3da2): the shared `escapeAttr` helper\n\nUsers should upgrade to 0.14.2 or later.\n\n### Credit\n\nReported by @PethuKannan.","summary":"odf-kit's odsToHtml() writes spreadsheet style values into an HTML style attribute without escaping, so a double quote in a crafted .ods file breaks out of the attribute. If the rendered HTML is inserted into a page, an attacker can inject tags and attributes and run script in that page's origin.","severity":"medium","cvss":6.1,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"odf-kit","fixed":["0.14.2"],"affected":">= 0.9.8, < 0.14.2","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cwe-79","javascript","npm","odf-kit","package","web-apps"],"not_affected_if":["no-odstohtml-usage","output-never-rendered-as-html","trusted-files-only","version-0.14.2-or-later"],"how_to_check":"Run `npm ls odf-kit` to find the installed version; affected versions are >= 0.9.8 and < 0.14.2.\nFind code that calls odsToHtml( and check whether its output is inserted into a web page as HTML.\nCheck whether those .ods files can come from untrusted users.","fix":"Upgrade odf-kit to 0.14.2 or later.\nIf you cannot upgrade right away, do not insert odsToHtml() output into pages unescaped, or sanitize it before rendering.","status":"open","published_at":"2026-10-23T20:55:31+00:00","first_seen_at":"2026-10-23T20:59:05+00:00","triaged_at":"2026-10-23T21:00:22+00:00","updated_at":"2026-10-23T21:00:22+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-10-23T21:00:22Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade odf-kit to 0.14.2 or later.\nIf you cannot upgrade right away, do not insert odsToHtml() output into pages unescaped, or sanitize it before rendering.","reach":"internet","impact":"high","summary":"odf-kit's odsToHtml() writes spreadsheet style values into an HTML style attribute without escaping, so a double quote in a crafted .ods file breaks out of the attribute. If the rendered HTML is inserted into a page, an attacker can inject tags and attributes and run script in that page's origin.","category":"package","who_cares":["npm","javascript","odf-kit","web-apps"],"confidence":0.85,"needs_auth":"no","worst_case":"Attacker runs script in the host page origin via crafted spreadsheet","analyst_note":"","how_to_check":"Run `npm ls odf-kit` to find the installed version; affected versions are >= 0.9.8 and < 0.14.2.\nFind code that calls odsToHtml( and check whether its output is inserted into a web page as HTML.\nCheck whether those .ods files can come from untrusted users.","needs_analyst":false,"not_affected_if":["version-0.14.2-or-later","no-odsToHtml-usage","output-never-rendered-as-html","trusted-files-only"],"user_interaction":"yes","needs_special_setup":"no","severity_if_unknown":"medium"},"cost_usd":0.000594,"input_tokens":3518,"output_tokens":485,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-113787':268C '-2026':267C '-79':31B '/githubnewbie0/odf-kit/commit/1424227d1ca2716a249b623d1456575ff0331dcc):':326C '/githubnewbie0/odf-kit/commit/aa64c734ebd7e7be40e35fcda83b1144193c3da2):':339C '0.14.0':173C '0.14.2':307C,348C '1424227':323C '2rqm':19A,24A '3cgg':263C '57h6':265C 'aa64c73':336C 'align':216C 'allow':136C 'app':40B 'arbitrari':137C,289C 'assembl':189C 'attack':81B,127C 'attacker-control':126C 'attribut':15A,54B,70B,86B,103C,135C,183C,231C,249C,292C,320C,329C 'attribute-escap':319C 'background':211C 'background-color':210C 'break':66B,131C 'buildinlinestyl':188C 'c5pp':264C 'cell':186C,195C,228C 'charact':123C 'color':200C,212C 'consum':271C 'control':128C,178C,314C 'convert':286C 'craft':63B,147C,282C 'credit':351C 'css':191C 'cve':266C 'cwe':30B 'defect':252C 'detail':166C 'document':161C,177C,313C 'document-control':176C,312C 'doubl':59B,121C 'double-quot':120C 'element':246C 'emiss':168C 'emit':223C,241C 'escap':56B,107C,185C,220C,321C,327C 'escapeattr':342C 'etc':217C 'execut':143C,294C 'famili':208C 'file':65B,149C,284C,288C 'fix':305C 'fo':199C,201C,205C,209C,213C 'font':203C,207C 'font-famili':206C 'font-siz':202C 'format':197C 'function':165C 'g79v':21A,26A 'get':278C 'ghsa':18A,23A,262C 'ghsa-2rqm-h6hv-g79v':17A,22A 'ghsa-3cgg-c5pp-57h6':261C 'github.com':325C,338C 'github.com/githubnewbie0/odf-kit/commit/1424227d1ca2716a249b623d1456575ff0331dcc):':324C 'github.com/githubnewbie0/odf-kit/commit/aa64c734ebd7e7be40e35fcda83b1144193c3da2):':337C 'h6hv':20A,25A 'height':233C,239C 'helper':322C,343C 'host':297C 'html':5A,52B,74B,106C,116C,157C,277C,334C 'html-escap':105C 'html/attribute':138C 'impact':269C 'inject':6A,83B,139C 'insert':76B,155C 'insid':124C 'interpol':100C 'javascript':32B 'kit':3A,29A,36B,43B,111C 'later':350C 'lead':7A 'npm':33B 'od':64B,113C,148C,283C,333C 'odf':2A,28A,35B,42B,96C,110C 'odf-kit':1A,27A,34B,41B,109C 'odstohtml':11A,45B,118C,274C 'odt':255C 'odttohtml':257C 'origin':94B,303C 'output':153C,275C 'packag':37B 'page':79B,92B,298C,301C 'patch':304C 'pethukannan':354C 'properti':198C 'purpos':162C 'quot':60B,122C 'raw':225C,242C 'render':73B,117C,152C,256C,273C,335C 'rendercel':222C 'report':352C 'rout':311C 'row':232C,238C,245C 'row-height':237C 'row.height':234C 'run':88B 'script':89B,142C,293C 'share':318C,341C 'site':169C,309C 'size':204C 'spreadsheet':47B 'src/ods-reader/html-renderer.ts':174C 'src/ods/to-html/html-renderer.ts':171C 'string':192C 'style':14A,48B,53B,97C,102C,129C,182C,187C,230C,236C,248C,328C 'summari':95C 'tag':84B,290C 'text':215C 'text-align':214C 'therefor':141C 'to-html':114C 'track':259C 'two':167C 'unescap':13A 'untrust':287C 'upgrad':346C 'user':344C 'valu':16A,49B,98C,130C,179C,315C,330C 'via':12A 'web':39B 'web-app':38B 'wherev':150C 'without':55B,104C,184C 'write':46B,175C 'xss':9A,144C,279C","nicknames":[],"mitigations":[],"who_affected":null,"tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-23T20:55:31+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-2rqm-h6hv-g79v","url":"https://github.com/advisories/GHSA-2rqm-h6hv-g79v","severity":"medium","published_at":"2026-10-23T20:55:31+00:00","modified_at":"2026-10-23T20:55:33+00:00","first_seen_at":"2026-10-23T20:59:05+00:00","fetched_at":"2026-10-23T20:59:05+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}