Dependency-Track is an open-source platform that helps organizations keep tabs on risks in their software supply chain by analyzing open source components. One of its key modules is @dependencytrack/frontend, a Single Page Application (SPA) that makes it easy to browse vulnerabilities, manage projects, and remediate risks. But in late 2022, a critical security flaw—CVE-2022-39350—was discovered, allowing attackers to run arbitrary JavaScript in the browser of unsuspecting users. Here’s a deep dive into what happened, how it worked, how attackers could abuse it, and how you can keep your instance secure.
What Was the Problem?
Dependency-Track’s frontend displays vulnerability details to its users. Vulnerability information is often provided in Markdown, which is a format used for simple documentation and descriptions. To render all this Markdown as HTML for display in the browser, Dependency-Track used a JavaScript library called Showdown.
But here’s the catch: Showdown doesn’t sanitize or encode output by default, and up until version 4.6.1, Dependency-Track’s frontend didn’t add any extra protection. This meant that if anyone entered malicious HTML or script code inside a vulnerability’s details (such as its Description, Details, Recommendation, or References), that code could be executed in the browser of any user viewing the page—this is known as Cross-Site Scripting (XSS).
Who could inject malicious code?
Only users with the VULNERABILITY_MANAGEMENT permission could create or edit custom vulnerabilities with XSS payloads. So this is not a bug any random person on the internet could exploit—attackers would need an account with those privileges.
Anyone with the VIEW_PORTFOLIO permission who visits the infected vulnerability page.
Additionally, although theoretically possible, it was highly unlikely that a public vulnerability database would distribute a malicious payload because such databases are well-maintained. In reality, insider threats or compromised privileged accounts posed the largest risk.
A Simple Exploit Example
Here’s what an attacker with VULNERABILITY_MANAGEMENT permission might input in the Description field of a custom vulnerability:
# Important Security Update
Watch out!
<img src="x" onerror="alert('XSS by CVE-2022-39350')" />
Or, using inline HTML and JavaScript
Click me:
<a href="#" onclick="alert('Hacked by CVE-2022-39350')">Click!</a>
When the vulnerability details are viewed in the Dependency-Track frontend, the Markdown parser (Showdown) turns this text into HTML, and the browser executes the JavaScript, popping up an alert.
In a real attack, the script wouldn’t just show a message—it could steal user sessions, modify page content, or mount further attacks on backend services.
Real-World Impact
While the access required to exploit this bug was limited to users with management privileges, in modern organizations, such permissions might be held by dozens of people, especially if onboarding isn’t tight or secrets leak.
Let’s break down the technical chain
1. Markdown Input from Users: Dependency-Track lets users describe vulnerabilities using Markdown in various fields.
Showdown Renders Markdown: The frontend uses Showdown to turn Markdown into HTML.
3. No Sanitization: The output from Showdown is sent straight into the DOM—no checks, no cleaning, no escaping.
4. Malicious JavaScript Runs: If a user put in <img src="/" onerror="alert('xss')">, any viewer’s browser would run that JavaScript.
Showdown explicitly documents that it provides NO XSS protection—developers must sanitize Markdown output themselves.
Old code (rough example)
import showdown from 'showdown';
const converter = new showdown.Converter();
function renderMarkdown(input) {
// DANGEROUS: No sanitization!
return converter.makeHtml(input);
}
// Output sent directly to React dangerouslySetInnerHTML or similar
Exploit
const payload = '<img src=x onerror="alert(\'CVE-2022-39350\')">';
renderMarkdown(payload); // HTML with script runs in the browser!
How Was It Fixed?
The fix was released in frontend version 4.6.1. The Dependency-Track team now properly sanitizes all Markdown output after conversion. This means that any embedded scripts or event handlers (like onerror, onclick, and so on) are removed before putting the HTML into the page.
Fixed code
import showdown from 'showdown';
import DOMPurify from 'dompurify';
const converter = new showdown.Converter();
function renderMarkdown(input) {
const html = converter.makeHtml(input);
return DOMPurify.sanitize(html); // Now safe!
}
If you’re using Dependency-Track, here’s what you should do
- Update @dependencytrack/frontend to at least version 4.6.1 (see GitHub advisory).
Useful References
- Official advisory
- Original CVE record
- Showdown XSS warning
- Dependency-Track frontend releases
Conclusion
CVE-2022-39350 might look like a simple bug, but it’s a reminder that:
Timeline
Published on: 10/25/2022 17:15:00 UTC
Last modified on: 10/28/2022 19:24:00 UTC