Back to Blog
high SEVERITY4 min read

linkify-it 5.0.1 mailto: Link Parsing Causes DoS

linkify-it versions up to 5.0.1 can be forced into excessive processing time when autolinking a specially crafted mailto: link, allowing a remote attacker to degrade or stall the parsing thread. Upgrading to linkify-it 5.0.2 closes the issue; any application that runs linkify-it (directly or via markdown-it) against untrusted text should update immediately.

O
By Orbis AppSec
•Published September 30, 2026•Reviewed September 30, 2026

Answer Summary

linkify-it versions up to and including 5.0.1 are affected. An attacker who can supply text that gets autolinked — for example a comment, markdown document, or chat message — can craft a mailto: link that causes the library's link-matching logic to consume disproportionate CPU time, resulting in denial of service for the parsing process. The fix, shipped in linkify-it 5.0.2, corrects the mailto: link matching behavior so crafted input no longer triggers pathological processing. The associated CWE has not been assigned by the advisory.

Vulnerability at a Glance

cweunknown
fixUpgrade linkify-it to 5.0.2, which corrects the mailto: link matching logic
riskA single malicious mailto: link can stall or exhaust CPU on the thread running linkify-it, blocking autolinking (and anything sharing that event loop, e.g. Node.js request handling)
languageJavaScript
root causelinkify-it's mailto: schema matcher in 5.0.1 processes crafted local-part/domain combinations inefficiently, leading to disproportionate scan time
vulnerabilityDenial of Service (uncontrolled resource consumption) via crafted mailto: link

Introduction

linkify-it is the autolinking engine behind popular markdown renderers like markdown-it — it scans plain text for things that look like URLs, emails, or mailto: addresses and turns them into clickable links. Because it runs on untrusted input (comments, markdown documents, chat messages, README files rendered on the fly), any inefficiency in its matching logic becomes a direct denial-of-service vector against whatever process is doing the rendering.

CVE-2026-59887 affects linkify-it 5.0.1: when the library's mailto: link matcher is handed a specially crafted address, it can burn a disproportionate amount of CPU time relative to the size of the input before it decides whether the text is or isn't a valid mailto link. An attacker doesn't need to control an entire document — a single crafted mailto: substring embedded in an otherwise ordinary paragraph is enough to trigger the slow path.

Affected Versions

Affected 5.0.1
Fixed in 5.0.2
Ecosystem npm
CVE / GHSA CVE-2026-59887 / not assigned
CWE unknown

The Vulnerability Explained

linkify-it maintains a set of schema matchers — one for http:, one for mailto:, and so on — that are tried against every position in the text being scanned. The mailto: matcher has to validate the shape of an email address (local-part, @, domain) inline, without the benefit of a full email-parsing grammar, because it's operating character-by-character over arbitrary text rather than a pre-tokenized address field.

In 5.0.1, that inline validation logic could be pushed into a pathological processing path by a carefully constructed local-part/domain combination in a mailto: link. Instead of failing fast on invalid or edge-case input, the matcher spends far more time than the input length would justify — the hallmark of a resource-exhaustion bug rather than a memory-safety one.

Attack scenario: Consider a service that renders user-submitted markdown with markdown-it and has linkify: true enabled (the common configuration for "GitHub-flavored" rendering). An attacker submits a comment or issue body containing a normal-looking paragraph with one crafted mailto: link buried in it. When the server calls into markdown-it's renderer — which delegates to linkify.match() under the hood — the request thread stalls processing that single document. Repeat the submission a handful of times concurrently, and the render workers (or the whole Node.js event loop, if rendering happens synchronously on it) back up, degrading or halting the service for all users — a classic high-severity availability issue with a very low bar to exploit.

The trivy scan that flagged this in the dependency tree couldn't confirm reachability from source, but any code path that feeds attacker-influenced text into linkify-it's matching functions — directly or through markdown-it — is exposed.

The Fix

The remediation here is a dependency version bump, not a source-code patch in this repository: linkify-it is upgraded from 5.0.1 to 5.0.2 in the manifest and lockfile.

-    "linkify-it": "5.0.1"
+    "linkify-it": "5.0.2"

Upstream, 5.0.2 corrects the mailto: matcher so that crafted local-part/domain input no longer forces the disproportionate scan behavior — the matcher now rejects malformed or adversarial mailto candidates without the excessive processing cost. Because the fix lives entirely inside linkify-it, no application code needs to change; consumers only need to pull in the patched dependency.

It's worth noting that the diff for this change is otherwise dominated by unrelated lockfile bookkeeping — moving a typedoc-material-theme entry from dependencies to devDependencies and marking a few Shiki-related packages as dev — none of which affects runtime behavior. The security-relevant change is exclusively the linkify-it version bump.

