---
id: "CVE-2026-104844"
kind: "vulnerability"
aliases: ["CVE-2026-104844", "GHSA-rj75-hqrm-r3gf"]
title: "PostCSS: Quadratic complexity in flat selector parsing allows CPU exhaustion"
severity: "medium"
cvss: 5.9
epss: 0.00389
exploited: false
status: "open"
category: "package"
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"]
published_at: "2026-10-02T16:16:46+00:00"
first_seen_at: "2026-10-26T14:11:12+00:00"
updated_at: "2026-10-26T14:30:37+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-rj75-hqrm-r3gf", "first_seen_at": "2026-10-26T14:11:12+00:00"}, {"source": "nvd", "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-104844", "first_seen_at": "2026-10-26T14:30:37+00:00"}]
---

# PostCSS: Quadratic complexity in flat selector parsing allows CPU exhaustion

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.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:postcss-selector-parser affected >= 7.0.0, < 7.1.6 fixed 7.1.6
- npm:postcss-selector-parser affected < 6.1.5 fixed 6.1.5

## How to check

- Run `npm ls postcss-selector-parser` to list every installed copy and version.
- 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.
- Until patched, cap the size of selectors accepted from untrusted sources before parsing.

## Timeline

- 2026-10-26T14:11:12+00:00 ghsa https://github.com/advisories/GHSA-rj75-hqrm-r3gf
- 2026-10-26T14:30:37+00:00 nvd https://nvd.nist.gov/vuln/detail/CVE-2026-104844

## From the sources

### Impact

`.` and `#` are not word delimiters in the tokenizer, so a flat selector such as
`.a.a.a...` reaches `splitWord()` as a single word token carrying n class or id
indexes. Three passes scanned those index arrays linearly for every index,
making the parse O(n^2) in the number of indexes rather than in input length:
`uniqs()`, the `indices.forEach` loop, and the Sass-interpolation filter.
Parsing a 400 KB flat selector took ~34 s on a modern laptop, fully occupying a
single thread. A benign selector of identical byte size parses in tens of
milliseconds, so the cost is driven by the index count, not the input size.
The nesting depth of such a selector is 0, so the `maxNestingDepth` guard added
in 7.1.3 offers no protection.

Reachability is deployment dependent. Only consumers that parse untrusted,
attacker-supplied selectors synchronously in a request path are exposed, for
example CSS sanitizers, CSS-in-JS services and online playgrounds. Ordinary
build-time use on trusted sources is not affected.

### Patches

Fixed in 7.1.6. The three passes now use Set membership tests, making parsing
linear in the number of indexes. There is no behaviour change: parsing is
byte-identical on a differential corpus of 8413 selectors.

### Workarounds

Cap the size of selectors accepted from untrusted sources before parsing.

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
