fix(260721-mmf): broaden Mimecast blast-radius fan-out to whole tenant
- searchDeliveredMessages now called with from+subject+start+end only (no `to`) so it returns every delivered/rejected message matching the campaign across all recipients, not just the reporter's mailbox - getHeldMessages now called with start+end only (no `recipient`) — domainsMatch() post-filter is the sole scoping mechanism for held rows - Updated inline comments to document the tenant-wide fan-out and the per-recipient merge behavior it now produces
This commit is contained in:
parent
f50b8a3a9b
commit
58202e0dee
1 changed files with 20 additions and 8 deletions
|
|
@ -139,15 +139,23 @@ export async function getBlastRadius(
|
||||||
|
|
||||||
// D-01 (corrected): the fan-out runs unconditionally — it is the only
|
// D-01 (corrected): the fan-out runs unconditionally — it is the only
|
||||||
// source of the matched/delivered/held/rejected/clicked counts.
|
// source of the matched/delivered/held/rejected/clicked counts.
|
||||||
|
//
|
||||||
|
// Tenant-wide fan-out (T-17-01): deliberately NOT scoped by a single
|
||||||
|
// recipient. searchDeliveredMessages is queried by sender+subject+
|
||||||
|
// date-window only, so it returns every delivered/rejected message
|
||||||
|
// matching this campaign across the whole tenant — the true blast
|
||||||
|
// radius, not just the reporter's mailbox. getHeldMessages is queried
|
||||||
|
// by date-window only (it has no server-side sender filter) and relies
|
||||||
|
// entirely on the domainsMatch() post-filter below to stay scoped to
|
||||||
|
// this campaign's sender.
|
||||||
const [deliveredResult, heldResult, threatsResult] = await Promise.all([
|
const [deliveredResult, heldResult, threatsResult] = await Promise.all([
|
||||||
client.searchDeliveredMessages({
|
client.searchDeliveredMessages({
|
||||||
to: input.recipient,
|
|
||||||
from: input.sender,
|
from: input.sender,
|
||||||
subject: input.subject,
|
subject: input.subject,
|
||||||
start: startStr,
|
start: startStr,
|
||||||
end: endStr,
|
end: endStr,
|
||||||
}),
|
}),
|
||||||
client.getHeldMessages({ recipient: input.recipient, start: startStr, end: endStr }),
|
client.getHeldMessages({ start: startStr, end: endStr }),
|
||||||
client.getThreatEvents(),
|
client.getThreatEvents(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
@ -192,12 +200,16 @@ export async function getBlastRadius(
|
||||||
|
|
||||||
const clicked = (threatsResult.items ?? []).filter((t) => isClickEvent(t.analysis)).length;
|
const clicked = (threatsResult.items ?? []).filter((t) => isClickEvent(t.analysis)).length;
|
||||||
|
|
||||||
// Per-recipient merge: group by the single-string `to` field. Delivered
|
// Per-recipient merge: group by the single-string `to` field. Since the
|
||||||
// (non-rejected) rows → 'delivered', rejected rows → 'rejected', held
|
// fan-out above is tenant-wide (not scoped to one recipient), this Map
|
||||||
// rows → 'held' (held overwrites a same-recipient delivered entry — a
|
// naturally accumulates one entry per distinct `to` value seen across all
|
||||||
// held message for a recipient is the more actionable signal). Ensure
|
// delivered/held/rejected rows — the true multi-recipient blast radius,
|
||||||
// the originally-queried recipient is always represented, even if
|
// not just the reporter. Delivered (non-rejected) rows → 'delivered',
|
||||||
// neither result set returned a row for them ('unknown').
|
// rejected rows → 'rejected', held rows → 'held' (held overwrites a
|
||||||
|
// same-recipient delivered entry — a held message for a recipient is the
|
||||||
|
// more actionable signal). Ensure the originally-reported recipient is
|
||||||
|
// always represented, even if neither result set returned a row for them
|
||||||
|
// ('unknown').
|
||||||
const perRecipientMap = new Map<string, 'delivered' | 'held' | 'rejected' | 'unknown'>();
|
const perRecipientMap = new Map<string, 'delivered' | 'held' | 'rejected' | 'unknown'>();
|
||||||
for (const row of nonRejectedDeliveredRows) {
|
for (const row of nonRejectedDeliveredRows) {
|
||||||
if (row.to) perRecipientMap.set(row.to, 'delivered');
|
if (row.to) perRecipientMap.set(row.to, 'delivered');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue