openvibers/openvibe.tools Commits
Tools reputation: a null MX (RFC 7505) reads as 'accepts no mail', not a nameless server (#65)
Changes
@@@ -600,7 +600,9 @@ function createChecks({ egress, resolverFor = defaultResolver, tlsUpgrade = tls.if (domain) {// Mail: MX, SPF, DMARC-- if (mx.length) add('Mail servers (MX)', 'good', `${mx.length} mail ${mx.length === 1 ? 'server' : 'servers'}: ${mx.sort((a, b) => a.priority - b.priority).slice(0, 3).map(m => m.exchange).join(', ')}.`);++ const real = mx.filter(m => m.exchange && m.exchange !== '.');++ if (real.length) add('Mail servers (MX)', 'good', `${real.length} mail ${real.length === 1 ? 'server' : 'servers'}: ${real.sort((a, b) => a.priority - b.priority).slice(0, 3).map(m => m.exchange).join(', ')}.`);++ else if (mx.length) add('Mail servers (MX)', 'good', 'A null MX (RFC 7505): the domain says it accepts no mail, so nobody waits on a mail server that is not there.');else add('Mail servers (MX)', 'info', 'No MX record: this domain does not receive mail. If it never sends mail either, SPF "v=spf1 -all" and DMARC p=reject stop others sending as it.');if (!spf.length) add('SPF', 'warn', 'No SPF record: anyone can send mail claiming to be from this domain, and receivers trust its mail less.', 15);else if (spf.length > 1) add('SPF', 'bad', `${spf.length} SPF records: receivers treat more than one as a permanent error, as if there were none.`, 15);@@@ -15,7 +15,7 @@ const TXT = {'_dmarc.good.example': [['v=DMARC1; p=reject; rua=mailto:[email protected]']],'bad.example': [['v=spf1 +all']],};--const MX = { 'good.example': [{ exchange: 'mail.good.example', priority: 10 }] };++const MX = { 'good.example': [{ exchange: 'mail.good.example', priority: 10 }], 'nomail.example': [{ exchange: '', priority: 0 }] };const fail = (code) => Promise.reject(Object.assign(new Error(code), { code }));const resolverFor = () => ({resolve4: (q) => (LISTED[q] ? Promise.resolve(LISTED[q]) : A[q] ? Promise.resolve(A[q]) : fail('ENOTFOUND')),@@@ -71,6 +71,12 @@ const reverse = async (ip) => ({ '93.184.216.31': ['host.isp.example'], '93.184.assert.strictEqual(r.grade, 'poor');assert.strictEqual(r.score, Math.max(0, 100 - r.findings.reduce((s, x) => s + x.cost, 0)));++ // A null MX (RFC 7505: "0 ." comes back as an empty exchange) says the domain takes no mail.++ A['nomail.example'] = ['93.184.216.40'];++ f = byCheck(await checks.reputation('nomail.example', { rdap, reverse }));++ assert.strictEqual(f['Mail servers (MX)'].status, 'good');++ assert.match(f['Mail servers (MX)'].detail, /null MX/);++// An address: blacklists and reverse DNS.r = await checks.reputation('93.184.216.31', { rdap, reverse });f = byCheck(r);
@@ -600,7 +600,9 @@ function createChecks({ egress, resolverFor = defaultResolver, tlsUpgrade = tls.
@@ -600,7 +600,9 @@ function createChecks({ egress, resolverFor = defaultResolver, tlsUpgrade = tls.
if (domain) {
if (domain) {
// Mail: MX, SPF, DMARC
// Mail: MX, SPF, DMARC
- if (mx.length) add('Mail servers (MX)', 'good', `${mx.length} mail ${mx.length === 1 ? 'server' : 'servers'}: ${mx.sort((a, b) => a.priority - b.priority).slice(0, 3).map(m => m.exchange).join(', ')}.`);
+ const real = mx.filter(m => m.exchange && m.exchange !== '.');
+ if (real.length) add('Mail servers (MX)', 'good', `${real.length} mail ${real.length === 1 ? 'server' : 'servers'}: ${real.sort((a, b) => a.priority - b.priority).slice(0, 3).map(m => m.exchange).join(', ')}.`);
+ else if (mx.length) add('Mail servers (MX)', 'good', 'A null MX (RFC 7505): the domain says it accepts no mail, so nobody waits on a mail server that is not there.');
else add('Mail servers (MX)', 'info', 'No MX record: this domain does not receive mail. If it never sends mail either, SPF "v=spf1 -all" and DMARC p=reject stop others sending as it.');
else add('Mail servers (MX)', 'info', 'No MX record: this domain does not receive mail. If it never sends mail either, SPF "v=spf1 -all" and DMARC p=reject stop others sending as it.');
if (!spf.length) add('SPF', 'warn', 'No SPF record: anyone can send mail claiming to be from this domain, and receivers trust its mail less.', 15);
if (!spf.length) add('SPF', 'warn', 'No SPF record: anyone can send mail claiming to be from this domain, and receivers trust its mail less.', 15);
else if (spf.length > 1) add('SPF', 'bad', `${spf.length} SPF records: receivers treat more than one as a permanent error, as if there were none.`, 15);
else if (spf.length > 1) add('SPF', 'bad', `${spf.length} SPF records: receivers treat more than one as a permanent error, as if there were none.`, 15);
@@ -15,7 +15,7 @@ const TXT = {
@@ -15,7 +15,7 @@ const TXT = {
'_dmarc.good.example': [['v=DMARC1; p=reject; rua=mailto:[email protected]']],
'_dmarc.good.example': [['v=DMARC1; p=reject; rua=mailto:[email protected]']],
'bad.example': [['v=spf1 +all']],
'bad.example': [['v=spf1 +all']],
};
};
-const MX = { 'good.example': [{ exchange: 'mail.good.example', priority: 10 }] };
+const MX = { 'good.example': [{ exchange: 'mail.good.example', priority: 10 }], 'nomail.example': [{ exchange: '', priority: 0 }] };
const fail = (code) => Promise.reject(Object.assign(new Error(code), { code }));
const fail = (code) => Promise.reject(Object.assign(new Error(code), { code }));
const resolverFor = () => ({
const resolverFor = () => ({
resolve4: (q) => (LISTED[q] ? Promise.resolve(LISTED[q]) : A[q] ? Promise.resolve(A[q]) : fail('ENOTFOUND')),
resolve4: (q) => (LISTED[q] ? Promise.resolve(LISTED[q]) : A[q] ? Promise.resolve(A[q]) : fail('ENOTFOUND')),
@@ -71,6 +71,12 @@ const reverse = async (ip) => ({ '93.184.216.31': ['host.isp.example'], '93.184.
@@ -71,6 +71,12 @@ const reverse = async (ip) => ({ '93.184.216.31': ['host.isp.example'], '93.184.
assert.strictEqual(r.grade, 'poor');
assert.strictEqual(r.grade, 'poor');
assert.strictEqual(r.score, Math.max(0, 100 - r.findings.reduce((s, x) => s + x.cost, 0)));
assert.strictEqual(r.score, Math.max(0, 100 - r.findings.reduce((s, x) => s + x.cost, 0)));
+ // A null MX (RFC 7505: "0 ." comes back as an empty exchange) says the domain takes no mail.
+ A['nomail.example'] = ['93.184.216.40'];
+ f = byCheck(await checks.reputation('nomail.example', { rdap, reverse }));
+ assert.strictEqual(f['Mail servers (MX)'].status, 'good');
+ assert.match(f['Mail servers (MX)'].detail, /null MX/);
+
// An address: blacklists and reverse DNS.
// An address: blacklists and reverse DNS.
r = await checks.reputation('93.184.216.31', { rdap, reverse });
r = await checks.reputation('93.184.216.31', { rdap, reverse });
f = byCheck(r);
f = byCheck(r);