Skip to content

Commit

Permalink
fix: fixed outlook DMARC issue, switched default code from 421 to 550…
Browse files Browse the repository at this point in the history
… for nonexistent aliases, fixed privacy protection with Received/X-Original-To header, fixed ARC sealing after message modifications, fixed header potential mutation, fixed duplicate FE header issue, updated FAQ, bump locales, enhance test edge cases
  • Loading branch information
titanism committed Jan 12, 2025
1 parent f348b8e commit 592f30e
Show file tree
Hide file tree
Showing 40 changed files with 456 additions and 342 deletions.
13 changes: 9 additions & 4 deletions app/models/aliases.js
Original file line number Diff line number Diff line change
Expand Up @@ -369,15 +369,20 @@ Aliases.pre('validate', function (next) {
});
}

// trim and convert to lowercase
this.name = this.name.trim().toLowerCase();

// require alias name
if (
!quotedEmailUserUtf8.test(this.name.trim().toLowerCase()) ||
(!this.name.trim().startsWith('/') && this.name.includes('+'))
!quotedEmailUserUtf8.test(this.name) ||
(!this.name.startsWith('/') && this.name.includes('+'))
)
return next(Boom.badRequest('Alias name was invalid.'));

// trim and convert to lowercase
this.name = this.name.trim().toLowerCase();
//
// TODO: allow + symbol if the domain is not global
// and if the owner of the alias also owns the non + version or if the non + version does not exist yet
//

