{"id":"CVE-2026-104844","kind":"vulnerability","aliases":["CVE-2026-104844","GHSA-rj75-hqrm-r3gf"],"related":[],"title":"PostCSS: Quadratic complexity in flat selector parsing allows CPU exhaustion","description":"### Impact\n\n`.` and `#` are not word delimiters in the tokenizer, so a flat selector such as\n`.a.a.a...` reaches `splitWord()` as a single word token carrying n class or id\nindexes. Three passes scanned those index arrays linearly for every index,\nmaking the parse O(n^2) in the number of indexes rather than in input length:\n`uniqs()`, the `indices.forEach` loop, and the Sass-interpolation filter.\nParsing a 400 KB flat selector took ~34 s on a modern laptop, fully occupying a\nsingle thread. A benign selector of identical byte size parses in tens of\nmilliseconds, so the cost is driven by the index count, not the input size.\nThe nesting depth of such a selector is 0, so the `maxNestingDepth` guard added\nin 7.1.3 offers no protection.\n\nReachability is deployment dependent. Only consumers that parse untrusted,\nattacker-supplied selectors synchronously in a request path are exposed, for\nexample CSS sanitizers, CSS-in-JS services and online playgrounds. Ordinary\nbuild-time use on trusted sources is not affected.\n\n### Patches\n\nFixed in 7.1.6. The three passes now use Set membership tests, making parsing\nlinear in the number of indexes. There is no behaviour change: parsing is\nbyte-identical on a differential corpus of 8413 selectors.\n\n### Workarounds\n\nCap the size of selectors accepted from untrusted sources before parsing.","summary":"postcss-selector-parser parses a long flat selector such as repeated class names in quadratic time, so a 400 KB input occupies a thread for about 34 seconds. An attacker who can submit selectors to a parsing service can exhaust CPU and degrade availability.","severity":"medium","cvss":5.9,"epss":0.00389,"exploited":false,"exploited_at":null,"products":[{"name":"postcss-selector-parser","fixed":["7.1.6"],"affected":">= 7.0.0, < 7.1.6","ecosystem":"npm"},{"name":"postcss-selector-parser","fixed":["6.1.5"],"affected":"< 6.1.5","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["css-in-js","css-sanitizer","cwe-400","cwe-407","node","npm","package","postcss"],"not_affected_if":["build-time-only","trusted-input"],"how_to_check":"- Run `npm ls postcss-selector-parser` to list every installed copy and version.\n- Check whether any service parses CSS selectors from untrusted input in a request path, such as a CSS sanitizer or online playground.","fix":"- Upgrade `postcss-selector-parser` to `7.1.6` on the 7.x line or `6.1.5` on the 6.x line.\n- Until patched, cap the size of selectors accepted from untrusted sources before parsing.","status":"open","published_at":"2026-10-02T16:16:46+00:00","first_seen_at":"2026-10-26T14:11:12+00:00","triaged_at":"2026-10-26T14:13:13+00:00","updated_at":"2026-10-26T14:30:37+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-26T14:13:13Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade `postcss-selector-parser` to `7.1.6` on the 7.x line or `6.1.5` on the 6.x line.\n- Until patched, cap the size of selectors accepted from untrusted sources before parsing.","reach":"internet","title":"postcss-selector-parser: flat selectors cause CPU exhaustion from quadratic parsing","impact":"low","summary":"postcss-selector-parser parses a long flat selector such as repeated class names in quadratic time, so a 400 KB input occupies a thread for about 34 seconds. An attacker who can submit selectors to a parsing service can exhaust CPU and degrade availability.","category":"package","nicknames":[],"who_cares":["npm","node","css-sanitizer","css-in-js","postcss"],"confidence":0.8,"needs_auth":"unknown","worst_case":"CPU exhaustion from a single crafted selector stalls a parsing thread","analyst_note":"","how_to_check":"- Run `npm ls postcss-selector-parser` to list every installed copy and version.\n- Check whether any service parses CSS selectors from untrusted input in a request path, such as a CSS sanitizer or online playground.","needs_analyst":false,"not_affected_if":["build-time-only","trusted-input"],"who_is_affected":"Only applications that parse attacker-supplied selectors synchronously in a request path are exposed, such as CSS sanitizers, CSS-in-JS services and online playgrounds. Ordinary build-time parsing of trusted source files is not affected.","user_interaction":"no","needs_special_setup":"no","severity_if_unknown":"unknown"},"cost_usd":0.000665,"input_tokens":3811,"output_tokens":568,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-104844':13A,21A '-2026':12A,20A '-400':38B '-407':40B '0':206C '2':134C '34':72B,162C '400':64B,157C '7.1.3':213C '7.1.6':263C '8413':295C 'a.a.a':105C 'accept':303C 'add':211C 'affect':259C 'allow':8A 'array':124C 'attack':75B,227C 'attacker-suppli':226C 'avail':89B 'behaviour':283C 'benign':174C 'build':251C 'build-tim':250C 'byte':178C,288C 'byte-ident':287C 'cap':298C 'carri':113C 'chang':284C 'class':57B,115C 'complex':3A 'consum':222C 'corpus':293C 'cost':187C 'count':193C 'cpu':9A,86B 'css':31B,35B,239C,242C 'css-in-j':30B,241C 'css-sanit':34B 'cve':11A,19A 'cwe':37B,39B 'degrad':88B 'delimit':95C 'depend':220C 'deploy':219C 'depth':200C 'differenti':292C 'driven':189C 'everi':127C 'exampl':238C 'exhaust':10A,85B 'expos':236C 'filter':154C 'fix':261C 'flat':5A,52B,101C,159C 'fulli':168C 'ghsa':15A 'ghsa-rj75-hqrm-r3gf':14A 'guard':210C 'hqrm':17A 'id':117C 'ident':177C,289C 'impact':90C 'index':118C,123C,128C,139C,192C,279C 'indices.foreach':147C 'input':66B,143C,196C 'interpol':153C 'js':33B,244C 'kb':65B,158C 'laptop':167C 'length':144C 'linear':125C,274C 'long':51B 'loop':148C 'make':129C,272C 'maxnestingdepth':209C 'membership':270C 'millisecond':184C 'modern':166C 'n':114C,133C 'name':58B 'nest':199C 'node':41B 'npm':42B 'number':137C,277C 'o':132C 'occupi':67B,169C 'offer':214C 'onlin':247C 'ordinari':249C 'packag':43B 'pars':7A,49B,82B,131C,155C,180C,224C,273C,285C,308C 'parser':25A,29A,48B 'pass':120C,266C 'patch':260C 'path':234C 'playground':248C 'postcss':1A,23A,27A,44B,46B 'postcss-selector-pars':45B 'postcss-selector-parser':22A,26A 'protect':216C 'quadrat':2A,60B 'r3gf':18A 'rather':140C 'reach':106C 'reachabl':217C 'repeat':56B 'request':233C 'rj75':16A 'sanit':36B,240C 'sass':152C 'sass-interpol':151C 'scan':121C 'second':73B 'selector':6A,24A,28A,47B,53B,79B,102C,160C,175C,204C,229C,296C,302C 'servic':83B,245C 'set':269C 'singl':110C,171C 'size':179C,197C,300C 'sourc':256C,306C 'splitword':107C 'submit':78B 'suppli':228C 'synchron':230C 'ten':182C 'test':271C 'thread':69B,172C 'three':119C,265C 'time':61B,252C 'token':98C,112C 'took':161C 'trust':255C 'uniq':145C 'untrust':225C,305C 'use':253C,268C 'word':94C,111C 'workaround':297C","nicknames":[],"mitigations":[],"who_affected":"Only applications that parse attacker-supplied selectors synchronously in a request path are exposed, such as CSS sanitizers, CSS-in-JS services and online playgrounds. Ordinary build-time parsing of trusted source files is not affected.","tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-02T16:16:46+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-rj75-hqrm-r3gf","url":"https://github.com/advisories/GHSA-rj75-hqrm-r3gf","severity":"medium","published_at":"2026-10-05T22:53:54+00:00","modified_at":"2026-10-26T14:07:57+00:00","first_seen_at":"2026-10-26T14:11:12+00:00","fetched_at":"2026-10-26T14:16:25+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-104844","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-104844","severity":null,"published_at":"2026-10-02T16:16:46+00:00","modified_at":"2026-10-02T18:44:11+00:00","first_seen_at":"2026-10-26T14:30:37+00:00","fetched_at":"2026-10-26T14:30:37+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"}