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

Infinite Loop in jsdiff: When Typography Attacks

Alon Barad
Alon Barad
Software Engineer

Feb 19, 2026·5 min read·45 visits

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.

More Reports

•about 20 hours ago•CVE-2026-58197
8.8

CVE-2026-58197: Host Escape and Lateral Movement via Insecure Container Network Defaults in ToolHive

A high-severity access control vulnerability in ToolHive CLI before v0.30.1 and ToolHive Studio before v0.38.0 allows local containerized MCP servers to bypass network isolation. This enables malicious workloads to establish TCP/IP connections to administrative and control plane endpoints exposed on the host loopback interface.

Amit Schendel
Amit Schendel
7 views•8 min read
•about 21 hours ago•CVE-2026-63405
5.9

CVE-2026-63405: Insufficient Verification of Data Authenticity in AnyCable Pusher REST API

AnyCable is a real-time communication server. Prior to version 1.6.15, its Pusher-compatible REST API suffered from an authentication bypass vulnerability because it failed to verify that the request body matched the signature-validated body_md5 parameter. This allows attackers to perform replay attacks with modified body contents.

Amit Schendel
Amit Schendel
7 views•5 min read
•about 22 hours ago•CVE-2026-64847
6.8

CVE-2026-64847: Indefinite Denial of Service via Undrained Stderr in AnyIO Process Pool Workers

A denial-of-service vulnerability exists in AnyIO prior to version 4.14.2. Standard error streams of process-pool workers are connected to an operating system pipe that is never drained by the parent process. This allows a worker to fill the pipe buffer and deadlock indefinitely.

Amit Schendel
Amit Schendel
7 views•6 min read
•about 23 hours ago•CVE-2026-63349
7.0

CVE-2026-63349: Privilege Dropping Bypass and Denial of Service in AnyIO Subprocess Module

CVE-2026-63349 is a critical privilege-dropping bypass vulnerability in the AnyIO asynchronous framework (versions 4.14.0 and 4.14.1) on POSIX platforms. Due to a variable assignment typo, supplementary groups specified by the developer are not correctly propagated to the execution backend, resulting in subprocesses retaining the parent process's elevated supplementary group permissions.

Alon Barad
Alon Barad
10 views•5 min read
•1 day ago•CVE-2026-63406
5.9

CVE-2026-63406: Information Disclosure via Insecure Telemetry and Hardcoded Credentials in AnyCable-Go

CVE-2026-63406 is an information disclosure vulnerability in AnyCable-go prior to version 1.6.15. The built-in telemetry client is enabled by default with a hardcoded public authentication token ('secret'). This client digests highly sensitive configuration parameters and command-line arguments, including JWT secrets and RPC secrets, into a stable SHA-256 fingerprint. This fingerprint is sent over public networks, exposing those administrative secrets to offline dictionary and brute-force attacks if intercepted.

Alon Barad
Alon Barad
7 views•5 min read
•1 day ago•CVE-2026-84992
6.1

CVE-2026-84992: Cross-Site Scripting (XSS) via Fenced Code Block Parsing in md-editor-v3

CVE-2026-84992 is a Cross-Site Scripting (XSS) vulnerability affecting md-editor-v3 before version 6.5.4. It occurs because the fenced-code block language parser directly interpolates unescaped language metadata into unquoted HTML attributes inside the custom rendering callback. This bypasses the built-in XSSPlugin which runs during the parsing phase, before rendering.

Amit Schendel
Amit Schendel
9 views•6 min read