// if it consists of wildcards only then convert to wildcard "*" single asterisk
if (
Expand Down
56 changes: 26 additions & 30 deletions app/views/faq/index.md

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions helpers/get-bounce-info.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,7 @@ function getBounceInfo(err) {
bounceInfo.action = 'defer';
}

// <https://github.com/zone-eu/zone-mta/issues/434>
if (response.startsWith('DMARC ') || response.includes(' DMARC ')) {
bounceInfo.category = 'dmarc';
bounceInfo.action = 'defer';
} else if (
if (
// WHM/cPanel generic country error
response.includes('Your country is not allowed to connect to this server')
) {
Expand Down Expand Up @@ -316,6 +312,12 @@ function getBounceInfo(err) {
)
bounceInfo.category = 'spam';

// <https://github.com/zone-eu/zone-mta/issues/434>
if (response.startsWith('DMARC ') || response.includes(' DMARC ')) {
bounceInfo.category = 'dmarc';
bounceInfo.action = 'defer';
}

return bounceInfo;
}

Expand Down
4 changes: 2 additions & 2 deletions helpers/get-forwarding-addresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ async function getForwardingAddresses(
if (!isSANB(record) && !hasIMAP)
throw new SMTPError(
`${address} is not yet configured with its email service provider ${config.urls.web} ;`,
{ responseCode: 421, ignore_hook: true, notConfigured: true }
{ responseCode: 550, ignore_hook: true, notConfigured: true }
);

// e.g. user@example.com => [email protected]
Expand Down Expand Up @@ -557,7 +557,7 @@ async function getForwardingAddresses(
if (forwardingAddresses.length === 0 && !hasIMAP) {
throw new SMTPError(
`${address} is not yet configured with its email service provider ${config.urls.web} ;`,
{ responseCode: 421, ignore_hook: true, notConfigured: true }
{ responseCode: 550, ignore_hook: true, notConfigured: true }
);
}

Expand Down
101 changes: 50 additions & 51 deletions helpers/get-recipients.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,57 +46,6 @@ async function getRecipients(session, scan) {
let customAllowlist = [];
let customDenylist = [];
let webhookKey;

// get all forwarding addresses for individual address
const {
aliasIds,
addresses,
hasIMAP,
aliasPublicKey,
vacationResponder,
ignored,
softRejected,
hardRejected
} = await getForwardingAddresses.call(
this,
to.address,
[],
session.originalFromAddressRootDomain === env.WEB_HOST,
session
);

if (ignored)
return {
address: to.address,
addresses: [],
ignored: true,
hasIMAP: false,
aliasPublicKey: false,
vacationResponder: false
};

if (softRejected)
return {
address: to.address,
addresses: [],
ignored: false,
hasIMAP: false,
aliasPublicKey: false,
vacationResponder: false,
softRejected: true
};

if (hardRejected)
return {
address: to.address,
addresses: [],
ignored: false,
hasIMAP: false,
aliasPublicKey: false,
vacationResponder: false,
hardRejected: true
};

// lookup the port (e.g. if `forward-email-port=` or custom set on the domain)
const domain = parseHostFromDomainOrAddress(to.address);

Expand Down Expand Up @@ -321,6 +270,56 @@ async function getRecipients(session, scan) {
);
}

// get all forwarding addresses for individual address
const {
aliasIds,
addresses,
hasIMAP,
aliasPublicKey,
vacationResponder,
ignored,
softRejected,
hardRejected
} = await getForwardingAddresses.call(
this,
to.address,
[],
session.originalFromAddressRootDomain === env.WEB_HOST,
session
);

if (ignored)
return {
address: to.address,
addresses: [],
ignored: true,
hasIMAP: false,
aliasPublicKey: false,
vacationResponder: false
};

if (softRejected)
return {
address: to.address,
addresses: [],
ignored: false,
hasIMAP: false,
aliasPublicKey: false,
vacationResponder: false,
softRejected: true
};

if (hardRejected)
return {
address: to.address,
addresses: [],
ignored: false,
hasIMAP: false,
aliasPublicKey: false,
vacationResponder: false,
hardRejected: true
};

return {
address: to.address,
addresses,
Expand Down
4 changes: 3 additions & 1 deletion helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ const getForwardingConfiguration = require('./get-forwarding-configuration');
const getMaxForwardedAddresses = require('./get-max-forwarded-addresses');
const getSettings = require('./get-settings');
const isAutoReplyOrMailingList = require('./is-auto-reply-or-mailing-list');
const signMessage = require('./sign-message');

const REGEX_LOCALHOST = require('./regex-localhost');

Expand Down Expand Up @@ -273,5 +274,6 @@ module.exports = {
getForwardingConfiguration,
getMaxForwardedAddresses,
getSettings,
isAutoReplyOrMailingList
isAutoReplyOrMailingList,
signMessage
};
27 changes: 7 additions & 20 deletions helpers/is-arbitrary.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ const config = require('#config');
const env = require('#config/env');
const getHeaders = require('#helpers/get-headers');
const isAutoReplyOrMailingList = require('#helpers/is-auto-reply-or-mailing-list');
const logger = require('#helpers/logger');
const parseHostFromDomainOrAddress = require('#helpers/parse-host-from-domain-or-address');
const parseRootDomain = require('#helpers/parse-root-domain');

Expand Down Expand Up @@ -96,13 +95,12 @@ function isArbitrary(session, headers) {
);

// until adobe responds
if (
subject &&
subject.includes(
'Signature requested on "the agreement for your new checking account with us"'
)
)
throw new SMTPError('Spam from Adobe');
// if (
// subject &&
// subject.includes('Signature requested on') &&
// session.originalFromAddress === '[email protected]'
// )
// throw new SMTPError('Due to spam from Adobe this message is blocked');

//
// check for paypal scam (very strict until PayPal resolves phishing on their side)
Expand Down Expand Up @@ -270,19 +268,8 @@ function isArbitrary(session, headers) {
) &&
!(subject && REGEX_SYSADMIN_SUBJECT.test(subject))
) {
// TODO: until we're certain this is properly working we're going to monitor it with code bug to admins
const err = new TypeError(
`Spoofing detected and was soft blocked from ${
session.resolvedRootClientHostname || session.remoteAddress
}`
);
err.isCodeBug = true;
err.session = session;
logger.fatal(err);

throw new SMTPError(
'Message likely to be spoofing attack and was rejected due to lack of SPF alignment with From header',
{ responseCode: 421 }
'Message likely to be spoofing attack and was rejected due to lack of SPF alignment with From header'
);
}
}
Expand Down
2 changes: 0 additions & 2 deletions helpers/is-authenticated-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ async function isAuthenticatedMessage(headers, body, session, resolver) {

session.arc = results.arc;
session.dmarc = results.dmarc;
// NOTE: `arcSealedHeaders` does not include our DKIM signature
session.arcSealedHeaders = results.headers;
session.bimi = results.bimi;
session.receivedChain = results.receivedChain;

Expand Down
Loading

0 comments on commit 592f30e

Please sign in to comment.