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-VCVR-R3JV-PC5J

CVE-2026-94545: SVG-Serialization Markup Injection in Vercel Satori and Next.js ImageResponse

Alon Barad
Alon Barad
Software Engineer

Sep 30, 2026·7 min read·6 visits

Executive Summary (TL;DR)

Unsanitized user inputs passed to the Next.js `ImageResponse` wrapper are processed by the underlying Satori library without appropriate HTML/XML escaping. This allows attackers to break out of XML tag boundaries, inject arbitrary SVG nodes, and trigger downstream vulnerabilities in image-processing backends.

An improper output encoding and escaping vulnerability (CWE-116) in Vercel Satori allows unauthenticated remote attackers to perform markup injection in dynamic Open Graph images generated via Next.js's ImageResponse. Unsanitized parameter interpolation into SVG elements breaks XML structural boundaries. This exposes downstream parsing, rasterization, and rendering pipelines to Server-Side Request Forgery (SSRF), Local File Read, and Remote Code Execution (RCE).

Vulnerability Overview

The Vercel Satori library is a widely adopted utility designed to convert HTML and CSS layouts into vector-based SVG graphics. In modern web architectures, particularly within the Next.js ecosystem, this functionality is wrapped by the next/og ImageResponse API. It enables developers to generate dynamic Open Graph (OG) social card images on-demand. Because these endpoints accept user-controlled query parameters to customize the text and layout of the generated cards, they present a high-exposure attack surface.

Historically, security models assumed that the JSX-to-SVG conversion pipeline handled input sanitization implicitly. However, CVE-2026-94545 exposes a failure in this pipeline. The vulnerability is classified under CWE-116 (Improper Encoding or Escaping of Output). It allows an attacker to manipulate the structural integrity of the generated XML document, escaping defined text fields and introducing arbitrary vector elements.

When a vulnerable Next.js application hosts an active Node.js-based ImageResponse endpoint, a remote, unauthenticated attacker can supply crafted query parameters containing XML tags. Satori incorporates these inputs into its output string without appropriate sanitization. This structural breakdown allows attackers to target downstream PDF or PNG rendering engines, which often run in highly privileged internal network environments.

Root Cause Analysis

The structural flaw originates within Satori's serialization routine, specifically inside the translateSVGNodeToSVGString() function located in src/handler/preprocess.ts. Satori transforms incoming React-like JSX nodes into string-based SVG markup. During this process, elements were systematically interpolated using standard ES6 template literals instead of structured, schema-validated XML node construction.

In affected versions, Satori converted raw text nodes directly by executing String(node) and appending the result directly to the output. If the input contained valid XML control characters, such as </title>, the text node prematurely closed its parent element and initiated a new, attacker-controlled XML context. Satori failed to run an HTML/XML entity encoder on these variables before concatenation.

Furthermore, Satori processed attributes and inline styles unsafely. Attribute values were encapsulated inside double quotes but were not escaped to prevent double-quote breakouts. In addition, internal layout properties starting with an underscore character, such as _inheritedBackgroundClipTextPath, were exposed to style interpolation. Attackers could manipulate these properties to alter internal document layouts or inject inline elements.

