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



CVE-2026-71497

CVE-2026-71497: Parser-Browser Desynchronization leading to XSS in jsoup Sanitizer

Amit Schendel
Amit Schendel
Senior Security Researcher

Aug 7, 2026·7 min read·1 visit

Executive Summary (TL;DR)

A parser differential between HTML5 specifications and Java's string-trimming rules in jsoup versions 1.14.3 to 1.23.0 allows unauthenticated attackers to bypass HTML sanitizers and execute arbitrary scripts in a victim's browser context.

jsoup is a widely used Java library for working with real-world HTML. Versions 1.14.3 up to but excluding 1.23.1 contain a Cross-Site Scripting (XSS) vulnerability. When an application configures a custom Safelist that explicitly permits certain raw-text or RCDATA elements, such as style, title, or iframe, an attacker can exploit a parser-browser desynchronization flaw to bypass sanitization. This is achieved by utilizing trailing ASCII control characters that are handled differently by the HTML5 parsing specification and Java's string normalization methods, resulting in unescaped markup execution on the client side.

Vulnerability Overview

The jsoup library is a core security dependency in many Java web applications, serving as the primary validation boundary to sanitize user-submitted HTML before persistence or display. The sanitizer relies on a structured node traversal of the parsed Document Object Model (DOM) to strip unapproved tags and attributes based on a defined Safelist. However, when a security mechanism depends on an intermediate parser parsing and neutralizing input, any discrepancy between the parser's logic and the client-side browser's engine can introduce severe vulnerabilities.

CVE-2026-71497 highlights a critical parser-browser desynchronization flaw in jsoup versions ranging from 1.14.3 up to 1.23.0. The vulnerability is classified under CWE-79 (Improper Neutralization of Input During Web Page Generation) and manifests when applications allow raw-text container elements (such as style, title, or iframe) in their custom Safelist configurations. By appending specific trailing control characters to permitted tag names, an attacker can disrupt the tokenizer's state transitions without failing the final tag name lookup.

The resulting parsed DOM representation remains out of sync with the underlying tree-structure rules. The subsequent serialization process outputs unescaped HTML characters that the client-side browser's HTML5 parser interprets as executable DOM elements. This allows the attacker to smuggle malicious script blocks past the jsoup sanitizer and execute arbitrary JavaScript in the context of the user's active session.

Root Cause Analysis

The root cause of CVE-2026-71497 lies in a fundamental behavioral discrepancy between the HTML5 parsing specification's definition of whitespace and the character-stripping logic of Java's native java.lang.String.trim() method. This discrepancy allows malformed element tags to undergo unequal sanitization and tokenization steps.

Under the HTML5 tokenizer standard, only five specific characters are recognized as valid whitespace or delimiters to terminate a tag name: Tab (U+0009), Newline (U+000A), Form Feed (U+000C), Carriage Return (U+000D), and Space (U+0020). Any other ASCII control character—including those under U+0020, such as the unit separator (U+001E) or start of heading (U+0001)—is parsed as a valid constituent character of the tag name itself. Consequently, when jsoup's Tokeniser encounters <style\u001E>, it registers the literal tag name as "style\u001E".

Because the exact tokenized string "style\u001E" is not an exact match for the predefined tag "style", the parser does not transition into the RAWTEXT state. It remains in the default Data state, which treats subsequent elements like < and > as standard HTML entities, decoding them into literal < and > characters within the text nodes. However, when resolving the Tag object for the parsed element, jsoup historically invoked Tag.valueOf() which normalization-trimmed the tag name using String.trim(). Since String.trim() strips all ASCII characters less than or equal to U+0020, the trailing control character U+001E was removed, normalizing "style\u001E" to "style". This discrepancy approved the element under the custom Safelist while preserving decoded executable code inside its text child nodes.

Code Analysis

The technical vulnerability was remediated in jsoup version 1.23.1. The primary structural change prevents the parser from executing unsafe trimming operations on internal tokenizer-supplied tag names. Reviewing the patch in TagSet.java reveals how the unconditional trimming was restricted to only execute on raw public API entries.

File: src/main/java/org/jsoup/parser/TagSet.java
@@ -137,7 +137,7 @@ private void doAdd(Tag tag) {
     Tag valueOf(String tagName, @Nullable String normalName, String namespace, boolean preserveTagCase) {
         Validate.notNull(tagName);
         Validate.notNull(namespace);
-        tagName = tagName.trim();
+        if (normalName == null) tagName = tagName.trim(); // public API input; tokenizer names are already delimited
         Validate.notEmpty(tagName);
         Tag tag = get(tagName, namespace);
         if (tag != null) return tag;

In addition to the changes in TagSet.java, Token.java was refactored to eliminate dependency on ParseSettings.normalName(). The previous implementation in ParseSettings.normalName() used normalization steps that ultimately performed trimming, masking the presence of control characters. In the patched code, normalName is resolved using a simple lowercasing routine, preserving the trailing control character in the token throughout the tokenization and tree-building sequence.

File: src/main/java/org/jsoup/parser/Token.java
@@ -312,7 +314,7 @@ final void appendTagName(String append) {
             // might have null chars - need to replace with null replacement character
             append = append.replace(TokeniserState.nullChar, Tokeniser.replacementChar);
             tagName.append(append);
-            normalName = ParseSettings.normalName(tagName.value());
+            normalName = lowerCase(tagName.value());
         }

This remediation ensures the fix is complete. By retaining the control characters inside the parsed token's name (e.g., keeping it as "style\u001E" instead of normalizing it to "style"), the resulting DOM node is associated with an unknown, custom tag. When the Jsoup Cleaner processes this element, it checks the unknown tag against the permitted safelist. Since "style\u001E" is not defined in any custom safelist, the entire tag and its malicious payload are completely stripped from the final cleaned DOM.

Attack Methodology and Proof-of-Concept

To construct a successful exploit payload against CVE-2026-71497, the attacker must target an application that validates HTML utilizing a custom Safelist configured to accept raw-text container elements. In this scenario, we use the style tag to demonstrate the exploit mechanics.

An attacker crafts a payload utilizing the Unicode unit separator control character (represented as \u001E or \x1E) at the boundary of the style tag:

<style\u001E>&lt;/style&gt;&lt;script&gt;alert(document.domain)&lt;/script&gt;</style\u001E>

The following code snippet illustrates the vulnerable application processing this input payload:

import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;
 
public class JsoupBypassPoC {
    public static void main(String[] args) {
        // Define a custom Safelist that allows raw-text style tags
        Safelist customSafelist = Safelist.none().addTags("style");
        
        // The payload uses the \u001E control character directly following 'style'
        String payload = "<style\u001E>&lt;/style&gt;&lt;script&gt;alert(document.domain)&lt;/script&gt;</style\u001E>";
        
        // Process and sanitize the payload using the vulnerable jsoup library
        String sanitizedHtml = Jsoup.clean(payload, customSafelist);
        
        // The output contains unescaped, active script blocks
        System.out.println("Sanitized Output: " + sanitizedHtml);
    }
}

Upon execution, jsoup's parser registers the tag as style\u001E, stays in the Data state, and decodes the entity tokens </style><script>alert(document.domain)</script>. During DOM lookup, the name is trimmed to style, matching the safelist rule. When jsoup's serializer formats the approved style node, it emits the children as raw, unescaped text. The output is serialized as <style></style><script>alert(document.domain)</script></style>. A victim's browser parsing this serialized HTML terminates the empty style block and immediately executes the script block.

Impact Assessment

The primary impact of CVE-2026-71497 is unauthenticated Cross-Site Scripting (XSS). An attacker can execute arbitrary script code within the context of a victim's browser session. Depending on the target web application, this script execution can lead to session hijacking via cookie theft, data exfiltration, localized browser storage access, or DOM hijacking to capture user credentials.

This vulnerability is scored as a CVSS v3.1 base score of 4.7 (Medium). The exploitability is limited by high Attack Complexity (AC:H) because the vulnerability cannot be exploited in default configurations. It requires the targeted application to explicitly implement a custom Safelist that permits raw-text elements like style, title, or iframe.

While the baseline score is Medium, the impact is classified as Low for confidentiality and integrity under standard CVSS criteria, but the practical severity is heavily dependent on application deployment. For instance, applications that allow users to submit custom CSS or inline frames to personalize UI elements are at higher risk. To date, this vulnerability has not been added to CISA's Known Exploited Vulnerabilities catalog, and exploit maturity remains at the proof-of-concept level.

Remediation and Mitigation

To remediate CVE-2026-71497, organizations should upgrade their jsoup dependencies to version 1.23.1 or newer. This version completely resolves the tag name normalization mismatch between the tokenizer and the DOM tree builder.

For Maven projects, update the dependency configuration block within your pom.xml:

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.23.1</version>
</dependency>

For Gradle projects, update the implementation line within your build.gradle file:

implementation 'org.jsoup:jsoup:1.23.1'

If upgrading is not immediately possible, implement temporary mitigation strategies. First, review custom Safelist configurations and remove permissions for elements that alter parser state (such as style, title, and iframe). Second, implement a strict Content Security Policy (CSP) that enforces script-src directives with nonces or SHA hashes, and explicitly disables unsafe-inline. This serves as a secondary barrier to block execution of script tags even if a sanitizer bypass occurs.

Official Patches

jsoupOfficial patch commit addressing the normalization desynchronization.

Fix Analysis (1)

Technical Appendix

CVSS Score
4.7/ 10
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:N

Affected Systems

Java Web Applications utilizing the jsoup HTML parser library versions >= 1.14.3 and < 1.23.1 with custom Safelist configurations.

Affected Versions Detail

Product
Affected Versions
Fixed Version
jsoup
jsoup
>= 1.14.3, < 1.23.11.23.1
AttributeDetail
CWE IDCWE-79 (Cross-site Scripting)
Attack VectorNetwork (AV:N)
CVSS Base Score4.7 (Medium)
EPSS Score0.00043
Exploit StatusProof-of-Concept (PoC)
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1189Drive-by Compromise
Initial Access
T1185Browser Session Hijacking
Collection
CWE-79
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')

The product does not neutralize or incorrectly neutralizes user-controlled input before it is placed in output that is used as a web page that is served to other users.

Known Exploits & Detection

GitHub Security AdvisoryOfficial GHSA advisory detailing the vulnerability and the reproduction methodology.

Vulnerability Timeline

Vulnerability identified and fix committed to GitHub repository
2023-11-12
jsoup version 1.23.1 released containing the security fix
2023-11-15

References & Sources

  • [1]jsoup Security Advisory GHSA-pmhh-3w7g-xqp8
  • [2]jsoup Issue Tracker #2538
  • [3]jsoup Release Version 1.23.1

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

•about 2 hours ago•GHSA-W9HM-4M3M-FXMM
8.6

GHSA-W9HM-4M3M-FXMM: Arbitrary JavaScript Execution via Malicious PDF Parsing in ngx-extended-pdf-viewer

The ngx-extended-pdf-viewer library embeds a version of Mozilla's pdf.js that contains vulnerability CVE-2026-16633. This vulnerability allows arbitrary JavaScript execution (XSS) upon rendering a malicious PDF file.

Alon Barad
Alon Barad
1 views•5 min read
•about 3 hours ago•CVE-2026-71430
6.2

CVE-2026-71430: Denial of Service via Native Assertion Failure in node-re2 Replace Operation

A denial-of-service vulnerability in node-re2 prior to version 1.25.1 allows attackers to trigger uncatchable native assertion failures in the Google V8 engine. By supplying output-amplifying replacement templates, an attacker can exceed V8 string limits, resulting in an immediate process crash.

Alon Barad
Alon Barad
2 views•6 min read
•about 4 hours ago•CVE-2026-71498
5.1

CVE-2026-71498: Out-of-bounds Heap Read in node-re2 via Truncated Multi-byte UTF-8 Characters

A medium-severity out-of-bounds (OOB) heap read vulnerability exists in node-re2 prior to version 1.26.1. When a raw binary Node.js Buffer with a truncated multi-byte UTF-8 character at its end is passed to the C++ native addon, the internal lookahead routine getUtf8CharSize() over-reads up to 3 bytes from the heap, leading to memory disclosure.

Alon Barad
Alon Barad
2 views•6 min read
•about 5 hours ago•CVE-2026-67434
7.3

CVE-2026-67434: OS Command Injection via Malicious Filenames in PHP_CodeSniffer Blame Reports

A critical OS command injection vulnerability exists in PHP_CodeSniffer's VCS blame report modules (Gitblame, Hgblame, Svnblame). Due to inadequate escaping of filenames passed to shell execution wrappers like popen(), an attacker who commits a file with a maliciously crafted name can execute arbitrary commands when the victim generates a blame report.

Alon Barad
Alon Barad
5 views•6 min read
•about 6 hours ago•GHSA-2RP4-X2J7-QMCC
8.2

GHSA-2RP4-X2J7-QMCC: Stored Cross-Site Scripting via Draft Names in Craft CMS Control Panel

An authenticated stored Cross-Site Scripting (XSS) vulnerability exists in the Control Panel helper of Craft CMS before version 5.10.8. Due to lack of HTML entity encoding within the elementLabelHtml method, unescaped draft names are rendered directly into administrative interfaces.

Amit Schendel
Amit Schendel
3 views•6 min read
•about 7 hours ago•GHSA-7HXC-F267-H5Q7
4.9

GHSA-7HXC-F267-H5Q7: Path Traversal via Validation-then-Normalization in Craft CMS

A path traversal vulnerability exists in the local filesystem driver of Craft CMS. Due to validation occurring before path normalization, directory containment checks can be bypassed by utilizing specific protocol schemes like 'file://' along with directory traversal sequences. This allows authenticated users with administrative privileges to access or manipulate files outside the defined storage root directory.

Alon Barad
Alon Barad
3 views•8 min read