Back to Blog
high SEVERITY7 min read

How Denial of Service via Specific Input Sequence happens in JavaScript (marked) and how to fix it

CVE-2026-41680 is a high-severity Denial of Service vulnerability in the marked Markdown parsing library, affecting versions prior to 18.0.2. By supplying a crafted input sequence to the parser, an attacker can cause the application to hang or exhaust resources, making the frontend unavailable. Upgrading marked from 18.0.0 to 18.0.2 in both `package.json` and `package-lock.json` closes the vulnerability without affecting valid Markdown rendering.

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

CVE-2026-41680 is a Denial of Service (CWE-400: Uncontrolled Resource Consumption) vulnerability in the marked JavaScript Markdown library (versions < 18.0.2). When a specially crafted input sequence is passed to the marked parser, it can trigger pathological processing that exhausts CPU or memory resources, crashing or freezing the frontend application. The fix is to upgrade marked to 18.0.2 by updating the version constraint in `package.json` and regenerating `package-lock.json`, ensuring the patched parsing logic handles malicious input sequences gracefully.

Vulnerability at a Glance

cweCWE-400
fixUpgrade marked from 18.0.0 to 18.0.2 in package.json and package-lock.json
riskAttackers can crash or freeze the frontend by submitting crafted Markdown input
languageJavaScript / Node.js
root causemarked 18.0.0 contains a parsing path that enters pathological processing on specific input sequences
vulnerabilityDenial of Service via specific input sequence

The Risk Hiding in Your Markdown Renderer

If your React frontend lets users write or submit Markdown—think comments, documentation editors, chat messages, or configuration notes—the library that parses that Markdown is directly in the path of untrusted input. In this application's frontend, marked is that library, and version 18.0.0 contains a high-severity Denial of Service vulnerability tracked as CVE-2026-41680.

The Trivy scanner flagged the vulnerable version pinned in frontend/package-lock.json, and an automated pull request was opened to upgrade to the patched release. This post explains exactly what the vulnerability is, how an attacker could exploit it, and what the two-file change does to close it.


The Vulnerability Explained

What Goes Wrong in marked 18.0.0

CVE-2026-41680 is a Denial of Service via specific input sequence in the marked Markdown parsing library. The root cause is a parsing path in marked 18.0.0 that, when fed a carefully constructed sequence of characters, enters a state of pathological processing—consuming CPU cycles or memory far beyond what any legitimate Markdown document would require.

This class of vulnerability is catalogued as CWE-400: Uncontrolled Resource Consumption. The parser does not adequately bound the resources it allocates or the work it performs when encountering the triggering input pattern.

The vulnerable declaration in frontend/package-lock.json (before the fix) was:

"node_modules/marked": {
  "version": "18.0.0",
  "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.0.tgz",
  "integrity": "sha512-2e7Qiv/HJSXj8rDEpgTvGKsP8yYtI9xXHKDnrftrmnrJPaFNM7VRb2YCzWaX4BP1iCJ/XPduzDJZMFoqTCcIMA==",
  "bin": {
    "marked": "bin/marked.js"
  }
}

And in frontend/package.json:

"marked": "^18.0.0"

The ^18.0.0 semver range would allow npm to resolve a newer patch version—but because package-lock.json had 18.0.0 pinned with a specific integrity hash, the vulnerable version was locked in place and would not be updated without an explicit change.

How an Attacker Could Exploit This

Consider a frontend feature that accepts user-supplied Markdown—a comment field, a documentation editor, or a live-preview input box. The application passes that content directly to marked for rendering. An attacker does not need any special privileges; they only need to be able to submit input.

By sending a POST request with a body containing the specific input sequence that triggers the DoS condition:

POST /api/comments
Content-Type: application/json

{
  "body": "<crafted_sequence_triggering_CVE-2026-41680>"
}

The frontend's Markdown rendering pipeline—or any server-side use of the same marked dependency—begins processing the input and either:

  1. Spins the event loop with a CPU-intensive parsing loop, blocking all other requests, or
  2. Exhausts heap memory, causing the Node.js process to crash with an out-of-memory error.

In a single-threaded Node.js environment, either outcome effectively takes the entire frontend service offline for every user until the process is restarted. Because the trigger is an input sequence rather than a volume attack, this can be achieved with a single, small HTTP request—no botnet required.


The Fix

What Changed and Why Both Files Matter

The fix required changes to exactly two files: frontend/package.json and frontend/package-lock.json.

frontend/package.json — updating the version constraint:

-    "marked": "^18.0.0",
+    "marked": "^18.0.2",

This shifts the minimum acceptable version to 18.0.2, ensuring that any fresh npm install will not resolve to the vulnerable 18.0.0 or 18.0.1.

