GHSA-rchf-xwx2-hm93 Fedify has ReDoS Vulnerability in HTML Parsing Regex
Hi Fedify team! 👋Thank you for your work on Fedify—it's a fantastic library for building federated applications. While reviewing the codebase, I discovered a Regular Expression Denial of Service (ReDoS) vulnerability that I'd like to report. I hope this helps improve the project's security.SummaryA Regular Expression Denial of Service (ReDoS) vulnerability exists in Fedify's document loader. The HTML parsing regex at `packages/fedify/src/runtime/docloader.ts:259` contains nested quantifiers that cause catastrophic backtracking when processing maliciously crafted HTML responses.**An attacker-controlled federated server can respond with a small (~170 bytes) malicious HTML payload that blocks the victim's Node.js event loop for 14+ seconds, causing a Denial of Service.**| Field | Value |
|-------|-------|
| **CWE** | CWE-1333 (Inefficient Regular Expression Complexity) |DetailsVulnerable CodeThe vulnerability is located in `packages/fedify/src/runtime/docloader.ts`, lines 258-264:Root Cause AnalysisThe regex has **nested quantifiers with alternation**, which is a classic ReDoS pattern:**Outer quantifier**: `((\s+...)+)` - one or more groups of attributes**Inner alternation**: `("[^"]*"|'[^']*'|[^\s>]+)` - multiple ways to match attribute valuesWhen the regex fails to match (e.g., an incomplete HTML tag), the regex engine backtracks exponentially through all possible ways the nested pattern could have matched.Attack VectorVictim's Fedify application calls `lookupObject("https://attacker.com/@user")` to fetch an actor profileAttacker's server responds with `Content-Type: text/html`The code path: `lookupObject()` → `documentLoader()` → `getRemoteDocument()` → HTML parsing (lines 258-287)Line 261: `response.text()` reads the entire body without size limitsLine 264: Regex execution triggers catastrophic backtrackingEvent loop is blocked for seconds to minutes, causing DoSWhy This Is Exploitable**No response size limit**: The HTML body is read entirely via `response.text()` without Content-Length validation**No timeout by default**: `AbortSignal` is optional and not enforced**Remote exploitation**: Attacker just needs the victim to fetch from their URL**No authentication required**: Federation commonly involves fetching profiles from untrusted servers**Amplifiable**: Multiple concurrent requests can fully disable the servicePoCQuick Reproduction (Node.js)You can verify this vulnerability with the following standalone script:Expected OutputTime approximately **quadruples every 2 additional repetitions**, demonstrating O(2^n) complexity.Full Docker-Based PoCFor a complete demonstration, here are the Docker files to run the PoC in an isolated environment:Running the Docker PoCImpactWho Is Affected?**All Fedify applications** that use `lookupObject()`, `getDocumentLoader()`, or the built-in document loader to fetch content from external URLs**Any federated server** that fetches actor profiles, posts, or other ActivityPub objects from potentially untrusted sources**Servers following standard federation patterns** - fetching remote actors is a normal operationSeverity Assessment| Factor | Assessment |
|--------|------------|
| **Attack Vector** | Network (remote) |
| **Attack Complexity** | Low (trivial payload) |
| **Privileges Required** | None |
| **User Interaction** | None |
| **Impact** | Availability (DoS) |
| **Scope** | Service-wide |Real-World ScenarioA Mastodon-compatible server powered by Fedify receives a follow request or mention from `@attacker@evil.com`The server attempts to fetch the attacker's profile via `lookupObject()`The attacker's server responds with malicious HTMLThe victim server's event loop is blocked for 14+ secondsDuring this time, all other requests are queued and potentially time outRepeated attacks can cause sustained service unavailabilityRecommended FixOption 1: Use a Proper HTML Parser (Recommended)Replace regex-based HTML parsing with a DOM parser that doesn't suffer from backtracking issues:Option 2: Add Response Size LimitsIf regex must be used, at minimum add size limits:Option 3: Refactor the RegexIf the regex approach is preferred, use atomic grouping or possessive quantifiers (where supported), or restructure to avoid nested quantifiers:Resources[OWASP: Regular Expression Denial of Service (ReDoS)](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS)[CWE-1333: Inefficient Regular Expression Complexity](https://cwe.mitre.org/data/definitions/1333.html)[Cloudflare Outage Analysis (ReDoS Example)](https://blog.cloudflare.com/details-of-the-cloudflare-outage-on-july-2-2019/)Thank you for taking the time to review this report. I'm happy to provide any additional information or help test a fix. Please let me know if you have any questions!
CVSS v3.1 score: 7.5