Key Takeaways

  • If your app renders user-supplied markdown with linkify enabled, treat linkify-it as directly reachable by attacker input, not as an incidental transitive dependency.
  • A DoS in a link-matching library doesn't need a huge payload — one crafted mailto: substring inside a normal document is enough.
  • Pin and monitor linkify-it versions the same way you would any parser that runs against untrusted text; 5.0.1 → 5.0.2 is a drop-in, no-API-change upgrade.
  • Rendering markdown synchronously on a shared event loop (e.g., Node.js) turns a single slow parse into a service-wide stall — consider isolating rendering for untrusted content regardless of this specific fix.

How Orbis AppSec Detected This

  • Source: text passed into linkify.match() / linkify.test() — typically user-submitted markdown, comments, or chat content routed through markdown-it's linkify option.
  • Sink: linkify-it's internal mailto: schema matcher, invoked automatically during autolink scanning.
  • Missing control: no bound on the processing cost of validating a crafted mailto: local-part/domain combination before version 5.0.2.
  • CWE: unknown (not assigned in the advisory).
  • Fix: Upgrade the linkify-it dependency from 5.0.1 to 5.0.2, which patches the mailto: matching logic.

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-59887 is a reminder that autolinking libraries sit directly in the path of untrusted text, and a single inefficient matcher — here, the mailto: handler in linkify-it 5.0.1 — is enough to turn ordinary content rendering into a denial-of-service vector. The fix is a one-line dependency bump to 5.0.2, but the impact scales with how much of your rendering pipeline touches attacker-controlled text, so it's worth verifying the upgrade landed anywhere linkify-it or markdown-it's linkify option is in use.

Prevention and further reading

Frequently Asked Questions

Does upgrading linkify-it from 5.0.1 to 5.0.2 change its public autolinking API?

No. The fix is contained to the mailto: link matching behavior; `linkify.match()`, `linkify.test()`, and schema registration work exactly as before.

Am I exposed if my project only uses linkify-it indirectly through markdown-it?

Yes, if the markdown-it linkify option is enabled and untrusted text is rendered, since markdown-it delegates autolinking to linkify-it's 5.0.1 mailto: matcher.

Does this vulnerability require the attacker to control the whole document, or just a substring?

Just a substring — a single crafted mailto: link embedded anywhere in otherwise normal text is sufficient to trigger the excessive processing.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #33

Related Articles

high

image-size 1.2.1 DoS: Zero-Valued Dimensions in Image Buffer Parser

A high-severity denial-of-service vulnerability in image-size 1.2.1 allows attackers to crash Node.js services using malicious image buffers with zero-valued dimensions. The fix removes the vulnerable `queue` dependency and tightens dimension validation in version 2.0.3.

critical

Slim CLI Unverified Remote Fetch in Version Check

The Slim CLI's version check command fetched remote package metadata without integrity verification, enabling attackers to serve malicious responses through repository hijacking or man-in-the-middle attacks. The fix adds input validation, HTTP status checking, and response schema verification to ensure only legitimate version data is processed.

critical

ensureTrivy() CWE-494: Unverified Trivy Binary Download

The `ensureTrivy()` function fetched the Trivy vulnerability scanner from GitHub releases without verifying its integrity, exposing applications to supply-chain attacks. An attacker in a MITM position or a compromised CDN could substitute a malicious binary that executes with the application's privileges. The fix adds cryptographic verification using SHA256 checksums published alongside each release.

high

@xmldom/xmldom 0.9.10 ReDoS: CVE-2026-83606 in PI Parsing

The dependency tree pinned `@xmldom/xmldom` at 0.9.10, a version vulnerable to CVE-2026-83606: a regular expression in the processing-instruction (`<?target data?>`) parsing path backtracks catastrophically on crafted input, so a single XML document can consume CPU for seconds or minutes. Because Node.js runs application code on one thread, that stall blocks every other request in flight. The lockfile was updated to resolve `@xmldom/xmldom` 0.9.12, which carries the fix.

high

Unbounded Map in createLoginRateLimiter Exhausts API Memory

The console API's `createLoginRateLimiter` and `createMutationRateLimiter` stored one `Map` entry per client key with no upper bound and no expiry sweep, so an attacker rotating source addresses or identifiers could grow those maps until the Node process hit an out-of-memory crash. The fix introduces a `maxTrackedKeys` option (default 5000), a `trackedEntryLimit` sanitizer, and an `evictOldestIfFull` helper that drops the oldest tracked key while protecting the shared global counter. The rate li

high

THiNX Device Platform: Command Injection via source.owner in Git Fetch

The THiNX device management platform fixed a critical vulnerability where the `source.owner` parameter flowed directly into shell commands during git repository operations. An attacker with source configuration access could inject arbitrary shell commands by providing malicious owner values containing metacharacters like `; curl`.