6 KiB
6 KiB
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | requirements-completed | duration | completed | |||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-260721-n49 | 01 | phishing-triage |
|
|
|
|
|
|
|
|
12min | 2026-07-21 |
Quick Task 260721-n49: Fix classifier Mimecast tenant scope Summary
Auto-classification (classifyCampaign/gatherCampaignEvidence) now queries the reporting company's own registered Mimecast tenant instead of always falling back to the global env-configured (Wulf) tenant.
Performance
- Duration: 12 min
- Tasks: 2 completed
- Files modified: 2
Accomplishments
gatherCampaignEvidence()now threadscompany_idfrom thereportstable throughCampaignReportSummary.companyId.- Before calling
getBlastRadius(), the classifier queriesmimecast_tenantsfor an enabled row matching the primary report'scompanyIdand, if found, builds a tenant-scoped client viagetMimecastClientForTenant()and passes{ client, cacheScope: companyId }as the second argument — exactly mirroring the already-shipped, already-tested block inapp/api/phishing/campaigns/[id]/route.ts("Bug 2 (D-05)"). - When no
companyIdis present, or no enabled tenant row exists for it,getBlastRadius()is still called with no second argument — the global env-configured Mimecast client fallback is unchanged from before this fix. - Root cause was verified live against ticket 700716 / company 29683407 ("Seubert and Associates"): before this fix, that company's auto-classification was being computed against the wrong (Wulf) tenant's Mimecast data, producing a silent false-"clean" signal.
Task Commits
Each task was committed atomically:
- Task 1: Resolve per-company Mimecast tenant in gatherCampaignEvidence() -
9f75f21(fix) - Task 2: Extend campaign-classifier tests for tenant resolution vs. global fallback -
9f12cd6(test)
Note: git log --oneline -3 from HEAD: 9f12cd6 (test), 9f75f21 (fix), f58856e (docs: pre-dispatch plan).
Files Created/Modified
lib/services/campaign-classifier.ts- AddedMimecastTenantRowinterface,company_idonReportDbRow/CampaignReportSummary, and the per-company tenant-resolution block (query +getMimecastClientForTenant+{ client, cacheScope }) before the existinggetBlastRadius()call ingatherCampaignEvidence().lib/services/campaign-classifier.test.ts- Added amimecast_tenantsrouting branch tostageQueries/StagedRows,company_idonReportFixtureRow(and all existing report fixtures, set tonullwhere not under test), plus two new test cases: one asserting tenant-scoped{ client, cacheScope }when an enabled tenant row exists, and a parameterized test asserting no second argument (undefined) whencompanyIdis null or has no enabled tenant row.
Decisions Made
- Kept the tenant-resolution block inline in the classifier (not extracted into a shared helper) per the plan's explicit judgment note — the route's existing block is already correct and tested, and extracting a shared helper would have required editing it, which the plan disallowed unless the extraction was a clean behavior-preserving drop-in for both call sites.
Deviations from Plan
None - plan executed exactly as written.
Issues Encountered
None.
User Setup Required
None - no external service configuration required.
Verification
npx vitest run lib/services/campaign-classifier.test.ts— 49/49 tests passed (including the 2 new tenant-resolution/global-fallback tests; all 47 pre-existing tests remained green).npx tsc --noEmit --pretty— clean, no errors.- Manual re-read confirmed the classifier's new tenant-resolution block is a line-for-line behavior mirror of
app/api/phishing/campaigns/[id]/route.ts's "Bug 2 (D-05)" block (same query, samegetMimecastClientForTenantbuild, same{ client, cacheScope }shape); the route file itself was not modified.
Next Phase Readiness
- Fix is self-contained to the evidence-gathering path of the campaign classifier; no follow-up work required.
- Companies with their own
mimecast_tenantsrow (e.g. Seubert and Associates / 29683407) will now get correct tenant-scoped blast-radius evidence on the next auto-classification run (webhook-triggered ticket creation, or any future manualclassifyCampaign()invocation) — no backfill of historicalclassificationsrows was in scope for this fix.
Phase: quick-260721-n49 Completed: 2026-07-21