Finally, when Satori generated embedded SVG elements within HTML data URLs, it applied a restrictive regex filter (/[ %#()<>?[\n]^{|}"']/g) to encode special characters. This encoding lookup omitted the ampersand character. As a result, XML entities like < or external entity declarations (&entity;`) bypassed the encoder, enabling raw XML injection in nested rendering contexts.

Code Analysis and Comparative Diff

To resolve the structural breakout, the Satori development team completely refactored the XML generation engine. They replaced template-literal concatenation with a strict, validation-driven XML builder pattern.

// Pre-Patch (Vulnerable Concatenation Loop in src/handler/preprocess.ts)
// Unescaped text node concatenation
if (typeof node !== 'object') return String(node);
 
// Unsafely interpolated attributes
let attrString = '';
for (const [key, value] of Object.entries(attrs)) {
  attrString += ` ${key}="${value}"`;
}
return `<${type}${attrString}>${children}</${type}>`;
// Post-Patch (Secured Validation and Serialization in src/utils.ts)
import escapeHTML from 'escape-html';
 
// 1. Strict validation of tag and attribute names to prevent parameter pollution
const XML_NAME_START_CHAR =
  ':A-Z_a-z\\u00C0-\\u00D6' +
  '\\u00D8-\\u00F6\\u00F8-\\u02FF' +
  '\\u0370-\\u037D\\u037F-\\u1FFF' +
  '\\u200C-\\u200D\\u2070-\\u218F' +
  '\\u2C00-\\u2FEF\\u3001-\\uD7FF' +
  '\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
const XML_NAME_CHAR =
  XML_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
const VALID_XML_NAME = new RegExp(
  '^[' + XML_NAME_START_CHAR + '][' + XML_NAME_CHAR + ']*$',
  'u'
);
 
export function assertValidXMLName(name: string, kind: 'element' | 'attribute') {
  if (!VALID_XML_NAME.test(name)) {
    throw new Error(`Invalid XML ${kind} name: ${JSON.stringify(name)}`);
  } 
}
 
// 2. Context-aware escaping utilities
export function escapeXMLAttribute(value: unknown): string {
  if (typeof value === 'number' || typeof value === 'boolean') {
    return '' + value;
  }
  return escapeHTML(String(value));
}
 
export function escapeXMLText(value: unknown): string {
  return escapeHTML(String(value));
}
 
// 3. Structured builder replacing template strings
export function buildXMLString(type: string, attrs: Record<string, unknown>, children?: string) {
  assertValidXMLName(type, 'element');
  let attrString = '';
  for (const [k, v] of Object.entries(attrs)) {
    if (typeof v !== 'undefined') {
      assertValidXMLName(k, 'attribute');
      attrString += ` ${k}="${escapeXMLAttribute(v)}"`;
    }
  }
  if (typeof children !== 'undefined') {
    return `<${type}${attrString}>${children}</${type}>`;
  }
  return `<${type}${attrString}/>`;
}

Additionally, the patch introduced an internal property check in Satori's style processing module (src/handler/expand.ts) that rejects keys prefixed with an underscore. The regex for embedded SVG symbols was updated to include the ampersand character, neutralizing entity-based injection vectors.

Exploitation and Payload Analysis

An analysis of the exploitation mechanics demonstrates that a remote attacker can execute arbitrary tag insertion by targeting any parameter placed within the dynamic ImageResponse layout structure. Consider a Next.js endpoint that reads the user's name from a query parameter and renders it inside a <title> tag.

An attacker supplies the following input vector via a GET parameter: </title><rect x="50" y="250" width="500" height="150" fill="red"/><title>

During processing, Satori generates the output string by wrapping the parameter with <title> tags. Due to the lack of output escaping, the raw output contains nested, valid SVG structures: <title></title><rect x="50" y="250" width="500" height="150" fill="red"/><title></title>

The downstream image-rendering library (typically a Rust-based or system-level library such as resvg) parses the generated string. It treats the injected <rect> as a root-level element, which is rasterized and rendered into the final PNG binary.

Automated scanning and verification of this vulnerability cannot rely on simple HTTP status codes or response size analysis. This is because both benign and malicious inputs return 200 OK statuses and valid PNG streams. Safe detection requires deep rendering side-channel analysis. This is achieved by deflating the PNG IDAT compression chunks, applying the inverse PNG filter, and programmatically counting the presence of injected colored pixels (such as the #FF0000 red pixels in the injected rectangle) at the target coordinates.

Downstream Impact and Attack Scenarios

The direct result of CVE-2026-94545 is arbitrary markup injection within vector image files. While the base CVSS score for Satori as an isolated markup generator is calculated as 5.3, the actual operational impact within a Next.js web application is Critical. The severity is determined by how the downstream server processes the generated SVG markup.

First, many modern serverless platforms convert SVG structures into PNG formats using headless instances of Chromium or other browser engines. Under these conditions, an injected element containing <iframe src="http://169.254.169.254/latest/meta-data/"/> or <image href="http://169.254.169.254/"/> can trigger Server-Side Request Forgery (SSRF). This allows attackers to access internal network endpoints, container orchestration metadata services, or local resources.

Second, if the downstream rasterizer supports inline scripting or XML External Entity (XXE) resolution, attackers can use the injection primitive to execute local file reads. They can exfiltrate sensitive files, such as environment variables, AWS session tokens, or local credentials.

Finally, if the underlying SVG compilation backend is linked to an unpatched system library (such as older versions of librsvg or ImageMagick), memory corruption or arbitrary command execution vulnerabilities can be triggered directly on the server host.

Defense and Remediation

Remediation of CVE-2026-94545 requires upgrading both the Next.js framework and any direct installations of the Satori package. Upgrading to Next.js 16.3.6 (or the backported security release 15.5.26) ensures the compiled @vercel/og bundle is updated to a secure version containing the structured XML serializer.

If immediate dependency updates are restricted by deployment freezes or legacy code constraints, developers must implement an input validation middleware. This middleware must sanitize query parameters before they reach the JSX structure. Using a regex-based substitution pattern to strip or encode critical XML control characters prevents tag escape sequences from being processed by Satori.

function sanitizeForSVG(input: string): string {
  return input.replace(/[&<>"']/g, (char) => {
    switch (char) {
      case '&': return '&amp;';
      case '<': return '&lt;';
      case '>': return '&gt;';
      case '"': return '&quot;';
      case "'": return '&#39;';
      default: return char;
    }
  });
}

Additionally, applications can migrate their dynamic Open Graph routes to run exclusively within the Edge Runtime instead of the Node.js environment. The Edge Runtime implementation of ImageResponse does not leverage the vulnerable downstream compilation paths, mitigating the exposure vector. Deploying restrictive Web Application Firewall (WAF) rules that detect nested XML tags inside image-related query parameters can provide supplementary edge detection.

Official Patches

Vercel SatoriOfficial Satori Repository Security Advisory
Vercel Next.jsOfficial Next.js Repository Security Advisory

Fix Analysis (2)

Technical Appendix

CVSS Score
9.8/ 10
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H
45,000
via Project-internal internet exposure estimation models

Affected Systems

Next.js Node.js-runtime dynamic Open Graph image generation pipelinesServerless functions executing next/og ImageResponse APIsCustom Node.js applications consuming standalone Vercel Satori packages for HTML/CSS-to-SVG conversion

Affected Versions Detail

Product
Affected Versions
Fixed Version
Next.js
Vercel
>= 16.2.0, < 16.3.616.3.6
Satori
Vercel
>= 0.0.27, < 0.33.50.33.5
AttributeDetail
CWE IDCWE-116 (Improper Encoding or Escaping of Output)
Attack VectorNetwork (AV:N) / Unauthenticated Remote HTTP Request
CVSS v4.0 Score9.8 (Critical Framework-Level Impact)
EPSS ScoreNot Available
Exploit StatusProof-of-Concept (PoC) documented and verified via rendering side-channel
CISA KEV StatusNo (Not currently listed in the CISA KEV catalog)
Mitigation StrategyUpdate Next.js or enforce strict input escaping and Edge runtime configurations

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1210Exploitation of Remote Services
Lateral Movement
T1059Command and Scripting Interpreter
Execution
CWE-116
Improper Encoding or Escaping of Output

Satori does not properly sanitize or escape input values before incorporating them into the generated XML-based SVG output. This permits a structural breakdown of the document boundaries, leading to arbitrary XML/SVG injection.

Known Exploits & Detection

GitHubVerification lab containing vulnerable test servers and an automated python scanner utilizing a PNG rendering side-channel decoding script

Vulnerability Timeline

Satori releases patch 0.33.5 resolving structural SVG template breakout
2026-09-22
Next.js releases version 16.3.6 (and 15.5.26) embedding secured @vercel/og packages
2026-09-22
Netlify broadcasts emergency platform patch instructions for hosted Next.js sites
2026-09-22
Security researcher Hassham1 publishes the first automated validation PoC utilizing PNG side-channel analysis
2026-09-24
Vulnerability database registers official CVSS ratings and primary CWE designations
2026-09-30

References & Sources

  • [1]Next.js Security Advisory GHSA-vcvr-r3jv-pc5j
  • [2]Satori Security Advisory GHSA-wx4j-mvgx-mqwp
  • [3]Satori Patch Commit 26a52affc
  • [4]Next.js Patch Commit 868fad3
  • [5]Satori Core Serialization PR 814
  • [6]Next.js v16.3.6 Release Tags
  • [7]Next.js Security Blog Update September 2026
  • [8]Netlify Customer Advisory on ImageResponse vulnerability
  • [9]Hassham1 CVE-2026-94545 next/og loopback test lab and scanner
Related Vulnerabilities
CVE-2026-94545

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

•18 minutes ago•CVE-2026-97711
2.3

CVE-2026-97711: Cross-Site Scripting (XSS) via Unescaped Script-Closing Tags in serialize-javascript

A security vulnerability in serialize-javascript v7.1.1 allows Cross-Site Scripting (XSS) due to an overly greedy regular expression (SCRIPT_CLOSE_REGEXP) used during function serialization. Two secondary defects involving a spoofed toString() validation bypass and a stateful native code validator are also addressed in the fixed version v7.1.2.

Alon Barad
Alon Barad
0 views•7 min read
•about 1 hour ago•CVE-2026-101918
5.3

CVE-2026-101918: Unauthenticated Denial of Service via Recursion Exhaustion in PyJWT

A Denial of Service (DoS) vulnerability exists in the PyJWT library when parsing unverified token payloads containing deeply nested JSON structures. Because PyJWT fails to catch RecursionError during payload parsing, an unauthenticated remote attacker can crash the application thread or worker by sending a specially crafted token.

Alon Barad
Alon Barad
5 views•5 min read
•about 3 hours ago•CVE-2026-102265
5.3

CVE-2026-102265: Unhandled RecursionError in PyJWT JSON Parser Leading to Denial of Service

An uncontrolled recursion vulnerability exists in PyJWT from version 2.13.0 to 2.14.0. The vulnerability allows remote, unauthenticated attackers to cause a Denial of Service (DoS) via crafted JWT headers that trigger stack exhaustion during JSON decoding.

Amit Schendel
Amit Schendel
6 views•5 min read
•about 4 hours ago•CVE-2026-102266
7.4

CVE-2026-102266: Signature Verification Bypass in PyJWT via Empty JWK

A signature verification bypass vulnerability in PyJWT allows unauthenticated remote attackers to forge JSON Web Tokens when processing JSON Web Key Sets containing an empty symmetric key.

Alon Barad
Alon Barad
5 views•8 min read
•about 5 hours ago•GHSA-V53P-9FQP-M79J
7.5

GHSA-V53P-9FQP-M79J: Regular Expression Denial of Service (ReDoS) in Nodemailer addressparser

A Regular Expression Denial of Service (ReDoS) vulnerability exists in Nodemailer's addressparser fallback engine before version 10.0.6. Under specific malformed inputs with excessive word boundaries, the parser exhibits quadratic backtracking, leading to high CPU utilization and event loop blockage.

Amit Schendel
Amit Schendel
5 views•6 min read
•about 6 hours ago•GHSA-G57G-F23G-4646
6.5

GHSA-G57G-F23G-4646: Parser Differential and SMTP Injection in Nodemailer Address Parser

Nodemailer versions prior to 10.0.9 are vulnerable to a parser differential bug. When processing a quoted local-part followed by an RFC 5322 comment and trailing characters, the internal addressparser module fails to order its normalization routine correctly. This error results in the generation of malformed envelope recipient addresses containing injected whitespace and secondary domains, allowing attackers to bypass routing restrictions and exfiltrate sensitive emails.

Alon Barad
Alon Barad
4 views•7 min read