Skip to main content

linkify-it CVE-2026-48801

| EUVDEUVD-2026-44454 HIGH
Inefficient Regular Expression Complexity (ReDoS) (CWE-1333)
2026-06-26 https://github.com/markdown-it/linkify-it GHSA-22p9-wv53-3rq4
8.7
CVSS 4.0 · Vendor: https://github.com/markdown-it/linkify-it
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Craft ~64 KB of repeated 'a@b.com' text
  2. Delivery
    Submit as Markdown post/comment/message
  3. Exploit
    linkify.match() enters O(N²) scan loop
  4. Execution
    Worker thread pinned for seconds
  5. Persist
    Repeat under concurrency
  6. Impact
    Rendering tier exhausted, service DoS

Vulnerability Assessment

Weakness
CWE
ASVS 5.0
V2 - Validation and Business Logic Regex complexity limits
MITRE ATT&CK

1 technique across 1 tactic: Impact

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

Exploitation Conditions

The vulnerable code path is only reached when the application invokes linkify-it's fuzzy matching on attacker-controlled text - concretely, calling LinkifyIt.prototype.match()/test(), or using markdown-it with the linkify:true option so the linkify core rule runs. The trigger input must contain many fuzzy link/email candidates (the PoC uses repeated "a@b.com" strings); tens of KB is sufficient to cause multi-second stalls. No authentication and no user interaction are required (CVSS PR:N/UI:N), and default markdown-it does NOT enable linkify, so the key limiting factor is configuration: services that leave linkify disabled, that don't render untrusted input, or that cap input size before rendering are not exploitable. Impact is limited to CPU/availability of the rendering worker - no data disclosure or code execution.

Impact

The signals are internally consistent and point to a genuine but availability-only issue. The CVSS 4.0 vector (AV:N/AC:L/AT:N/PR:N/UI:N) confirms remote, low-complexity, unauthenticated triggering with no user interaction, and the impact metrics isolate the effect to availability (VA:H; VC:N/VI:N), matching the reporter's explicit 'no confidentiality or integrity impact' statement. Publicly available exploit code exists (a working PoC is embedded in the GHSA advisory and reproduced against both linkify-it and markdown-it), which raises practical risk, but this must not be conflated with active exploitation - there is no CISA KEV listing and no EPSS score in the provided data, so real-world exploitation frequency is unquantified here. The vendor-scored 8.7 (High) is defensible for a trivially remote DoS, though the practical blast radius is bounded: it only matters for services that synchronously render untrusted Markdown with linkify:true on a request hot-path. Note the only score supplied is CVSS 4.0; no CVSS 3.1 vector was provided. Net: a real prioritization candidate for exposed Markdown-rendering services, but lower priority for deployments that don't feed untrusted text through linkify.

Exploit ScenarioDerived

An attacker posts a comment, chat message, or wiki edit containing ~64 KB of repeated "a@b.com " to a service that renders untrusted Markdown with linkify:true; the synchronous render call blocks one worker thread for ~2.5 seconds. Because the vector is remote, low-complexity, and unauthenticated (AV:N/AC:L/PR:N/UI:N), the attacker scripts this at modest concurrency (10-50 requests) to wedge the entire rendering tier. A publicly available PoC in the GHSA advisory demonstrates the quadratic curve against both linkify-it directly and markdown-it.

This Week Schedule remediation this week. Why: HIGH severity (CVSS 7.0-8.9).
Patch available 0.4% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv2 – Jul 14, 2026

Denial of service in linkify-it (npm) through v5.0.0 lets remote unauthenticated attackers wedge a rendering worker by submitting tens of KB of repeated email/link-like text. The core public API LinkifyIt.prototype.match runs an O(N²) scan loop that re-slices the input and re-runs unanchored fuzzy regex searches once per match, so 64 KB of "a@b.com" burns ~2.5 s of single-threaded CPU and 128 KB ~10 s. The flaw is inherited by markdown-it (~21.6M weekly npm downloads) whenever linkify:true is set, exposing forums, chat, wikis and AI chat UIs; publicly available exploit code (a PoC in the GHSA advisory) exists, but there is no evidence of active exploitation.

