fix(260721-n49): resolve per-company Mimecast tenant in gatherCampaignEvidence
- Thread company_id through reports query and CampaignReportSummary
- Mirror the route's Bug 2 (D-05) tenant-resolution block: query
mimecast_tenants for an enabled row, build a tenant-scoped client via
getMimecastClientForTenant, and pass { client, cacheScope } to
getBlastRadius() when one exists
- Preserve global env fallback unchanged when no companyId or no
enabled tenant row is present
This commit is contained in:
parent
f58856e103
commit
9f75f2160c
1 changed files with 52 additions and 9 deletions
|
|
@ -17,6 +17,7 @@
|
|||
import type { AuthResults } from './eml-parser';
|
||||
import { postgresClient } from './postgres-client';
|
||||
import { getBlastRadius, type BlastRadiusResult } from './mimecast-blast-radius';
|
||||
import { getMimecastClientForTenant } from './mimecast-client';
|
||||
|
||||
// =============================================================================
|
||||
// D-06/D-07: KnowBe4 / Breach Secure Now simulation sender-domain allowlist
|
||||
|
|
@ -213,6 +214,14 @@ interface ReportDbRow {
|
|||
title: string | null;
|
||||
created_at: string;
|
||||
requester_email: string | null;
|
||||
company_id: string | null;
|
||||
}
|
||||
|
||||
/** Mirrors app/api/phishing/campaigns/[id]/route.ts's MimecastTenantRow shape verbatim. */
|
||||
interface MimecastTenantRow {
|
||||
client_id: string;
|
||||
client_secret: string;
|
||||
base_url: string | null;
|
||||
}
|
||||
|
||||
interface MessageDbRow {
|
||||
|
|
@ -256,6 +265,7 @@ export interface CampaignReportSummary {
|
|||
title: string | null;
|
||||
createdAt: string;
|
||||
requesterEmail: string | null;
|
||||
companyId: string | null;
|
||||
}
|
||||
|
||||
export interface CampaignEvidence {
|
||||
|
|
@ -283,7 +293,7 @@ export async function gatherCampaignEvidence(campaignId: string): Promise<Campai
|
|||
// app/api/phishing/campaigns/[id]/route.ts's existing bulk-fetch shape).
|
||||
const reportsRes = await postgresClient.query<ReportDbRow>(
|
||||
`SELECT r.id::text AS id, r.title, r.created_at::text AS created_at,
|
||||
c.email_address AS requester_email
|
||||
c.email_address AS requester_email, r.company_id::text AS company_id
|
||||
FROM reports r
|
||||
LEFT JOIN contacts c ON c.id = r.requester_contact_id
|
||||
WHERE r.campaign_id = $1
|
||||
|
|
@ -295,6 +305,7 @@ export async function gatherCampaignEvidence(campaignId: string): Promise<Campai
|
|||
title: r.title,
|
||||
createdAt: r.created_at,
|
||||
requesterEmail: r.requester_email,
|
||||
companyId: r.company_id,
|
||||
}));
|
||||
const reportIds = reports.map((r) => r.id);
|
||||
|
||||
|
|
@ -347,15 +358,47 @@ export async function gatherCampaignEvidence(campaignId: string): Promise<Campai
|
|||
(i) => i.messageId === primaryMessage?.id && i.indicatorType === 'sender'
|
||||
);
|
||||
const createdAt = new Date(primaryReport.createdAt);
|
||||
blastRadius = await getBlastRadius({
|
||||
sender: senderIndicator?.value ?? primaryMessage?.from.email ?? '',
|
||||
recipient: primaryReport.requesterEmail ?? '',
|
||||
subject: primaryMessage?.subject ?? primaryReport.title ?? '',
|
||||
dateWindow: {
|
||||
start: new Date(createdAt.getTime() - 24 * 60 * 60 * 1000),
|
||||
end: new Date(createdAt.getTime() + 24 * 60 * 60 * 1000),
|
||||
|
||||
// Bug 2 (D-05): resolve the reporting company's own registered Mimecast
|
||||
// tenant, if one exists, and query it directly instead of the global
|
||||
// env-configured (Wulf) tenant. Falls back to the global client when the
|
||||
// company has no enabled mimecast_tenants row. Mirrors
|
||||
// app/api/phishing/campaigns/[id]/route.ts's identical tenant-resolution
|
||||
// block verbatim — same query, same client build, same options shape.
|
||||
let tenantOptions: { client: ReturnType<typeof getMimecastClientForTenant>; cacheScope: string } | undefined;
|
||||
if (primaryReport.companyId) {
|
||||
const tenantRes = await postgresClient.query<MimecastTenantRow>(
|
||||
`SELECT client_id, client_secret, base_url
|
||||
FROM mimecast_tenants
|
||||
WHERE company_id = $1 AND enabled = true
|
||||
ORDER BY id LIMIT 1`,
|
||||
[primaryReport.companyId]
|
||||
);
|
||||
const tenantRow = tenantRes.rows[0];
|
||||
if (tenantRow) {
|
||||
tenantOptions = {
|
||||
client: getMimecastClientForTenant({
|
||||
client_id: tenantRow.client_id,
|
||||
client_secret: tenantRow.client_secret,
|
||||
base_url: tenantRow.base_url ?? undefined,
|
||||
}),
|
||||
cacheScope: primaryReport.companyId,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
blastRadius = await getBlastRadius(
|
||||
{
|
||||
sender: senderIndicator?.value ?? primaryMessage?.from.email ?? '',
|
||||
recipient: primaryReport.requesterEmail ?? '',
|
||||
subject: primaryMessage?.subject ?? primaryReport.title ?? '',
|
||||
dateWindow: {
|
||||
start: new Date(createdAt.getTime() - 24 * 60 * 60 * 1000),
|
||||
end: new Date(createdAt.getTime() + 24 * 60 * 60 * 1000),
|
||||
},
|
||||
},
|
||||
});
|
||||
tenantOptions
|
||||
);
|
||||
} else {
|
||||
// No report ever linked to this campaign — nothing to look up (research A6).
|
||||
blastRadius = { status: 'unavailable', reason: 'not_configured' };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue