Sep 9, 2026·7 min read·65 visits
A 5-byte malformed TOML payload can block the single-threaded Node.js event loop indefinitely, causing 100% CPU utilization and Denial of Service.
Prior to version 1.7.1, smol-toml is vulnerable to an infinite loop Denial of Service when parsing a malformed TOML payload containing an unclosed comment inside an array or inline table.
The vulnerability CVE-2026-85730 is a critical Denial of Service (DoS) flaw within the smol-toml Node.js library, a performance-focused parser and serializer for the TOML format. It stems from an algorithmic flaw in how the parser skips whitespace and comments when analyzing nested structures such as arrays or inline tables. Unauthenticated remote attackers can leverage this flaw to cause a permanent Denial of Service by submitting highly compact, malformed payloads.
The vulnerability represents an "Infinite Loop" weakness categorized under CWE-835 and CWE-606. When processing untrusted inputs containing specific structural anomalies, the parsing utility fails to recognize the End-of-File (EOF) boundary and continually cycles over the same block of data. Because the Node.js runtime employs a single-threaded event loop, this continuous CPU utilization completely starves other asynchronous processes, disabling the server's capability to process concurrent requests.
The attack surface resides in any exposed application endpoint that accepts TOML payloads and parses them using the default parse interface of smol-toml. This makes microservices, configuration endpoints, and ingestion pipelines primary targets. Given that the minimum payload required to trigger this state is merely five bytes, the processing footprint of the attack is negligible while the impact is absolute for the affected thread.
The core of the vulnerability is found within the character-skipping routine skipUntil located in src/util.ts. This utility function is designed to scan through strings, ignoring non-structural tokens and comments, until a target separator or boundary character is encountered. In the case of arrays, the parser looks for separators like commas or the terminating bracket, while bypassing nested comments marked by the hash symbol (#).
During execution on a malformed payload like a=[1#, the pointer is positioned on the hash character at the end of the string. The function encounters the # and attempts to skip past the comment by identifying the next newline character. To do this, it calls the internal utility helper indexOfNewline(str, i). Under normal conditions, this returns the index of the next newline, allowing the loop to skip the comment block entirely and resume parsing on the following line.
When the input document abruptly terminates with a comment and contains no trailing newline, the indexOfNewline function fails to match any newline character. Consequently, it returns -1 to signal the failure. The parser directly assigns this return value to the loop index variable i. Since -1 is less than the current index, the pointer is unexpectedly warped backward to the beginning of the file.
The structure of the loop depends on standard iteration mechanics, applying the post-iteration update expression i++ once the loop block finishes. This increments the index i from -1 to 0. During the next iteration check, the condition i < str.length evaluates to true, causing the parser to restart scanning from index 0. This loop repeats indefinitely, continuously scanning the same string from the beginning, finding the same comment, warping back to -1, and incrementing back to 0 as shown in the following visual pipeline:
The following comparison demonstrates the exact loop-handling flaw before and after the application of the official security patch.
// Vulnerable Code (prior to v1.7.1)
export function skipUntil(str: string, ptr: number, sep: string, end?: string, banNewLines: boolean = false) {
if (!end) {
ptr = indexOfNewline(str, ptr)
}
for (let i = ptr; i < str.length; i++) {
let c = str[i]
if (c === '#') {
// Vulnerability: No validation of the returned index is performed.
// If no newline character exists, indexOfNewline returns -1.
i = indexOfNewline(str, i)
} else if (c === sep) {
return i + 1
} else if (c === end || (banNewLines && (c === '\n' || (c === '\r' && str[i + 1] === '\n')))) {
return i
}
}
return str.length
}In the unpatched code shown above, the return value of indexOfNewline(str, i) is blindly assigned to i. When the search fails, i becomes -1. The loop's step mechanism then increments i to 0 before evaluating i < str.length. Because the string length is greater than zero, the iteration condition is satisfied, and the engine begins scanning from the start of the string again.
To resolve this execution logic defect, the patch introduces immediate evaluation of the search outcome.
// Patched Code (v1.7.1)
export function skipUntil(str: string, ptr: number, sep: string, end?: string, banNewLines: boolean = false) {
if (!end) {
ptr = indexOfNewline(str, ptr)
}
for (let i = ptr; i < str.length; i++) {
let c = str[i]
if (c === '#') {
i = indexOfNewline(str, i)
// Security Fix: Break out of the loop immediately if no newline is found
if (i < 0) break
} else if (c === sep) {
return i + 1
} else if (c === end || (banNewLines && (c === '\n' || (c === '\r' && str[i + 1] === '\n')))) {
return i
}
}
return str.length
}The check if (i < 0) break successfully prevents the negative assignment from being incremented and re-evaluated inside the iteration block. Breaking out of the loop allows the function to execute return str.length, accurately marking the end-of-file condition and terminating the parsing execution pathway.
Exploitation of CVE-2026-85730 requires very little effort. The attack relies on an architectural asymmetry where an incredibly small payload can lead to complete service exhaustion. The prerequisite is that the target Node.js application must ingest untrusted TOML configurations or raw input payloads and process them using the parse method of smol-toml.
An attacker constructs a request payload containing an unclosed array or table with an unclosed inline comment block. Two common variations are available to trigger this state:
# Variant 1: Unclosed Array Payload (5 bytes)
a=[1#
# Variant 2: Unclosed Inline Table Payload (7 bytes)
a={k=1#Once received by the application, the library attempts to find the end-of-comment indicator. Failing to locate a newline character, the execution loop is permanently trapped in a cycle of reset iterations. In a standard single-threaded Node.js deployment, this immediately freezes the main event loop, preventing the application from handling concurrent requests or system heartbeats.
The structural failure inside smol-toml results in an immediate loss of service availability. Although there is no risk of remote code execution or data exposure, the absolute exhaustion of resources makes the impact of this vulnerability substantial. This vulnerability receives a CVSS v4.0 Base Score of 8.2.
Because Node.js runs on a single thread, any operation that forces an infinite loop blocks the event queue entirely. Consequently, even if a service employs multiple CPUs, a simple series of malicious requests corresponding to the number of server processes can completely paralyze the infrastructure.
Furthermore, because the exploitation payload is small, detecting the attack based on input size filters is impractical. This enables actors to bypass traditional packet size limits easily, stressing the necessity of patching underlying parser utilities.
The primary recommendation is to update the smol-toml dependency to version 1.7.1 or higher. This update resolves the execution logic bug by checking for negative index results and terminating the loop immediately.
When immediate library upgrades are not feasible, you can employ temporary mitigations. Implementing a Web Application Firewall (WAF) rule to block incoming request bodies containing specific malformed TOML structures can block exploit attempts. For example, rules can check for the presence of # inside brackets without a trailing newline character.
Deploying application processes under robust cluster managers or container environments with active liveness probes can minimize downtime. If the event loop freezes, a container orchestrator will fail its health checks and restart the container automatically, mitigating prolonged denial of service states.
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
smol-toml squirrelchat | < 1.7.1 | 1.7.1 |
| Attribute | Detail |
|---|---|
| Vulnerability ID | CVE-2026-85730 |
| CWE ID | CWE-835 / CWE-606 |
| CVSS v4.0 | 8.2 (High) |
| Attack Vector | Network (AV:N) |
| Exploit Status | Proof of Concept (PoC) |
| CISA KEV Listed | No |
| Affected Ecosystem | npm (Node.js) |
The program contains an iteration loop for which the exit condition is never met, causing execution to continue indefinitely.
CVE-2026-115895 is a high-severity code injection vulnerability (CWE-94) in the happy-dom npm package prior to version 21.0.4. The vulnerability exists within the ECMAScriptModuleCompiler component, which transforms ECMAScript module syntax into executable Node.js functions when JavaScript evaluation is enabled. Due to an incomplete fix for CVE-2026-33943, the compiler failed to validate local named import aliases against legal JavaScript identifier rules. Attackers supplying crafted module imports—specifically utilizing destructuring default initializers alongside JSON module attributes—can inject arbitrary JavaScript expressions directly into the host Node.js runtime context.
An improper input validation and authorization bypass vulnerability exists in the LangGraph storage layer affecting langgraph, langgraph-checkpoint-postgres, and langgraph-checkpoint-sqlite. Because relational store implementations serialize namespace tuples into dot-delimited strings without validating labels on read, search, delete, and batch operations, an attacker who controls a namespace label can inject periods (.) to traverse namespace boundaries and access, modify, or delete data belonging to other namespaces.
CVE-2026-115886 (GHSA-2wqp-q3c6-ccj5) is a Server-Side Request Forgery (SSRF) and Unsafe Reflection vulnerability in MLflow versions 3.12.0rc0 prior to 3.17.0. Unauthenticated REST API endpoints accept serialized third-party scorer JSON payloads that are dynamically deserialized and reflectively instantiated without sanitizing constructor keyword arguments. When optional dependencies such as trulens and trulens-providers-litellm are installed, attackers can inject egress-steering arguments into the scorer payload, forcing the MLflow server to dispatch outbound HTTP requests to arbitrary internal destinations or cloud metadata services.
CVE-2026-113872 (GHSA-q7qp-rjg7-q26m) is a high-severity denial-of-service vulnerability affecting the multer multipart/form-data upload middleware for Node.js and Express in versions 2.0.0 through 2.4.0. When an endpoint is configured to accept multiple files using an asynchronous fileFilter callback, a crafted, truncated multipart HTTP request can trigger an unhandled error event on the underlying busboy stream parser after request teardown has already completed. Because all error listeners are stripped during teardown, Node.js treats the resulting error event as an uncaught exception and immediately terminates the server process.
CVE-2026-115897 (also tracked as GHSA-m5mm-p8h7-m29v and aliased as CVE-2026-115754) is a Cross-Site Scripting (XSS) vulnerability in the Yahoo serialize-javascript npm package prior to version 7.1.3. The flaw exists in the regular expression used by escapeFunctionBody() to neutralize HTML script closing tags within serialized JavaScript functions. Because function bodies are sanitized in isolation before structural formatting is applied, a function whose string representation terminates directly with the sequence '</script' bypasses the positive lookahead check. When combined with serialization formatting options that introduce whitespace delimiters and a subsequent property that supplies a closing bracket, an attacker can prematurely terminate an enclosing HTML <script> block and inject arbitrary HTML or JavaScript into the rendered document.
CVE-2026-109841 is a Subresource Integrity (SRI) verification bypass vulnerability in undici, the official HTTP/1.1 and HTTP/2 client library for Node.js that powers the global fetch() implementation. The flaw stems from improper handling of case sensitivity (CWE-178) when validating cryptographic hash algorithm tokens within SRI metadata. Because undici compared incoming algorithm identifiers against a strictly lowercase set without prior ASCII-lowercase normalization, valid uppercase or mixed-case tokens such as SHA256- or Sha384- were treated as unrecognized algorithms and discarded. Under SRI forward-compatibility rules, discarding all provided tokens results in an empty integrity list, causing undici to fail open and accept tampered HTTP response bodies without cryptographic verification.