Updated email sending to remove invalid recipient emails (#16171)

closes https://github.com/TryGhost/Team/issues/2388

We have seen examples of sites with member emails that have invalid characters that can cause an entire email send to fail, or just cause a failure to those addresses. The issue that allowed members with invalid email address to be saved was patched earlier, but its possible there are still sites that contain some of those invalid email addresses.

This change updates new sending service to filter out the recipients with invalid email address before passing them to mail provider, so these rogue addresses don't affect the whole batch in anyway. We also trim the recipient emails to clear out any spaces first, which is the most likely culprit.

- uses new email validator that detects invalid email addresses with special chars
This commit is contained in:
Rishabh Garg 2023-01-24 16:13:10 +05:30 committed by GitHub
parent 9df131ee5a
commit 88979c852b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 1 deletions

View File

@ -1,3 +1,6 @@
const validator = require('@tryghost/validator');
const logging = require('@tryghost/logging');
/**
* @typedef {object} EmailData
* @prop {string} html
@ -111,7 +114,7 @@ class SendingService {
buildRecipients(members, replacementDefinitions) {
return members.map((member) => {
return {
email: member.email,
email: member.email?.trim(),
replacements: replacementDefinitions.map((def) => {
return {
id: def.id,
@ -120,6 +123,13 @@ class SendingService {
};
})
};
}).filter((recipient) => {
// Remove invalid recipient email addresses
const isValidRecipient = validator.isEmail(recipient.email, {legacy: false});
if (!isValidRecipient) {
logging.warn(`Removed recipient ${recipient.email} from list because it is not a valid email address`);
}
return isValidRecipient;
});
}
}

View File

@ -32,6 +32,7 @@
"@tryghost/logging": "2.4.0",
"@tryghost/tpl": "0.1.21",
"bson-objectid": "2.0.4",
"@tryghost/validator": "^0.2.0",
"cheerio": "0.22.0",
"handlebars": "4.7.7",
"juice": "8.1.0",

View File

@ -102,6 +102,66 @@ describe('Sending service', function () {
));
});
it('removes invalid recipients before sending', async function () {
const sendingService = new SendingService({
emailRenderer,
emailProvider
});
const response = await sendingService.send({
post: {},
newsletter: {},
segment: null,
emailId: '123',
members: [
{
email: 'member@example.com',
name: 'John'
},
{
email: 'member+invalid@example.com<6F>',
name: 'John'
}
]
}, {
clickTrackingEnabled: true,
openTrackingEnabled: true
});
assert.equal(response.id, 'provider-123');
sinon.assert.calledOnce(sendStub);
assert(sendStub.calledWith(
{
subject: 'Hi',
from: 'ghost@example.com',
replyTo: 'ghost+reply@example.com',
html: '<html><body>Hi {{name}}</body></html>',
plaintext: 'Hi',
emailId: '123',
replacementDefinitions: [
{
id: 'name',
token: '{{name}}',
getValue: sinon.match.func
}
],
recipients: [
{
email: 'member@example.com',
replacements: [{
id: 'name',
token: '{{name}}',
value: 'John'
}]
}
]
},
{
clickTrackingEnabled: true,
openTrackingEnabled: true
}
));
});
it('maps null replyTo to undefined', async function () {
const sendingService = new SendingService({
emailRenderer,