docs(quick-260721-n49): fix classifier to resolve per-company Mimecast tenant instead of the global tenant
This commit is contained in:
parent
1cd0d4db48
commit
e9478101a3
2 changed files with 104 additions and 1 deletions
|
|
@ -28,7 +28,7 @@ See: .planning/PROJECT.md (updated 2026-07-14)
|
|||
Phase: Milestone v3.0 complete
|
||||
Plan: —
|
||||
Status: Awaiting next milestone
|
||||
Last activity: 2026-07-21 — Completed quick task 260721-mmf: fix Mimecast blast-radius query scope (tenant-wide fan-out instead of single-recipient)
|
||||
Last activity: 2026-07-21 — Completed quick task 260721-n49: fix classifier to resolve per-company Mimecast tenant instead of always using the global tenant
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
|
|
@ -132,6 +132,7 @@ None yet.
|
|||
| 260718-9qg | Add self-contained `QBO_INTEGRATION_HANDOFF.md` documenting Pulse's QuickBooks Online OAuth2 flow, token storage/refresh, sandbox/production API base URLs, and gotchas (deletion-diffing, CSRF state gap, NEXTAUTH_URL legacy var) for a new app's team | 2026-07-18 | ea8a36b | [260718-9qg-create-a-quickbooks-online-integration-h](./quick/260718-9qg-create-a-quickbooks-online-integration-h/) |
|
||||
| 260721-fy8 | Fix missing `mimecast-sync`/`qbo` scheduler dispatch branches (both silently fell through to a generic Autotask full sync) and reschedule `mimecast-sync` off the 2am 3-way cron collision with `qbo-sync-2am` and `veeam-full` | 2026-07-21 | db7db98 | [260721-fy8-fix-mimecast-and-qbo-sync-scheduler-disp](./quick/260721-fy8-fix-mimecast-and-qbo-sync-scheduler-disp/) |
|
||||
| 260721-mmf | Fix Mimecast blast-radius query scope — dropped the single-recipient `to`/`recipient` filter from `searchDeliveredMessages`/`getHeldMessages` so the fan-out returns every delivered/held message across the whole tenant for a campaign's sender+subject+date-window, not just whether it reached the original reporter's mailbox | 2026-07-21 | 534eda3 | [260721-mmf-fix-mimecast-blast-radius-scope](./quick/260721-mmf-fix-mimecast-blast-radius-scope/) |
|
||||
| 260721-n49 | Fix `gatherCampaignEvidence()` (used by auto-classification on ticket creation) to resolve the reporting company's own `mimecast_tenants` row before calling `getBlastRadius()`, mirroring the campaign-detail route's existing per-tenant resolution — previously it always used the global env-configured (Wulf) tenant, silently returning wrong-tenant (often empty) blast-radius data for any company with its own registered Mimecast tenant | 2026-07-21 | 9f12cd6 | [260721-n49-fix-classifier-mimecast-tenant-scope](./quick/260721-n49-fix-classifier-mimecast-tenant-scope/) |
|
||||
|
||||
## Deferred Items
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
---
|
||||
phase: quick-260721-n49
|
||||
plan: 01
|
||||
subsystem: phishing-triage
|
||||
tags: [mimecast, classifier, multi-tenant, campaign-classifier, blast-radius]
|
||||
|
||||
requires:
|
||||
- phase: 260721-mmf
|
||||
provides: "getBlastRadius() tenant-wide fan-out fix (query-scope, unrelated to this tenant-resolution fix)"
|
||||
provides:
|
||||
- "gatherCampaignEvidence() resolves the reporting company's own Mimecast tenant before calling getBlastRadius(), mirroring the already-shipped route-level fix"
|
||||
affects: [phishing-triage, classifications, mimecast]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Per-company Mimecast tenant resolution: SELECT client_id/client_secret/base_url FROM mimecast_tenants WHERE company_id = $1 AND enabled = true ORDER BY id LIMIT 1, then getMimecastClientForTenant() + { client, cacheScope: companyId } passed as getBlastRadius()'s second argument. Falls back to no second argument (global env client) when absent."
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- lib/services/campaign-classifier.ts
|
||||
- lib/services/campaign-classifier.test.ts
|
||||
|
||||
key-decisions:
|
||||
- "Mirrored app/api/phishing/campaigns/[id]/route.ts's 'Bug 2 (D-05)' tenant-resolution block inline in the classifier rather than extracting a shared helper — the plan's judgment note allowed extraction only if it was a clean, behavior-preserving drop-in for both call sites; the route's block was already correct/tested, so leaving it untouched and duplicating the small inline block in the classifier was the safer choice."
|
||||
|
||||
requirements-completed: [FIX-CLASSIFIER-TENANT]
|
||||
|
||||
duration: 12min
|
||||
completed: 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 threads `company_id` from the `reports` table through `CampaignReportSummary.companyId`.
|
||||
- Before calling `getBlastRadius()`, the classifier queries `mimecast_tenants` for an enabled row matching the primary report's `companyId` and, if found, builds a tenant-scoped client via `getMimecastClientForTenant()` and passes `{ client, cacheScope: companyId }` as the second argument — exactly mirroring the already-shipped, already-tested block in `app/api/phishing/campaigns/[id]/route.ts` ("Bug 2 (D-05)").
|
||||
- When no `companyId` is 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:
|
||||
|
||||
1. **Task 1: Resolve per-company Mimecast tenant in gatherCampaignEvidence()** - `9f75f21` (fix)
|
||||
2. **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` - Added `MimecastTenantRow` interface, `company_id` on `ReportDbRow`/`CampaignReportSummary`, and the per-company tenant-resolution block (query + `getMimecastClientForTenant` + `{ client, cacheScope }`) before the existing `getBlastRadius()` call in `gatherCampaignEvidence()`.
|
||||
- `lib/services/campaign-classifier.test.ts` - Added a `mimecast_tenants` routing branch to `stageQueries`/`StagedRows`, `company_id` on `ReportFixtureRow` (and all existing report fixtures, set to `null` where 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`) when `companyId` is 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, same `getMimecastClientForTenant` build, 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_tenants` row (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 manual `classifyCampaign()` invocation) — no backfill of historical `classifications` rows was in scope for this fix.
|
||||
|
||||
---
|
||||
*Phase: quick-260721-n49*
|
||||
*Completed: 2026-07-21*
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- FOUND: lib/services/campaign-classifier.ts
|
||||
- FOUND: lib/services/campaign-classifier.test.ts
|
||||
- FOUND: .planning/quick/260721-n49-fix-classifier-mimecast-tenant-scope/260721-n49-SUMMARY.md
|
||||
- FOUND commit: 9f75f21 (fix task)
|
||||
- FOUND commit: 9f12cd6 (test task)
|
||||
Loading…
Add table
Add a link
Reference in a new issue