Earlier versions of this analysis (1)
v1 Jun 26, 2026 - 21:20

Denial of service in linkify-it (all versions through 5.0.0, fixed in 5.0.1) lets remote attackers wedge a worker process by submitting tens of KB of repeated email- or link-like text. The library's primary LinkifyIt.prototype.match API runs an O(N²) scan loop that re-slices the input tail and re-runs unanchored fuzzy regex searches once per match, so 64 KB of "a@b.com\n" burns ~2.5 s of single-threaded CPU and 128 KB ~10 s. The same cost flows transitively through markdown-it (~21.6M weekly npm downloads) whenever linkify:true is set, exposing any service that synchronously renders untrusted Markdown on a request hot-path; a detailed proof-of-concept is published in the advisory, but it is not in CISA KEV.

RemediationAI

Vendor-released patch: linkify-it 5.0.1 - upgrade the dependency directly, and for markdown-it deployments update the transitive resolution (e.g. npm audit fix, an override/resolution pinning linkify-it >=5.0.1, or a lockfile refresh) so the fixed version is actually installed. The fix reworks the scan to check each pattern separately using global (g-flag) regexes and drops the tail.slice()/internal cache, restoring O(N) behavior (commit 6be6d15e0641bf1daeaa977d500cabc743166159). Where immediate upgrade is not possible, the effective compensating control is to stop feeding large untrusted inputs through the fuzzy scanner: cap request/body and per-field input size before rendering (e.g. reject Markdown bodies over a few KB) - trade-off is rejecting legitimately long posts; disable linkify (set linkify:false in markdown-it) - trade-off is losing automatic URL/email linkification; or move rendering off the synchronous request path into a worker with a strict CPU/time budget - trade-off is added architectural complexity. Advisory and fix references: https://github.com/markdown-it/linkify-it/security/advisories/GHSA-22p9-wv53-3rq4 and https://github.com/markdown-it/linkify-it/commit/6be6d15e0641bf1daeaa977d500cabc743166159.

Affected VersionsENISA

linkify-it < 5.0.1

Affected Packages

npm linkify-it fix: 5.0.1

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 1 package in npm

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

linkify-it is a small JavaScript library that auto-detects URLs, emails and fuzzy links in free text; it is the linkification engine behind markdown-it. The root cause is classed as CWE-1333 (Inefficient Regular Expression Complexity), but the maintainer and reporter both stress this is NOT catastrophic regex backtracking - it is an algorithmic (structural) O(N²) problem in the JS scan loop. In index.mjs:528-554, match() maintains a growing shift offset and calls tail.slice() each iteration (O(N) allocation) then test(), which runs three full-tail unanchored searches: host_fuzzy_test via text.search (index.mjs:444), link_fuzzy via text.match (:448) and email_fuzzy via text.match (:467). With one match consumed per iteration over N matches, total work is Σ(N - i·c) = O(N²). The adjacent schema-prefixed branch (index.mjs:428-440) already does this correctly using a g-flag RegExp advancing lastIndex, which is the in-repo precedent for the fix. CPE identifies the affected package as pkg:npm/linkify-it.

Know when Mattermost gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/markdown-it/linkify-it) PRIMARY 8.7 HIGH
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
vuln.today AI 7.5 HIGH

Remote, low-complexity, unauthenticated trigger with no user interaction against services rendering untrusted input; impact is availability-only CPU exhaustion, so C:N/I:N/A:H.

3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N
SUSE 7.5 HIGH
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Red Hat 7.5 MEDIUM

