import PostalMime from "postal-mime";
import { abuseForwardAddress, guessAbuseCategory, recordAbuseReport, siteIdForUrl } from "./abuse";
import type { ReporterKind } from "./abuse";
const MAX_RAW_BYTES = 2 * 1024 * 1024;
const MAX_TARGETS = 10;
const URL_PATTERN = /\bhttps?:\/\/[^\s<>"'()]+/gi;
const TRAILING_PUNCTUATION = /[.,;:!?\]]+$/;
/** URLs mentioned in the mail, hosted sites first so a long signature can't push the reported page out. */
export async function extractReportTargets(text: string): Promise<{ hosted: string[]; other: string[] }> {
const seen = new Set<string>();
const hosted: string[] = [];
const other: string[] = [];
for (const match of text.matchAll(URL_PATTERN)) {
const url = match[0].replace(TRAILING_PUNCTUATION, "");
if (seen.has(url) || seen.size >= MAX_TARGETS * 4) continue;
seen.add(url);
if (hosted.length < MAX_TARGETS && (await siteIdForUrl(url))) hosted.push(url);
else if (other.length < MAX_TARGETS) other.push(url);
}
return { hosted, other };
}
/**
* Whether the receiving MTA (Cloudflare Email Routing, which prepends its own Authentication-Results) saw a DMARC
* pass aligned with the From domain. Only the topmost header counts: anything below it came from the sender and
* can be forged.
*/
export function dmarcPassed(headers: { key: string; value: string }[], fromDomain: string): boolean {
const results = headers.find((header) => header.key.toLowerCase() === "authentication-results")?.value;
if (!results || !fromDomain) return false;
const dmarc = results
.split(";")
.map((part) => part.trim().toLowerCase())
.find((part) => part.startsWith("dmarc="));
if (!dmarc || !/^dmarc=pass\b/.test(dmarc)) return false;
const from = /header\.from=([^\s;]+)/.exec(dmarc)?.[1];
return from === fromDomain.toLowerCase();
}