← back to George Gmail
lib/send-preflight.js
152 lines
'use strict';
const parse5 = require('parse5');
const DEFAULT_MAILING_ADDRESS = '15442 Ventura Blvd. #102, Sherman Oaks, CA 91403';
const MISLEADING_SUBJECT = /\b(?:re|fw|fwd)\s*:/i;
const NON_RENDERED_ELEMENTS = new Set(['script', 'style', 'template', 'noscript', 'head', 'svg', 'canvas']);
const MAILBOX = /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/i;
function parseMailbox(value, { allowDisplayName = false, allowBlank = false } = {}) {
const raw = String(value ?? '');
const trimmed = raw.trim();
if (!trimmed) return { supplied: false, valid: allowBlank, email: '' };
if (/[\r\n\0]/.test(raw) || /[,;]/.test(raw)) return { supplied: true, valid: false, email: '' };
let address = trimmed;
if (trimmed.includes('<') || trimmed.includes('>')) {
if (!allowDisplayName) return { supplied: true, valid: false, email: '' };
const match = trimmed.match(/^([^<>]+?)\s*<([^<>]+)>$/);
if (!match || !match[1].trim()) return { supplied: true, valid: false, email: '' };
address = match[2].trim();
}
if (!MAILBOX.test(address)) return { supplied: true, valid: false, email: '' };
return { supplied: true, valid: true, email: address.toLowerCase() };
}
function emailOf(value) {
const parsed = parseMailbox(value, { allowDisplayName: true, allowBlank: true });
return parsed.valid ? parsed.email : '';
}
function attrs(node) {
return Object.fromEntries((node.attrs || []).map(({ name, value }) => [name.toLowerCase(), value]));
}
function isHiddenElement(node) {
if (!node.tagName) return false;
const tag = node.tagName.toLowerCase();
if (NON_RENDERED_ELEMENTS.has(tag)) return true;
const attributes = attrs(node);
if (Object.hasOwn(attributes, 'hidden')) return true;
if (String(attributes['aria-hidden'] || '').trim().toLowerCase() === 'true') return true;
if (tag === 'input' && String(attributes.type || '').trim().toLowerCase() === 'hidden') return true;
const style = String(attributes.style || '')
.replace(/\/\*[\s\S]*?\*\//g, '')
.replace(/\s+/g, '')
.toLowerCase();
return /(?:^|;)display:none(?:!important)?(?:;|$)/.test(style)
|| /(?:^|;)visibility:hidden(?:!important)?(?:;|$)/.test(style);
}
function parsedVisibleContent(body) {
const fragment = parse5.parseFragment(String(body || ''));
const text = [];
const anchors = [];
function visit(node, hidden = false, activeAnchor = null) {
const nowHidden = hidden || isHiddenElement(node);
if (!nowHidden && node.nodeName === '#text') {
const value = node.value || '';
text.push(value);
if (activeAnchor) activeAnchor.text.push(value);
}
let childAnchor = activeAnchor;
if (!nowHidden && String(node.tagName || '').toLowerCase() === 'a') {
const href = attrs(node).href;
if (href) {
childAnchor = { href: href.trim(), text: [] };
anchors.push(childAnchor);
}
}
for (const child of node.childNodes || []) visit(child, nowHidden, childAnchor);
if (node.content) visit(node.content, true, null);
}
visit(fragment);
return {
visibleText: text.join(' ').replace(/\s+/g, ' ').trim(),
anchors: anchors.map(({ href, text: label }) => ({ href, visibleLabel: label.join(' ').replace(/\s+/g, ' ').trim() })),
};
}
function hasMailingAddress(body, mailingAddress = DEFAULT_MAILING_ADDRESS) {
const wanted = String(mailingAddress || '').replace(/[^a-z0-9]/gi, '').toLowerCase();
const actual = parsedVisibleContent(body).visibleText.replace(/[^a-z0-9]/gi, '').toLowerCase();
return Boolean(wanted) && actual.includes(wanted);
}
function isCanonicalOptOutMailto(url, expectedFrom) {
if (url.protocol !== 'mailto:' || url.hash) return false;
let recipient;
try { recipient = decodeURIComponent(url.pathname); } catch { return false; }
if (recipient.toLowerCase() !== emailOf(expectedFrom)) return false;
const entries = [...url.searchParams.entries()];
if (entries.length !== 1 || entries[0][0] !== 'subject') return false;
const subject = entries[0][1];
return Boolean(subject) && !/[\r\n\0]/.test(subject) && /(?:unsubscribe|opt\s*out|stop\s+receiving)/i.test(subject);
}
function hasWorkingOptOutLink(body, expectedFrom) {
const { anchors } = parsedVisibleContent(body);
return anchors.some(({ href, visibleLabel }) => {
try {
if (!/(?:unsubscribe|opt\s*out|stop\s+receiving)/i.test(visibleLabel)) return false;
const url = new URL(href);
return isCanonicalOptOutMailto(url, expectedFrom);
} catch {
return false;
}
});
}
function classifySend(payload, { externalRecipients = [], endpointEvaluation = false } = {}) {
if (!externalRecipients.length) return { messageClass: 'internal', basis: 'server-derived-internal', enforcement: 'exempt' };
const explicit = String(payload.message_class || '').trim().toLowerCase();
if (explicit === 'commercial' || payload.compliance_required === true) {
return { messageClass: 'commercial', basis: explicit === 'commercial' ? 'caller-explicit-commercial' : 'caller-compliance-required', enforcement: 'block' };
}
if (explicit === 'transactional' || explicit === 'reply') {
return { messageClass: explicit, basis: `caller-explicit-${explicit}-unverified`, enforcement: 'report-only' };
}
if (endpointEvaluation) return { messageClass: 'commercial', basis: 'preflight-endpoint-conservative-default', enforcement: 'block' };
return { messageClass: 'unclassified', basis: explicit ? 'caller-unknown-class' : 'legacy-external-unclassified', enforcement: 'report-only' };
}
function sendPreflight(payload, options = {}) {
const { expectedFrom = '', externalRecipients = [], endpointEvaluation = false, mailingAddress = DEFAULT_MAILING_ADDRESS } = options;
const classification = classifySend(payload, { externalRecipients, endpointEvaluation });
if (classification.messageClass === 'internal') return { ok: true, shouldBlock: false, ...classification, checks: [], failed: [], external: [] };
const sender = emailOf(expectedFrom);
const requestedFrom = parseMailbox(payload.from, { allowDisplayName: true, allowBlank: true });
const compliance = payload.compliance || {};
const coreChecks = [
{ id: 'accurate_from', ok: Boolean(sender) && (!requestedFrom.supplied || (requestedFrom.valid && requestedFrom.email === sender)), detail: `blank From may inherit; any supplied From must be exactly one valid mailbox resolving to ${sender || '(configured account)'}` },
{ id: 'honest_subject', ok: Boolean(String(payload.subject || '').trim()) && !MISLEADING_SUBJECT.test(String(payload.subject || '')), detail: 'subject is required and must not imply a reply or forward without server-verified context' },
];
const commercialChecks = [
{ id: 'physical_address', ok: hasMailingAddress(payload.body, mailingAddress), detail: 'body must contain the configured physical postal address' },
{ id: 'working_opt_out', ok: hasWorkingOptOutLink(payload.body, sender), detail: 'body must contain a visible, valid opt-out anchor' },
{ id: 'dnc_scrubbed', ok: compliance.dnc_scrubbed === true, detail: 'compliance.dnc_scrubbed must be true' },
{ id: 'age_gate_excluded', ok: compliance.age_gated_excluded === true, detail: 'compliance.age_gated_excluded must be true' },
];
const checks = classification.messageClass === 'transactional' || classification.messageClass === 'reply'
? coreChecks
: [...coreChecks, ...commercialChecks];
const failed = checks.filter((check) => !check.ok);
const ok = failed.length === 0;
return { ok, shouldBlock: classification.enforcement === 'block' && !ok, ...classification, checks, failed, external: externalRecipients };
}
module.exports = { classifySend, sendPreflight, parseMailbox, parsedVisibleContent, hasMailingAddress, hasWorkingOptOutLink, isCanonicalOptOutMailto, DEFAULT_MAILING_ADDRESS };