Commit graph

841 commits

Author SHA1 Message Date
7e33a8568b docs(23-04): complete admin phishing-automation page plan
- Adds 23-04-SUMMARY.md documenting Task 1 (page) and Task 2 (index tile)
- Records that Task 1's human-verify checkpoint was approved via a
  temporary production container rebuild rather than local npm run dev
2026-07-16 21:02:00 -04:00
160fe88253 feat(23-04): add admin index tile for Phishing Automation
- Adds a Phishing Automation tile to the Tools & Data section of /admin
- Links to /admin/phishing-automation, imports Bot icon from lucide-react
2026-07-16 21:01:08 -04:00
5dabaad722 feat(23-04): build /admin/phishing-automation page with per-stage toggles
- Company table with search + type filter (cloned from client-scope pattern)
- Three independent Switch toggles per row: auto-parse, auto-classify, auto-report
- toggle() PATCHes /api/admin/phishing-automation/{companyId} with all three current flags
- Helper caption clarifies stage dependency (informational, not enforced)
2026-07-16 19:46:16 -04:00
b69558aae8 docs(phase-23): update tracking after wave 1 2026-07-16 19:42:35 -04:00
9aadbcdeea chore: merge executor worktree (worktree-agent-a5d0fe500dc50fe1e) 2026-07-16 19:40:47 -04:00
d9d8962ca7 chore: merge executor worktree (worktree-agent-a1093d44311d63302) 2026-07-16 19:40:47 -04:00
9f29424264 chore: merge executor worktree (worktree-agent-a099928868240d5fb) 2026-07-16 19:40:47 -04:00
fe2375e3a2 docs(23-01): complete classification disposition + acknowledgment plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 19:40:21 -04:00
6224e44219 feat(23-01): wire acknowledge_user manual-path real note post into remediateApprovedActions
- remediateApprovedActions now captures the transaction's RemediateResult,
  then post-commit checks whether an approved acknowledge_user row was
  transitioned this pass (alreadyCompleted === false) and, if so, calls
  generateAndPostAcknowledgment(campaignId) exactly once
- Call happens outside the DB transaction (network I/O hazard) and is
  wrapped in its own try/catch that logs and swallows failures -- the DB
  transition has already committed
- Every other action type (block_sender, purge_message, warn_user,
  reset_password, isolate_endpoint, disable_forwarding_rule, quarantine)
  remains a simulated status-only transition, unchanged
- Updated top-of-file D-01 doc comment to record the narrow D-04 carve-out
- Tests: acknowledge_user IS posted once when remediated, NOT called for
  block_sender/warn_user-only remediation, NOT called on idempotent re-run
  of an already-completed acknowledge_user row, and a post rejection does
  not propagate out of remediateApprovedActions
2026-07-16 19:39:13 -04:00
42a6c05d6f docs(23-03): append self-check results to SUMMARY.md
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 19:38:43 -04:00
63d6e85f37 docs(23-03): complete migration 100 + phishing-automation API plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 19:38:29 -04:00
50e241592c feat(23-01): add generateAndPostAcknowledgment customer-visible note writer
- New generateAndPostAcknowledgment(campaignId) posts a short, appreciative
  thank-you note to every ticket linked to a campaign, using noteType 18
  (Client Portal Note, verified live against tenant's TicketNotes field
  metadata) so the note is customer-visible; publish stays 1 unchanged
- Body is a fixed template with zero evidence/URL/classification
  interpolation (T-23-01) -- not the evidence-dump formatTriageNote() template
- Mirrors generateAndPostTriageNote's per-ticket try/catch-in-loop error
  isolation and { noteText, tickets } return shape
- Tests: noteType 18 + publish 1 payload assertion, per-ticket failure
  isolation, and zero-linked-reports case
2026-07-16 19:37:56 -04:00
78b6b49c5b docs(23-03): backfill Phase 23 CLASSDISP-*/AUTOGATE-* requirement entries
- Adds Classification Disposition + Automation Gate subsection under v1
  Requirements (Phase 23 is active v3.0 roadmap work, not deferred)