frontend/package-lock.json — pinning the patched release:

 "node_modules/marked": {
-  "version": "18.0.0",
-  "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.0.tgz",
-  "integrity": "sha512-2e7Qiv/HJSXj8rDEpgTvGKsP8yYtI9xXHKDnrftrmnrJPaFNM7VRb2YCzWaX4BP1iCJ/XPduzDJZMFoqTCcIMA==",
+  "version": "18.0.2",
+  "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.2.tgz",
+  "integrity": "sha512-NsmlUYBS/Zg57rgDWMYdnre6OTj4e+qq/JS2ot3KrYLSoHLw+sDu0Nm1ZGpRgYAq6c+b1ekaY5NzVchMCQnzcg==",
+  "license": "MIT",
   "bin": {
     "marked": "bin/marked.js"
   }
 }

The lock file update is critical. Without it, even if package.json specifies ^18.0.2, the lock file's pinned integrity hash for 18.0.0 would continue to be used in CI pipelines and production deployments that run npm ci (which respects the lock file exactly). Updating both files ensures that every environment—local development, CI, and production—installs the patched version.

The new integrity hash sha512-NsmlUYBS/... cryptographically verifies that the installed tarball is exactly marked@18.0.2 from the npm registry, preventing supply-chain substitution attacks.

What the Patch Does Inside marked

The 18.0.2 release tightens the parser's handling of the specific input sequences that trigger pathological processing. The fix introduces bounds on the internal parsing state so that the problematic input path terminates in bounded time rather than running indefinitely. Valid Markdown documents—headings, lists, code blocks, links, emphasis—are processed identically to before; only the malicious edge-case input is handled differently.


Prevention & Best Practices

1. Lock Files Are Not a Set-and-Forget Security Control

package-lock.json pins exact versions for reproducibility, but that same property means a vulnerable version stays pinned until you explicitly update it. Treat your lock file as a security artifact that requires regular review, not just a build reproducibility tool.

2. Automate Dependency Vulnerability Scanning

Integrate a scanner into your CI pipeline that checks package-lock.json against the CVE database on every pull request and on a scheduled basis:

# npm's built-in audit
npm audit --audit-level=high

# Trivy (the scanner that caught this issue)
trivy fs --scanners vuln frontend/package-lock.json

# Snyk
snyk test --file=frontend/package-lock.json

3. Apply Input Length Limits as Defense-in-Depth

Even with a patched marked, enforcing a maximum length on user-submitted Markdown reduces the blast radius of any future parsing vulnerabilities:

const MAX_MARKDOWN_LENGTH = 50_000; // characters

function renderMarkdown(input) {
  if (typeof input !== 'string' || input.length > MAX_MARKDOWN_LENGTH) {
    throw new Error('Input exceeds maximum allowed length');
  }
  return marked.parse(input);
}

This does not replace patching, but it shrinks the attack surface for any undiscovered parsing edge cases.

4. Consider a Markdown Parsing Timeout

For high-risk deployments, wrap Markdown rendering in a timeout to cap worst-case processing time:

function renderMarkdownWithTimeout(input, timeoutMs = 500) {
  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => reject(new Error('Markdown rendering timed out')), timeoutMs);
    try {
      resolve(marked.parse(input));
    } catch (err) {
      reject(err);
    } finally {
      clearTimeout(timer);
    }
  });
}

5. Security Standards Reference

  • CWE-400: Uncontrolled Resource Consumption — https://cwe.mitre.org/data/definitions/400.html
  • OWASP: Denial of Service Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html
  • npm audit documentation: https://docs.npmjs.com/cli/v10/commands/npm-audit

Key Takeaways

  • package-lock.json must be updated alongside package.json: changing only the version range in package.json leaves the vulnerable version pinned in the lock file and deployed via npm ci.
  • marked 18.0.0 is unsafe for any user-submitted Markdown: the DoS trigger requires no authentication and can be sent in a single small HTTP request, making exposure proportional to how widely the input field is accessible.
  • Trivy's dependency scanning caught a pinned vulnerable version that semver range semantics alone would not have resolved—demonstrating why lock-file-aware scanners are necessary.
  • Input length limits are a valuable complement to patching, not a replacement: they reduce the attack surface for any future parsing vulnerabilities in the same code path.
  • The ^18.0.2 constraint ensures that future patch releases (e.g., 18.0.3) will be picked up automatically, while still preventing a downgrade to the vulnerable 18.0.0.

How Orbis AppSec Detected This

  • Source: User-controlled Markdown content submitted through the frontend application's input fields
  • Sink: The marked.parse() call consuming the untrusted input, resolved to marked@18.0.0 as pinned in frontend/package-lock.json
  • Missing control: No version constraint or integrity check preventing the vulnerable 18.0.0 release from being installed; no runtime input bounding before the parse call
  • CWE: CWE-400 — Uncontrolled Resource Consumption
  • Fix: Upgraded marked from 18.0.0 to 18.0.2 in both frontend/package.json and frontend/package-lock.json, replacing the vulnerable integrity hash with the verified hash for the patched release

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2026-41680 is a concrete reminder that Markdown parsers sit directly in the path of untrusted input and carry the same security obligations as any other input-processing component. The vulnerability in marked 18.0.0 required nothing more than a crafted string to take down a frontend service—no authentication, no elevated privileges, no volume.