Primary rating from Vendor (https://github.com/markdown-it/linkify-it).

CVSS VectorVendor: https://github.com/markdown-it/linkify-it

Attack Vector
Network
Attack Complexity
Low
Attack Requirements
None
Privileges Required
None
User Interaction
None
Vulnerable system: Confidentiality
None
Vulnerable system: Integrity
None
Vulnerable system: Availability
High
Subsequent system: Confidentiality
None
Subsequent system: Integrity
None
Subsequent system: Availability
None

Lifecycle Timeline

1
CVSS changed
Jul 14, 2026 - 21:22 NVD
8.7 (HIGH)

DescriptionCVE.org

Summary

LinkifyIt.prototype.match - the package's primary public API - has O(N²) algorithmic complexity for inputs containing many fuzzy links or emails. This is not a regex backtrack bug; it's a structural issue in the JS-level scan loop that re-slices the input and re-runs unanchored regex searches on progressively shorter tails, N times.

64 KB of "a@b.com\n" repeated burns ~2.5 s of single-threaded CPU; 128 KB takes ~10 s. Doubling the input quadruples the time - textbook O(N²).

The same cost passes through markdown-it (linkify:true) unmodified. Any service that synchronously renders untrusted Markdown with linkify enabled on a request hot-path (forums, comments, chat, wikis, AI chat UIs) inherits a worker-process DoS triggerable by a tens-of-KB request body.

Affected component

  • HEAD audited: 8e887d5bace3f5b09b1d1f70492fa0364ef1793d (v5.0.0)
  • Vulnerable function: LinkifyIt.prototype.match - index.mjs:528-554
  • Re-scan call sites inside test(): index.mjs:444 (fuzzy host search), :448 (fuzzy link match), :467 (fuzzy email match)
  • Transitive consumer: markdown-it (~21.6M weekly npm DLs) calls linkify.match() at lib/rules_core/linkify.mjs:57 when linkify:true
  • All versions affected - the vulnerable loop exists since the initial commit (2014) through v5.0.0

Vulnerability details

The O(N²) outer loop

index.mjs:528-554:

js
LinkifyIt.prototype.match = function match (text) {
  const result = []
  let shift = 0
  let tail = shift ? text.slice(shift) : text

  while (this.test(tail)) {
    result.push(createMatch(this, shift))
    tail = tail.slice(this.__last_index__)   // <-- re-allocates remaining tail each iteration
    shift += this.__last_index__
  }

  if (result.length) return result
  return null
}

The loop iterates O(N) times (once per match). Each iteration:

  1. tail.slice() re-allocates a string of length |text| - shift - O(N) per iteration
  2. this.test(tail) runs three unanchored regex searches over the full new tail:
js
// index.mjs:444 - full-tail search
tld_pos = text.search(this.re.host_fuzzy_test)
// index.mjs:448 - full-tail match
ml = text.match(this.re.link_fuzzy)
// index.mjs:467 - full-tail match
me = text.match(this.re.email_fuzzy)

Total cost: Σ(N - i*c) for i=0..N = O(N²).

Contrast with the linear schema branch

The schema-prefixed scan in the same test() function does it correctly at index.mjs:428-440:

js
re = this.re.schema_search
re.lastIndex = 0
while ((m = re.exec(text)) !== null) { ... }

That branch uses a g-flag RegExp and advances lastIndex - linear. The fuzzy branches don't follow this pattern.

Proof of concept

bash
mkdir /tmp/linkifyit-redos && cd /tmp/linkifyit-redos
npm install linkify-it@5.0.0

cat > poc.mjs <<'EOF'
import LinkifyIt from 'linkify-it'
const l = new LinkifyIt()
for (const n of [1000, 2000, 4000, 8000, 16000]) {
  const evil = 'a@b.com\n'.repeat(n)
  const t0 = process.hrtime.bigint()
  l.match(evil)
  const ms = Number(process.hrtime.bigint() - t0) / 1e6
  console.log(`n=${n} bytes=${evil.length} took ${ms.toFixed(0)} ms`)
}
EOF
node poc.mjs

Measured output (Node v25.5.0, Apple Silicon)

n=1000  bytes=8000    took 44 ms
n=2000  bytes=16000   took 159 ms
n=4000  bytes=32000   took 628 ms
n=8000  bytes=64000   took 2506 ms
n=16000 bytes=128000  took 9948 ms

Doubling N → ~4× wall-clock, consistent with O(N²).

markdown-it transitive (independently confirmed)

bash
npm install markdown-it@14.1.1
node -e "
  const md = require('markdown-it')({ linkify: true })
  for (const n of [1000, 2000, 4000, 8000]) {
    const evil = 'a@b.com '.repeat(n)
    const t0 = process.hrtime.bigint()
    md.render(evil)
    const ms = Number(process.hrtime.bigint() - t0) / 1e6
    console.log('n=' + n + ' bytes=' + evil.length + ' md.render=' + ms.toFixed(0) + 'ms')
  }
"
n=1000 bytes=8000   md.render=45ms
n=2000 bytes=16000  md.render=171ms
n=4000 bytes=32000  md.render=672ms
n=8000 bytes=64000  md.render=2636ms

Same quadratic curve. 64 KB is enough to burn 2.6 s in markdown-it.render().

Impact

  • Availability (High): A single HTTP request containing tens of KB of repeated email-like strings blocks one worker thread for seconds to tens of seconds. Under moderate concurrency (10-50 requests), the entire rendering tier of an affected service is wedged.
  • No confidentiality or integrity impact.

Real-world scenario: Any service that renders untrusted Markdown with linkify:true on the request path - Discourse, Mattermost, GitLab CE, AI chat UIs (Open WebUI, LibreChat), wiki/note apps using markdown-it - receives a post/comment containing 64 KB of "a@b.com ". The render call blocks the worker for 2.5+ seconds. Scripted at scale, this wedges the rendering tier.

Suggested remediation

The fix is algorithmic - convert the outer scan loop to stateful regex iteration so each character is examined a constant number of times:

  1. Add the g flag to email_fuzzy, link_fuzzy, link_no_ip_fuzzy, host_fuzzy_test in lib/re.mjs
  2. Rewrite test() (or add testAt(text, pos)) so fuzzy branches set re.lastIndex = pos and call re.exec(text) instead of text.match()/text.search() on a sliced tail
  3. In match(), drop tail = tail.slice(...) entirely - advance a pos offset instead

The schema branch at index.mjs:428-440 is already structured this way - it's the in-repo precedent for the fix.

js
// proposed sketch
LinkifyIt.prototype.match = function match (text) {
  const result = []
  let pos = 0
  while (this.testAt(text, pos)) {
    result.push(createMatch(this, 0))
    pos = this.__last_index__
  }
  return result.length ? result : null
}

Total cost becomes O(N): each character scanned at most once per regex across the whole loop.

Duplicate-risk analysis

  • Zero GHSAs on linkify-it (gh api /repos/markdown-it/linkify-it/security-advisories → [])
  • Zero OSV entries (api.osv.dev/v1/query → {})
  • markdown-it's only GHSA (CVE-2022-21670, "Possible ReDOS in newline rule") targets markdown-it's own newline regex, not the linkify pipeline

This finding appears novel.

Note to maintainers

Since markdown-it is the dominant consumer and shares maintainership (Vitaly Puzrin), a patched linkify-it release should be paired with a markdown-it minor that pins the new minimum version.

Executive ViewAI

Why It Matters

linkify-it (used by markdown-it in millions of web applications) allows unauthenticated attackers to remotely trigger denial-of-service conditions by submitting crafted text; publicly available exploit code exists but no active exploitation has been detected. Any organization running chat systems, forums, wikis, or AI interfaces with link detection enabled faces imminent risk of service outages if not patched. Patching is available and should be prioritized within 24 hours.

Business Impact: High
Compensating Controls

Apply vendor patch.

Threat Intelligence Overview

EPSS Score
0.4%
Top 64% exploitable
Prevalence
High
linkify engine behind markdown-it (~21.6M weekly npm downloads), widely deployed transitively
Threat Intel
1
1 source
Exploit
POC
Public exploit code exists
Social Media
1
Sightings
Data Quality
6 sources
Confidence: High

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity HIGH
CISA KEV No
Public POC Yes
EPSS 0.4%
Patch available Yes
Active threat No
Deciding rule
HIGH severity (CVSS 7.0-8.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Out-of-Cycle
Exploitation· derived
Poc
System Exposure· derived
Open
Automatable· derived
Yes
Human Impact
High

Priority Score

64
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +44
PoC: +20
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Denial of Service attacks render applications or systems unavailable by overwhelming resources or triggering failure conditions.

CVE-2025-25279 CRITICAL
9.9 Feb 24

Mattermost versions 10.4.x <= 10.4.1, 9.11.x <= 9.11.7, 10.3.x <= 10.3.2, 10.2.x <= 10.2.2 fail to properly validate boa

CVE-2022-4044 MEDIUM POC
6.5 Nov 23

A denial-of-service vulnerability in Mattermost allows an authenticated user to crash the server via multiple large auto

CVE-2022-3257 MEDIUM POC
6.5 Sep 23

Mattermost version 7.1.x and earlier fails to sufficiently process a specifically crafted GIF file when it is uploaded w

CVE-2025-4981 CRITICAL
9.9 Jun 20

A remote code execution vulnerability (CVSS 9.9) that allows authenticated users. Critical severity with potential for s

CVE-2025-20051 CRITICAL
9.9 Feb 24

Mattermost versions 10.4.x <= 10.4.1, 9.11.x <= 9.11.7, 10.3.x <= 10.3.2, 10.2.x <= 10.2.2 fail to properly validate inp

CVE-2026-4858 CRITICAL
9.9 May 21

Privilege escalation to full administrative control in Mattermost self-hosted server (versions 11.6.0 and earlier in the

CVE-2025-12421 CRITICAL
9.9 Nov 27

Mattermost versions 11.0.x <= 11.0.2, 10.12.x <= 10.12.1, 10.11.x <= 10.11.4, 10.5.x <= 10.5.12 fail to to verify that t

CVE-2025-12419 CRITICAL
9.9 Nov 27

Mattermost versions 10.12.x <= 10.12.1, 10.11.x <= 10.11.4, 10.5.x <= 10.5.12, 11.0.x <= 11.0.3 fail to properly validat

CVE-2023-6458 CRITICAL
9.8 Dec 06

Mattermost webapp fails to validate route parameters in/<TEAM_NAME>/channels/<CHANNEL_NAME> allowing an attacker to perf

CVE-2020-26276 CRITICAL
9.8 Dec 17

Fleet is an open source osquery manager. Rated critical severity (CVSS 9.8), this vulnerability is remotely exploitable,

CVE-2025-24490 CRITICAL
9.6 Feb 24

Mattermost versions 10.4.x <= 10.4.1, 9.11.x <= 9.11.7, 10.3.x <= 10.3.2, 10.2.x <= 10.2.2 fail to use prepared statemen

CVE-2024-39777 CRITICAL
9.6 Aug 01

Mattermost versions 9.9.x <= 9.9.0, 9.5.x <= 9.5.6, 9.7.x <= 9.7.5 and 9.8.x <= 9.8.1 fail to disallow unsolicited invit

Vendor StatusVendor

SUSE

Severity: Important
Product Status
SUSE Linux Enterprise High Performance Computing 12 Not-Affected
SUSE Linux Enterprise High Performance Computing 15 SP7 Not-Affected
SUSE Linux Enterprise High Performance Computing 15 SP7 Not-Affected
SUSE Linux Enterprise Module for Web and Scripting 15 SP7 Not-Affected
SUSE Linux Enterprise Module for Web and Scripting 15 SP7 Not-Affected

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy