CVE-2026-55847
Published:June 19, 2026
Updated:June 21, 2026
Summary The "ansi.js" Handlebars helper in allure-generator passes user-controlled "statusMessage" and "statusTrace" values from test result files through the "ansi-to-html" library and wraps the output in Handlebars "SafeString" without HTML escaping. Since "ansi-to-html" does not escape HTML entities by default, an attacker who can influence test result content (e.g., via crafted JUnit XML failure messages) can inject arbitrary JavaScript that executes when anyone views the generated Allure report. Details The vulnerability is an incomplete fix — commit "4c64b19" (PR #3271) fixed XSS in "linky.js" and "text-with-links.js" by adding "escapeExpression()", but the same pattern in "ansi.js" was not addressed. Vulnerable sink — "allure-generator/src/main/javascript/helpers/ansi.js:10-11": export default function (input) { return new SafeString(ansiConverter.toHtml(input)); }; The "AnsiToHtml" constructor at line 4 does not set "escapeForHtml: true": const ansiConverter = new AnsiToHtml({ fg: "black", bg: "black", newline: true, }); The "ansi-to-html" library (v0.7.2) defaults "escapeForHtml" to "false", meaning HTML entities in the input pass through unchanged. Wrapping the result in "SafeString" tells Handlebars to skip its auto-escaping, so the raw HTML reaches the browser. Template usage — "allure-generator/src/main/javascript/blocks/status-details/status-details.hbs:7,10": <pre class="status-details__message"><code>{{ansi statusMessage}}</code></pre>... <pre class="{{b 'status-details' 'trace'}}"><code>{{ansi statusTrace}}</code></pre>Source — "plugins/junit-xml-plugin/src/main/java/io/qameta/allure/junitxml/JunitXmlPlugin.java:307-308": result.setStatusMessage(element.getAttribute(MESSAGE_ATTRIBUTE_NAME)); result.setStatusTrace(element.getValue()); These values are read directly from XML attributes with no sanitization. The same pattern exists in TRX, xUnit XML, xctest, and Allure1/2 plugins. Contrast with the fixed helper — "linky.js" (post-fix) correctly escapes before wrapping in "SafeString": const safeText = escapeExpression(text); return new SafeString("<a href="${safeText}" ...>${safeText}</a>"); PoC 1. Create a malicious JUnit XML test result file: <?xml version="1.0" encoding="UTF-8"?><testsuite name="XSSTest" tests="1" failures="1"> <testcase name="xssPayload" classname="com.example.Test"> <failure message="<img src=x onerror=alert(document.cookie)>"> Stack trace: <img src=x onerror=alert('statusTrace_XSS')> </failure> </testcase> </testsuite> 2. Generate an Allure report: allure generate /path/to/results-with-malicious-xml -o /tmp/allure-report 3. Open the report and navigate to the failed test case: allure open /tmp/allure-report 4. When viewing the test's status details, the "<img onerror>" payloads execute JavaScript in the viewer's browser. Impact - Arbitrary JavaScript execution in the browser of anyone viewing the generated Allure report - Cookie theft, session hijacking if the report is served from a domain with active sessions (e.g., CI dashboards) - Data exfiltration — the injected script can read the full report content and send it to an attacker-controlled server - Attack vectors: A malicious dependency that throws crafted exception messages, a CI pipeline processing test results from untrusted pull requests, or a contributor submitting test files containing XSS payloads - Allure reports are commonly hosted on CI/CD platforms (Jenkins, GitLab, GitHub Actions artifacts) where session cookies may be present Recommended Fix Configure "AnsiToHtml" with "escapeForHtml: true" to escape HTML entities while preserving ANSI-to-HTML conversion: import AnsiToHtml from "ansi-to-html"; import {SafeString} from "handlebars/runtime"; const ansiConverter = new AnsiToHtml({ fg: "black", bg: "black", newline: true, escapeForHtml: true, // Escape HTML entities in non-ANSI input }); export default function (input) { return new SafeString(ansiConverter.toHtml(input)); }; This is the correct approach because it preserves the ANSI escape sequence → HTML conversion (colored output) while ensuring that any non-ANSI HTML in the input is safely escaped. The alternative of using "escapeExpression()" on the input would destroy ANSI sequences before they could be converted.
Affected Packages
https://github.com/allure-framework/allure2.git (GITHUB):
Affected version(s) >=2.0-BETA8 <2.39.0Fix Suggestion:
Update to version 2.39.0io.qameta.allure:allure-generator (JAVA):
Affected version(s) >=2.0-BETA8 <2.39.0Fix Suggestion:
Update to version 2.39.0Related Resources (2)
Do you need more information?
Contact UsCVSS v4
Base Score:
5.3
Attack Vector
NETWORK
Attack Complexity
LOW
Attack Requirements
NONE
Privileges Required
NONE
User Interaction
PASSIVE
Vulnerable System Confidentiality
LOW
Vulnerable System Integrity
LOW
Vulnerable System Availability
NONE
Subsequent System Confidentiality
LOW
Subsequent System Integrity
LOW
Subsequent System Availability
NONE
CVSS v3
Base Score:
6.1
Attack Vector
NETWORK
Attack Complexity
LOW
Privileges Required
NONE
User Interaction
REQUIRED
Scope
CHANGED
Confidentiality
LOW
Integrity
LOW
Availability
NONE
Weakness Type (CWE)
Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')