wulf-pulse/scripts/verify-mansfield-mimecast.ts

110 lines
4 KiB
TypeScript
Raw Permalink Normal View History

/**
* 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);
});