The fix is a two-line version bump across two files, but the lesson is broader: lock files need to be treated as security-sensitive artifacts, dependency scanners need to run against them continuously, and any library that processes user input needs to be kept current. Upgrading to marked 18.0.2 closes this specific door; the practices above keep future doors from opening unnoticed.


References

Frequently Asked Questions

What is a Denial of Service vulnerability in a Markdown parser?

A DoS vulnerability in a Markdown parser means a specially crafted string—when passed to the parser—causes it to consume excessive CPU or memory, making the application unresponsive or crashing it entirely without requiring authentication.

How do you prevent Denial of Service in JavaScript Markdown libraries?

Keep Markdown parsing libraries up to date, enforce input length limits before passing content to the parser, and use automated dependency scanning tools to catch vulnerable versions before they reach production.

What CWE is this Denial of Service vulnerability?

This vulnerability maps to CWE-400: Uncontrolled Resource Consumption, where the application fails to constrain the resources consumed while processing attacker-controlled input.

Is input length limiting enough to prevent this type of DoS?

Length limiting reduces risk but is not sufficient on its own—some DoS patterns can be triggered with short but carefully structured inputs. Patching to the fixed version (18.0.2) is the definitive mitigation.

Can static analysis detect this type of Denial of Service vulnerability?

Yes. Dependency-aware scanners like Trivy, Snyk, and npm audit can identify known-vulnerable package versions in package-lock.json and flag them against CVE databases, exactly as Trivy flagged this issue.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #10

Related Articles

high

How Denial of Service via Unbounded Intermediate Arrays happens in JavaScript and how to fix it

CVE-2026-69152 is a high-severity Denial of Service vulnerability in the `brace-expansion` npm package (versions prior to 1.1.18/2.1.4/3.0.6/5.0.9) that allows attackers to crash a Node.js application by crafting glob patterns that generate unbounded intermediate arrays, effectively bypassing the earlier CVE-2026-14257 mitigation. The fix upgrades `brace-expansion` from 1.1.14 to 1.1.18 in `frontend/package-lock.json`, closing the bypass and restoring safe memory bounds during pattern expansion.

high

How Quadratic CPU Consumption happens in JavaScript YAML parsing and how to fix it

A high-severity denial-of-service vulnerability (GHSA-5p4m-2wfm-xmqj) was discovered in js-yaml affecting both the 3.x and 4.x branches, where parsing YAML documents containing `!!omap` tags triggers quadratic CPU consumption. The fix upgrades js-yaml from `^4.1.1` to `5.2.0` in the project's GitHub Actions workflow dependencies, closing the attack surface for any untrusted YAML input processed by CI/CD tooling.

critical

How Missing Rate Limiting happens in Express.js and how to fix it

Two public API endpoints in `server.js` — `/api/health` and `/api/contact` — were exposed without any rate limiting middleware, allowing attackers to exhaust server resources or spam an SMTP server with unlimited requests. The fix adds rate limiting to both endpoints, with stricter controls on the resource-intensive `/api/contact` route that triggers email sending operations. This change closes a directly exploitable denial-of-service vector in a production web service.

high

How Quadratic CPU Consumption happens in JavaScript YAML parsing and how to fix it

A high-severity denial-of-service vulnerability in js-yaml (GHSA-5p4m-2wfm-xmqj) caused quadratic CPU consumption when resolving `!!omap` YAML types in both the 3.x and 4.x branches. The fix upgrades js-yaml from 3.14.2 to 3.15.1 and from 4.1.1 to 4.3.1, eliminating the algorithmic complexity exploit while leaving all valid YAML inputs unaffected.

high

How Denial of Service via Unbounded Data Happens in JavaScript and how to fix it

CVE-2025-58754 is a high-severity Denial of Service vulnerability in the popular axios HTTP client library, caused by the absence of a data size check on incoming response or request payloads. An attacker who can influence the size of data processed by axios could exhaust server memory or CPU, bringing down dependent Node.js applications. The fix upgrades axios from version 1.8.4 to 1.18.0, closing the unbounded data processing path.

high

How Path Traversal happens in PostCSS Source Map Loading and how to fix it

A path traversal vulnerability in PostCSS versions before 8.5.18 allowed malicious `sourceMappingURL` comments in CSS files to trick PostCSS into loading arbitrary `.map` files from the filesystem. The fix upgrades PostCSS from 8.5.15 to 8.5.18 in `frontend/package-lock.json` and pins the version via an override in `frontend/package.json`, closing the file disclosure vector before it could be chained with other weaknesses.