- Adds 6 Traceability rows (CLASSDISP-01/02/03, AUTOGATE-01/02/03 -> Phase 23, Pending)
- Updates Coverage footer counts (32 -> 38 total)
- No existing v1/v2 entries or rows altered
2026-07-16 19:37:45 -04:00
50d0def2ae docs(23-02): append self-check result to SUMMARY
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 19:37:07 -04:00
ea5047c80a feat(23-03): add admin phishing-automation GET/PATCH/DELETE routes
- GET /api/admin/phishing-automation: admin-gated list with COALESCE(...,false) gate defaults
- PATCH /api/admin/phishing-automation/[companyId]: upserts all three flags, actor+timestamp stamped
- DELETE /api/admin/phishing-automation/[companyId]: reverts company to all-OFF default
- Mirrors app/api/admin/company-scope/* route pattern
2026-07-16 19:37:06 -04:00
14ed8ca248 feat(23-01): add USER_AWARENESS verdict + acknowledge_user action mapping
- Add USER_AWARENESS to the Verdict union in campaign-classifier.ts
- mapVerdictToActions('USER_AWARENESS') returns ['acknowledge_user']; not added to DESTRUCTIVE_ACTIONS so requires_approval computes false
- classifyCampaign's simulation branch now assigns verdict = 'USER_AWARENESS' directly instead of falling through to evaluateSpamVsUnwanted
- deriveDefaultParams('acknowledge_user') returns {} (no operator-editable params)
- Widen TriageNoteEvidence.verdict to admit 'USER_AWARENESS' (pure type widen, no formatting change)
- Tests: classifier simulation fixtures now assert USER_AWARENESS/acknowledge_user/requiresApproval=false; new mapVerdictToActions/computeRequiresApproval/deriveDefaultParams cases
2026-07-16 19:36:53 -04:00
44bbe90ae0 docs(23-02): complete Review UI USER_AWARENESS + acknowledge_user support plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 19:36:53 -04:00
db7d67cf25 feat(23-03): add phishing_automation_gate migration 100
- Opt-in per-company table (auto_parse/auto_classify/auto_report, all default false)
- PK company_id -> companies(id) ON DELETE CASCADE, updated_by/updated_at audit cols
- Applied to running dev Postgres (existing volume, not auto-run on init)
2026-07-16 19:36:14 -04:00
9ae834f5e6 feat(23-02): add USER_AWARENESS support to TimelineCard, prevent review-page crash
- Add USER_AWARENESS to classification-entry verdict union and VERDICT_TINT (emerald, matching ClassificationCard)
- Widen campaign_classified audit-case cast to include USER_AWARENESS
- Add defensive fallback on both .split(' ') tint sites so any unrecognized runtime verdict string can never crash the render (T-23-12) — phishing-timeline.ts emits verdict as an unvalidated plain string
2026-07-16 19:36:11 -04:00
547372e57a feat(23-02): add acknowledge_user manual action case to ActionAreaCard
- Add acknowledge_user: 'Acknowledge user' to ACTION_LABEL
- Add case 'acknowledge_user' to ActionParamsForm mirroring no_action (no-params form)
- Renders as a normal checkbox + Approve action for every company; no automation-gate logic added (D-04) — the auto-approval carve-out lives only in the webhook path (Plan 05)
2026-07-16 19:35:38 -04:00
af8a5bd438 feat(23-02): add USER_AWARENESS badge variant + acknowledge_user label to ClassificationCard
- Add USER_AWARENESS to ClassificationCardData verdict union
- Add emerald VERDICT_VARIANT_CLASS entry for USER_AWARENESS (distinct from UNWANTED amber and THREAT destructive)
- Add acknowledge_user: 'Acknowledge user' to ACTION_LABEL
2026-07-16 19:35:22 -04:00
7048bf693a docs(23): create phase plan 2026-07-16 19:31:58 -04:00
2f48dba6be docs(23): fix REQUIREMENTS.md section placement instruction in plan 23-03
Checker warning: creating a new '## v3 Requirements' top-level section would
misleadingly read as deferred/future work (that's what '## v2 Requirements'
means in this file). Phase 23 is active v3.0 roadmap work -- instruct adding
a subsection under the existing '## v1 Requirements' heading instead,
mirroring Phase 22's existing subsection.
2026-07-16 19:30:49 -04:00
b50c1f5e1a fix(23): resolve checker blockers (timeline crash, acknowledge_user manual post) + doc/type-safety warnings 2026-07-16 19:26:00 -04:00
683e647984 docs(23): create phase plan (5 plans, 2 waves)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 19:08:45 -04:00
fd92263416 docs(23): correct acknowledge_user delivery field per live Autotask verification
Verified against TicketNotes/entityInformation/fields on the real tenant:
publish has no client-facing value (1=All Autotask Users, 2=Internal Project
Team, 4=Internal & Co-Managed -- all internal-staff tiers). Client Portal
visibility is controlled by noteType=18 ("Client Portal Note"), not publish.
Corrects D-03's original "flip the publish flag" framing before planning.
2026-07-16 18:59:33 -04:00
bd2fc240ec docs(state): record phase 23 context session 2026-07-16 18:37:10 -04:00
98c7153a10 docs(23): capture phase context 2026-07-16 18:37:04 -04:00
8a11a9f331 docs(23): add Classification Disposition + Per-Client Automation Gate phase
Scoping a dedicated "User Awareness" verdict for confirmed phishing-simulation
reports (KnowBe4/Breach Secure Now) plus a per-company automation gate for the
parse/classify/report-to-ticket pipeline stages. Surfaced live during Phase 22
review of a real Breach Secure Now report (ticket 699415).
2026-07-16 18:25:27 -04:00
1c9ce682e0 docs(260716-pgr): record quick task completion in STATE.md 2026-07-16 18:21:54 -04:00
3f16268152 fix(260716-pgr): confidence display shows percent, not raw 0-1 scale
classifications.confidence is 0.0-1.0 (1 = max confidence). ClassificationCard
appended "%" directly to the raw value, so confidence=1 rendered as "1%
confidence" -- read as near-zero, the opposite of its true meaning. Found live
while reviewing a Breach Secure Now simulated-phishing classification.
2026-07-16 18:21:40 -04:00
40dd1e10e9 docs(260716-n46): record quick task completion in STATE.md
Mimecast blast-radius fixes: future end-date no longer silently swallowed
into a false-clean result, and campaigns from companies with their own
registered mimecast_tenants row (e.g. Seubert & Associates) now resolve
against that tenant instead of always querying the global Wulf tenant.
2026-07-16 16:50:10 -04:00
50c55eec98 Merge branch 'worktree-agent-aa777fce7f2c01045' 2026-07-16 16:49:36 -04:00
4c0b1ff5aa docs(260716-n46): append self-check result to SUMMARY 2026-07-16 16:48:43 -04:00
1e384d5c9e docs(260716-n46): add plan SUMMARY and deferred-items log
Documents the getBlastRadius per-tenant injection/swallowed-error fixes,
the campaign detail route's date-window clamp + tenant resolution, the
in-scope test-hygiene deviation, and the pre-existing out-of-scope
itglue-search.test.ts failure.
2026-07-16 16:48:29 -04:00
9951e53832 fix(260716-n46): clamp blast-radius date window and resolve per-company Mimecast tenant
Bug 1: clamp dateWindow.end to Math.min(createdAt + 24h, Date.now()) so a
freshly-detected campaign (<24h old primary report) never sends Mimecast a
future end-date -- previously rejected as err_track_and_trace_invalid_end_date
and swallowed internally as a false-clean zero-count result.

Bug 2 (D-05): add company_id to the reports SELECT and, when the reporting
company has its own enabled mimecast_tenants row, resolve a tenant-scoped
client via getMimecastClientForTenant() and thread it into getBlastRadius as
{ client, cacheScope: companyId }. Falls back to the global env-configured
client when no company-specific tenant is registered.
2026-07-16 16:47:19 -04:00
12250c1e1d test(260716-n46): add coverage for getMimecastClientForTenant
Covers the already-implemented per-tenant factory: returns a MimecastClient
instance, builds a new independent instance per call (never the cached
global), doesn't affect getMimecastClient()'s singleton, and defaults
base_url when omitted. Uses fake credentials only.
2026-07-16 16:44:54 -04:00
7c724cc489 feat(260716-n46): support per-tenant client injection + surface swallowed delivered-search errors
- getBlastRadius(input, options?) accepts an optional injected MimecastClient
  and cacheScope; an injected client bypasses the global isMimecastConfigured()
  gate since it carries its own credentials
- cache key namespaced by cacheScope to prevent cross-tenant collisions
- deliveredResult.error (previously swallowed) now rethrown so the outer
  catch converts it to status: unavailable / reason: lookup_failed --
  defense-in-depth against Bug 1 (future end-date rejected by Mimecast)
- test mock hygiene: getMimecastClientMock now cleared in beforeEach
2026-07-16 16:44:23 -04:00
4d54abacae test(260716-n46): add failing tests for tenant client injection + swallowed-error surfacing
- Fake tenant client via options.client bypasses getMimecastClient
- Injected tenant client runs fan-out even when global env unconfigured
- searchDeliveredMessages error field now expected to degrade to unavailable/lookup_failed
2026-07-16 16:43:37 -04:00
ad84885daa docs(260716-n46): pre-dispatch plan for Mimecast blast-radius fixes 2026-07-16 16:42:23 -04:00
ced258bbda docs(22): mark phase 22 complete — approval UI (LiveLink) shipped
All 6 plans merged: pure-logic extraction (resolver/default-params/timeline),
API read surface, evidence/classification/timeline cards, action-area card,
and the ticket-scoped review page + campaigns list + nav entry. Blocking
LiveLink checkpoint verified against a real Autotask ticket (numeric ticket
ID confirmed, no ticket_number fallback needed).
2026-07-16 15:40:30 -04:00
897bc67b9e Merge branch 'worktree-agent-a2447e1cd43bb4c28' 2026-07-16 15:39:28 -04:00
4b2a7713b4 docs(22-06): complete LiveLink review page plan
Task 3 (blocking human-verify) resolved as "verified": a real production
Autotask LiveLink click against ticket 699340 confirmed the numeric-
ticket-id resolver assumption, no ticket_number fallback needed. Marks
REVIEW-01, REVIEW-05, REVIEW-06 complete in REQUIREMENTS.md.
2026-07-16 15:39:12 -04:00
5f5d809050 feat(22-06): campaigns list page + Phishing nav entry (D-00, D-02)
- app/phishing/page.tsx: minimal DataTable-backed campaigns list, row click
  navigates to /phishing/tickets/{firstReportTicketId}, EmptyState when no
  campaigns exist yet
- components/navigation/app-navigation.tsx: add flat "Phishing" nav item
  (ShieldAlert icon) immediately after PAX8, visible to all roles (every
  role has phishing:read)
2026-07-16 14:58:52 -04:00
3761312f93 feat(22-06): ticket-scoped LiveLink review page (REVIEW-01,05,06)
- app/phishing/tickets/[ticketId]/page.tsx: resolves ticket->campaign via
  the plan-02 resolver route, drives a loading/not-triaged/ungrouped/ready/
  error state machine, branches ready into grouped-but-unclassified
  (Classify CTA, no ClassificationCard/ActionAreaCard) vs. classified (all
  four cards with explicit props), refetches after every action (D-04),
  session-only auth (no token/query-param scheme)
- app/api/phishing/reports/[report_id]/route.ts (new, additive): thin
  report-scoped evidence + fresh blast-radius lookup for the D-08
  ungrouped-report state, which has no campaignId to key the existing
  campaign-detail route on — added as a Rule 2 dependency since the plan's
  own D-08 truth ("standalone-report notice + evidence") has no other data
  source
2026-07-16 14:58:39 -04:00
bced734717 Merge branch 'worktree-agent-a68fa4bcada53d003' 2026-07-16 14:45:47 -04:00
81734820d5 docs(22-02): append self-check result to SUMMARY
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 14:45:25 -04:00
de13cd73b3 docs(22-02): complete review page read surface plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
2026-07-16 14:45:03 -04:00
0b5fc36cd5 Merge branch 'worktree-agent-ac9c5343cdf410faf' 2026-07-16 14:44:19 -04:00
a8bbd05e6b docs(22-05): append self-check results to SUMMARY
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 14:43:52 -04:00