docs(quick-260718-7v8): Fix Mimecast blast-radius held-message false-positive fix
This commit is contained in:
parent
b7d6be47c6
commit
a30d72e9ba
2 changed files with 112 additions and 1 deletions
|
|
@ -28,7 +28,7 @@ See: .planning/PROJECT.md (updated 2026-07-14)
|
||||||
Phase: Milestone v3.0 complete
|
Phase: Milestone v3.0 complete
|
||||||
Plan: —
|
Plan: —
|
||||||
Status: Awaiting next milestone
|
Status: Awaiting next milestone
|
||||||
Last activity: 2026-07-18 — Completed quick task 260717-v6c: Mark as accidental report action
|
Last activity: 2026-07-18 — Completed quick task 260718-7v8: Mimecast blast-radius held-message false-positive fix
|
||||||
|
|
||||||
## Performance Metrics
|
## Performance Metrics
|
||||||
|
|
||||||
|
|
@ -128,6 +128,7 @@ None yet.
|
||||||
| 260716-pgr | Fix confidence display bug (0-1 scale rendered as raw percent, e.g. "1%" instead of "100%") | 2026-07-16 | 3f16268 | [260716-pgr-fix-confidence-display-bug-in-classifica](./quick/260716-pgr-fix-confidence-display-bug-in-classifica/) |
|
| 260716-pgr | Fix confidence display bug (0-1 scale rendered as raw percent, e.g. "1%" instead of "100%") | 2026-07-16 | 3f16268 | [260716-pgr-fix-confidence-display-bug-in-classifica](./quick/260716-pgr-fix-confidence-display-bug-in-classifica/) |
|
||||||
| 260717-a19 | Fix phishing simulation-vendor allowlist gaps (3 missing KnowBe4 domains), auto-parse timing race (retry on ticket.update), and parseAndStoreMessage idempotency; reclassified 6 stale Seubert campaigns (all flipped UNWANTED → USER_AWARENESS) | 2026-07-17 | cf04f07 | [260717-a19-fix-phishing-simulation-vendor-allowlist](./quick/260717-a19-fix-phishing-simulation-vendor-allowlist/) |
|
| 260717-a19 | Fix phishing simulation-vendor allowlist gaps (3 missing KnowBe4 domains), auto-parse timing race (retry on ticket.update), and parseAndStoreMessage idempotency; reclassified 6 stale Seubert campaigns (all flipped UNWANTED → USER_AWARENESS) | 2026-07-17 | cf04f07 | [260717-a19-fix-phishing-simulation-vendor-allowlist](./quick/260717-a19-fix-phishing-simulation-vendor-allowlist/) |
|
||||||
| 260717-v6c | Add "Mark as accidental report" action to the phishing Action Area — closes out a campaign and posts a fixed customer-facing note to the reporter (distinct from the silent "Mark as false positive" action) | 2026-07-18 | 565a0c1 | [260717-v6c-add-a-mark-as-accidental-report-action-t](./quick/260717-v6c-add-a-mark-as-accidental-report-action-t/) |
|
| 260717-v6c | Add "Mark as accidental report" action to the phishing Action Area — closes out a campaign and posts a fixed customer-facing note to the reporter (distinct from the silent "Mark as false positive" action) | 2026-07-18 | 565a0c1 | [260717-v6c-add-a-mark-as-accidental-report-action-t](./quick/260717-v6c-add-a-mark-as-accidental-report-action-t/) |
|
||||||
|
| 260718-7v8 | Fix Mimecast blast-radius false positives — date-scope `getHeldMessages()` and add a sender-domain relevance guard so unrelated held mail in a recipient's queue no longer inflates held/matched counts or overwrites a genuinely delivered recipient's status | 2026-07-18 | b7d6be4 | [260718-7v8-fix-mimecast-blast-radius-held-message-f](./quick/260718-7v8-fix-mimecast-blast-radius-held-message-f/) |
|
||||||
|
|
||||||
## Deferred Items
|
## Deferred Items
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,110 @@
|
||||||
|
---
|
||||||
|
phase: quick-260718-7v8
|
||||||
|
plan: 01
|
||||||
|
subsystem: api
|
||||||
|
tags: [mimecast, blast-radius, phishing, false-positive-fix]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires:
|
||||||
|
- phase: 17-mimecast-blast-radius-lookup
|
||||||
|
provides: getBlastRadius() fan-out orchestration and MimecastClient
|
||||||
|
provides:
|
||||||
|
- Date-scoped getHeldMessages() (optional start/end threaded into request body)
|
||||||
|
- Sender-domain relevance guard on held rows before counting/merging
|
||||||
|
affects: [17-mimecast-blast-radius-lookup, 22-approval-ui-livelink-addressable-campaign-review-and-approve]
|
||||||
|
|
||||||
|
# Tech tracking
|
||||||
|
tech-stack:
|
||||||
|
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."
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
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
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "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."
|
||||||
|
|
||||||
|
requirements-completed: [BUG-BLAST-RADIUS-HELD]
|
||||||
|
|
||||||
|
# Metrics
|
||||||
|
duration: 15min
|
||||||
|
completed: 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.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue