/** * verify-mansfield-mimecast.ts * * Ticket 699451 (Richard Mansfield, Seubert) was manually forwarded by a * technician, not reported via KnowBe4's Phish Alert button or Microsoft's * "Report Message" add-in — so the detector never created a `reports` row, * and everything we know about the sender ("eserver@it-support.care") comes * from a human's free-text ticket description, not a parsed EML/header. * * it-support.care IS on the KNOWN_SIMULATION_SENDERS allowlist * (lib/services/campaign-classifier.ts) for KnowBe4. Before trusting the * ticket text at face value, independently verify against Mimecast's actual * message-trace data for Seubert's tenant: does a message from that sender * to Rich, with that subject, in that time window, actually exist and what * does Mimecast say about it (delivered/held/rejected, real headers)? * * Usage: * POSTGRES_HOST=localhost npx tsx scripts/verify-mansfield-mimecast.ts */ import { config } from 'dotenv'; import { resolve } from 'path'; config({ path: resolve('/opt/stacks/pulse/.env.local') }); import postgresClient from '../lib/services/postgres-client'; import { getMimecastClientForTenant } from '../lib/services/mimecast-client'; const SEUBERT_COMPANY_ID = 29683407; const REPORTED_SENDER = 'eserver@it-support.care'; const RECIPIENT = 'rmansfield@seubert.com'; const SUBJECT = 'Internal Email Problems'; async function main() { const tenantRow = await postgresClient.query<{ client_id: string; client_secret: string; base_url: string | null; account_name: string | null; }>( `SELECT client_id, client_secret, base_url, account_name FROM mimecast_tenants WHERE company_id = $1 AND enabled = true`, [SEUBERT_COMPANY_ID] ); const tenant = tenantRow.rows[0]; if (!tenant) { console.log('No enabled Mimecast tenant for Seubert — cannot verify.'); process.exit(1); } console.log(`Using Mimecast tenant: ${tenant.account_name}`); const client = getMimecastClientForTenant({ client_id: tenant.client_id, client_secret: tenant.client_secret, base_url: tenant.base_url ?? undefined, }); const now = new Date(); const start = new Date(now.getTime() - 5 * 24 * 60 * 60 * 1000); // 5 days back const startStr = start.toISOString().replace(/\.\d{3}Z$/, '+0000'); const endStr = now.toISOString().replace(/\.\d{3}Z$/, '+0000'); console.log(`\n=== 1. Exact sender+recipient+subject match ===`); console.log(`from=${REPORTED_SENDER} to=${RECIPIENT} subject="${SUBJECT}" window=${startStr}..${endStr}`); const exact = await client.searchDeliveredMessages({ from: REPORTED_SENDER, to: RECIPIENT, subject: SUBJECT, start: startStr, end: endStr, }); console.log(JSON.stringify(exact, null, 2)); console.log(`\n=== 2. Sender domain only (it-support.care), any recipient at seubert.com ===`); const domainOnly = await client.searchDeliveredMessages({ from: 'it-support.care', to: 'seubert.com', start: startStr, end: endStr, }); console.log(JSON.stringify(domainOnly, null, 2)); console.log(`\n=== 3. All mail TO Rich in the window (no sender filter) ===`); const toRich = await client.searchDeliveredMessages({ to: RECIPIENT, subject: SUBJECT, start: startStr, end: endStr, }); console.log(JSON.stringify(toRich, null, 2)); console.log(`\n=== 4. Held messages for Rich ===`); const held = await client.getHeldMessages({ recipient: RECIPIENT }); console.log(JSON.stringify(held, null, 2)); console.log(`\n=== 5. Recent threat events on this tenant (last 500) ===`); const threats = await client.getThreatEvents({ pageSize: 500 }); const relevant = threats.items.filter( (t) => (t.actorEmail ?? '').toLowerCase().includes('it-support.care') ); console.log(`Total threat events fetched: ${threats.items.length}`); console.log(`Events involving it-support.care: ${relevant.length}`); console.log(JSON.stringify(relevant, null, 2)); process.exit(0); } main().catch((err) => { console.error(err); process.exit(1); });