wulf-pulse/.planning/quick/260718-7v8-fix-mimecast-blast-radius-held-message-f/260718-7v8-SUMMARY.md

6.3 KiB

phase plan subsystem tags requires provides affects tech-stack key-files key-decisions requirements-completed duration completed
quick-260718-7v8 01 api
mimecast
blast-radius
phishing
false-positive-fix
phase provides
17-mimecast-blast-radius-lookup getBlastRadius() fan-out orchestration and MimecastClient
Date-scoped getHeldMessages() (optional start/end threaded into request body)
Sender-domain relevance guard on held rows before counting/merging
17-mimecast-blast-radius-lookup
22-approval-ui-livelink-addressable-campaign-review-and-approve
added patterns
Sender-relevance guard: filter external-API rows by domain match against campaign input.sender BEFORE they feed counts or a per-recipient merge — mirrors campaign-classifier's exact-or-proper-subdomain comparison without reusing its allowlist-specific function.
created modified
lib/services/mimecast-client.ts
lib/services/mimecast-blast-radius.ts
lib/services/mimecast-blast-radius.test.ts
lib/services/mimecast-client.test.ts
getHeldMessages() start/end are pre-formatted strings supplied by the caller (mirrors searchDeliveredMessages), never formatted inside the client method.
domainsMatch() is a new local helper in mimecast-blast-radius.ts, not a reuse of campaign-classifier's domainMatchesAllowlist (that helper is allowlist-specific).
Held-row counts (held/matched) and the perRecipient merge both use the same relevantHeldRows array so a filtered-out row can never leak into either output.
BUG-BLAST-RADIUS-HELD
15min 2026-07-18

Quick Task 260718-7v8: Fix Mimecast Blast-Radius Held-Message False Positives Summary

Date-scoped getHeldMessages() plus a sender-domain relevance guard eliminate unrelated held-message false positives in the Blast Radius panel.

Performance

  • Duration: ~15 min
  • Started: 2026-07-18T09:35:00Z (approx.)
  • Completed: 2026-07-18T09:46:50Z
  • Tasks: 2
  • Files modified: 4

Accomplishments

  • MimecastClient.getHeldMessages() now accepts optional start/end and threads them into data[0] as siblings of admin/searchBy (backward compatible when omitted; the existing 403 fallback body inherits them automatically via its { ...reqBody } spread).
  • getBlastRadius() passes the same startStr/endStr window already computed for searchDeliveredMessages into getHeldMessages(), so the held-queue lookup is scoped to the reported campaign's window instead of the recipient's entire hold queue.
  • Added a domainsMatch() sender-relevance guard in mimecast-blast-radius.ts: held rows whose sender domain does not match input.sender's domain (exact-or-proper-subdomain, case-insensitive) are filtered out of relevantHeldRows before they can contribute to held/matched counts or the perRecipient merge.
  • Extended test coverage in both files: date-window threading assertion, unrelated-sender-domain exclusion test, matching-sender-domain override-still-works test, and client-level POST-body-shape tests for start/end presence/absence.

Task Commits

Each task was committed atomically:

  1. Task 1: Date-scope getHeldMessages and add sender-relevance guard in getBlastRadius - 94f7dad (fix)
  2. Task 2: Extend test coverage for date-scoping and sender-relevance filtering - 67ee680 (test)

Plan metadata: committed separately by the orchestrator after this summary.

Files Created/Modified

  • lib/services/mimecast-client.ts - getHeldMessages() gains optional start/end params, threaded into the request body's data[0] alongside admin/searchBy.
  • lib/services/mimecast-blast-radius.ts - getBlastRadius() passes the shared date window into getHeldMessages(); new domainsMatch() helper filters held rows by sender-domain relevance before they feed held/matched/perRecipient.
  • lib/services/mimecast-blast-radius.test.ts - new tests: shared-window assertion, unrelated-sender exclusion, matching-sender override still works.
  • lib/services/mimecast-client.test.ts - new describe('getHeldMessages date-scoping') block: asserts start/end presence/absence in the POST body via a request spy.

Decisions Made

  • Followed the plan's interface precedent exactly: caller pre-formats start/end strings (same pattern as searchDeliveredMessages); the client method does no date formatting itself.
  • domainsMatch() intentionally duplicates (rather than imports) the exact-or-proper-subdomain comparison logic from campaign-classifier.ts's domainMatchesAllowlist, per the plan's explicit instruction not to reuse that allowlist-specific function.
  • Kept the existing "merges delivered/held/threat-event fixtures" test's held row using from: BASE_INPUT.sender (a domain match) so it continues to pass unchanged — confirmed via the test run (25/25 passing).

Deviations from Plan

None — plan executed exactly as written. No architectural changes, no scope additions beyond the two locked fixes and their specified test coverage.

Issues Encountered

Two TypeScript errors surfaced in the new client test ('body' is of type 'unknown' from destructuring vi.spyOn(...).mock.calls[0] directly) — fixed inline by typing the extracted body as any off the correct tuple index (requestSpy.mock.calls[0][2]) before asserting on body.data[0]. This is an in-scope Rule 1 fix (blocking type error in code written as part of this same task), not a deviation from the plan's intent.

Known Stubs

None.

Threat Flags

None — no new network endpoints, auth paths, or trust-boundary changes. This plan only tightens an existing data-filtering boundary (held-message relevance) inside an already-reviewed lookup module.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

The Blast Radius panel (Phase 17/22 consumers) now returns a correctly date-scoped and sender-relevance-filtered held count for any campaign lookup. No follow-up work is required; this was a standalone bug fix confirmed against two live verification scripts referenced in the plan (date-scoping alone: 15→1 false held rows; sender-relevance guard: 1→0).


Quick task: 260718-7v8 Completed: 2026-07-18

Self-Check: PASSED

All modified/created files confirmed present on disk; both task commits (94f7dad, 67ee680) confirmed present in git log.