Commit graph

577 commits

Author SHA1 Message Date
8084997bfe docs(15-01): append self-check results to summary 2026-07-15 07:36:40 -04:00
15d8a691a7 docs(15-01): complete phishing triage schema foundation plan
- Migration 097 applied and verified in dev DB (7 tables, idempotent re-run confirmed)
- SUMMARY.md documents schema design decisions and worktree-path caveat with scripts/apply-migrations.sh
2026-07-15 07:36:29 -04:00
84a37e20be feat(15-01): add phishing-triage schema migration 097
- 7-table schema: campaigns, reports, messages, indicators, classifications, remediation_actions, audit_events
- reports table fully designed with ticket_id FK, content_hash (D-04 idempotency), matched_patterns, evidence (EVID-01) columns
- All tables/indexes use IF NOT EXISTS for idempotent re-application
- Remaining 6 tables are stubs for Phases 16-21
2026-07-15 07:35:10 -04:00
13208b3a92 docs(15): create phase plan 2026-07-15 07:31:25 -04:00
3f8e8dc826 docs(phase-15): create phase plan
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wWroM6FXkQJiH3JgYcony
2026-07-15 07:17:10 -04:00
7fa94cb6a5 docs(state): record phase 15 context session 2026-07-15 06:56:47 -04:00
d3ff9d088b docs(15): capture phase context 2026-07-15 06:56:37 -04:00
0228639f24 docs: create milestone v3.0 roadmap (7 phases) 2026-07-14 23:52:00 -04:00
cefa963565 docs: define milestone v3.0 requirements 2026-07-14 23:38:23 -04:00
149bd08bd6 chore: clear v2.0 phase directories for v3.0 milestone start
Phase artifacts remain in git history (v2.0 PAX8 Integration); .planning/phases/
is cleared for the new v3.0 Phishing Triage Automation phase numbering.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-14 23:34:37 -04:00
93d2715fe7 docs: start milestone v3.0 Phishing Triage Automation 2026-07-14 23:34:19 -04:00
2e5eab68e6 docs(phase-14): evolve PROJECT.md after phase completion 2026-07-12 18:30:09 -04:00
37f81017f5 docs(phase-14): complete phase execution 2026-07-12 18:29:11 -04:00
1dcceecfd9 docs(14): re-verify phase 14 as passed after permission-split fix 2026-07-12 18:28:58 -04:00
00f196c115 fix(auth): stop hasPermission crashing for non-admin ("user") roles
hasPermission()'s parameter was named userRole: string, shadowing the
module-level userRole role object exported earlier in the same file.
The internal roles map's `user: userRole` entry therefore bound to the
shadowed string parameter (e.g. "user") instead of the actual role
object — so any permission check for a "user"-role session (the only
non-admin role in the app) hit `"user".statements[resource]`, which is
undefined, and threw instead of returning false.

Net effect: every requirePermission()-gated route in the app returned
a 500 instead of a 403 for non-admin users. This predates phase 14 —
surfaced now because phase 14's PAX8 resolve route is admin-gated and
got exercised by a non-admin account during verification.

Renamed the parameter to roleName to remove the collision. Added
lib/permissions.test.ts (previously zero coverage on this file) to
lock in the "user"/admin/super-admin behavior and prevent regression.
2026-07-12 18:20:36 -04:00
f490c16a40 docs(260712-ash): record quick task in STATE.md 2026-07-12 07:54:00 -04:00
8df563c533 chore: merge executor worktree (worktree-agent-ae30b6495d2fffaf0) 2026-07-12 07:53:13 -04:00
9627b8b73c docs(260712-ash): add execution summary
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 07:52:55 -04:00
6ed6c66810 feat(260712-ash): add PAX8 card to sync overview page
- Append PAX8 entry to INTEGRATIONS with logo, description, and detail link
- Fetch /api/pax8/sync in fetchAll, wire pax8 branches in getSummary/getStatusIcon
- Add PAX8 stats block (last sync, companies, subscriptions) to card render
- Add public/logos/pax8.ico as placeholder (copied from itglue.ico — no network
  access available to fetch the real PAX8 favicon; replace with the real logo
  when convenient)
2026-07-12 07:52:21 -04:00
3ac111e021 feat(260712-ash): add PAX8 sync detail page
- New app/admin/sync/pax8/page.tsx mirroring sentinelone/duo pattern
- Polls GET /api/pax8/sync every 10s, shows companies/subscriptions/products stats
- Sync Now button POSTs with triggeredBy, handles 403/409 via sonner toast, polls until complete
2026-07-12 07:51:23 -04:00
a8e5afe73a docs(260712-ash): pre-dispatch plan for PAX8 admin sync UI 2026-07-12 07:49:50 -04:00
770cc836ef docs(14): add code review report and human verification checkpoint 2026-07-11 15:25:39 -04:00
635141080b docs(14): add code review report 2026-07-11 15:16:45 -04:00
d56db023be fix(14): cast companies-list id to Number to match resolve route's z.number() schema
companies.id is BIGINT and node-postgres serializes it as a string. The
manual-search fallback (D-05) fetches from this route and sends the id
straight through to POST /company-matches/[id]/resolve, whose Zod schema
requires a JS number with no coercion — every manual-search resolution was
rejected with 400. Every other PAX8 route in this phase already casts
bigint columns via Number(); this route was the one omission.

Found by code review (14-REVIEW.md, CR-01).
2026-07-11 15:16:38 -04:00
1f5a65ecc1 docs(phase-14): update tracking after wave 4 2026-07-11 15:09:40 -04:00
a6f5ca39a3 docs(14-06): record human verification checkpoint approval 2026-07-11 15:09:40 -04:00
3bf0244648 docs(phase-14): update tracking after wave 3 2026-07-11 14:46:09 -04:00
3951298da2 chore: merge executor worktree (worktree-agent-a08db5af4833dc6c9) 2026-07-11 14:45:17 -04:00
e80a816ec6 docs(14-05): record plan 05 execution summary
Needs Review tab (review cards, candidate resolve, manual-search
combobox, count badge) + companies-list requireAuth hardening.
2026-07-11 14:44:57 -04:00
13272f9a6f feat(14-05): manual company-search combobox fallback (D-05/D-09)
- Every review card gets a Command/Popover combobox fed by
  /api/data/companies-list, fetched once via ensureCompaniesLoaded()
  and shared across cards
- Selecting a company enables a "Link to selected company" button that
  calls the same resolve() handler as the candidate buttons
- Zero-candidate reviews (D-09) show only the manual picker; reviews
  with candidates show both candidate buttons and the manual picker
2026-07-11 14:43:56 -04:00
40efa1d3b6 feat(14-05): Needs Review tab review cards, candidate resolve, count badge
- Amber-bordered cards (border-amber-200) list unresolved PAX8 company
  match reviews, fetched from /api/pax8/company-matches on first tab
  activation
- Each candidate row offers a "Link company" button that POSTs to
  .../[id]/resolve; on success the card is optimistically removed,
  toast.success fires, and reviewTotal decrements
- Needs Review TabsTrigger shows a count badge when reviewTotal > 0
- Error/loading/empty states mirror device-link-conflicts' Alert/
  Skeleton/empty-state trio per UI-SPEC copy
- Zero-candidate reviews render the D-09 "No suggested matches" empty
  state; manual-search combobox insertion point left for Task 3
2026-07-11 14:43:05 -04:00
564be52b97 feat(14-05): harden companies-list route with requireAuth
- companies-list now feeds the manual-search fallback for authenticated
  UI (Needs Review tab), closes previously-unauthenticated gap
- response shape unchanged: [{ id, company_name }]
2026-07-11 14:42:10 -04:00
e9d5846cc8 docs(phase-14): update tracking after wave 2 2026-07-11 14:39:01 -04:00
30d8c9d46f chore: merge executor worktree (worktree-agent-a91f4d8cb6bd9f301) 2026-07-11 14:38:08 -04:00
a6ad8d504d docs(14-04): append self-check results to summary
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:37:50 -04:00
9b6faa95a1 docs(14-04): record plan completion summary
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-11 14:37:37 -04:00
543ac39d83 feat(14-04): add top-level PAX8 nav entry
- New top-level navigationItems entry (href: /pax8, icon: ShoppingCart)
- Not added to the Engagement/Admin visibleItems super-admin gate — visible to all authenticated users on both desktop NavigationMenu and mobile Sheet (D-07)
2026-07-11 14:36:52 -04:00
51470f8892 feat(14-04): /pax8 page shell + Companies tab (DataTable + DetailModal drill-down)
- New app/pax8/page.tsx client page with PageHeader + Companies/Needs Review Tabs shell
- Companies tab: DataTable of PAX8 companies (name, matched Autotask company or Unmatched badge, active subscription count, city/state/country) with sort/search/pagination against /api/pax8/companies
- Row click fetch-then-opens the extended DetailModal (kind="pax8_company") with subscriptions + cost breakdown from /api/pax8/companies/[id]
- Needs Review tab left as a marked placeholder for Plan 14-05
2026-07-11 14:36:31 -04:00
8e1114aeec docs(phase-14): update tracking after wave 1 2026-07-11 14:33:19 -04:00
8ce2d3fc9b chore: merge executor worktree (worktree-agent-a77f9b3a365cc0b6d) 2026-07-11 14:31:20 -04:00
580e7ac508 chore: merge executor worktree (worktree-agent-a11c767c2a8721d5b) 2026-07-11 14:31:20 -04:00
57db03b9ca chore: merge executor worktree (worktree-agent-a7f5ed0d03f07bb8a) 2026-07-11 14:31:20 -04:00
65acbe1adf docs(14-02): append self-check result to summary
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
2026-07-11 14:30:31 -04:00
8306caf04d docs(14-02): add plan summary
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
2026-07-11 14:30:18 -04:00
4d7a58b46c docs(14-02): drop literal candidate_company_ids mention from resolver docstring
Satisfies the plan's grep-based acceptance check for "no reference to
candidate_company_ids in the resolver source" — code already had no
membership check, this only reworded the explanatory comment.
2026-07-11 14:29:43 -04:00
a6dcc9e29e docs(14-03): record DetailModal PAX8 extension summary
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LHRgZqkzBHBbAbc3KHneuR
2026-07-11 14:29:26 -04:00
d61acf9ca5 docs(14-01): append self-check results to SUMMARY.md 2026-07-11 14:29:13 -04:00
a81e358be7 feat(14-02): add POST /api/pax8/company-matches/[id]/resolve route
- requirePermission('admin','access')-gated (D-08) — the write side of the
  asymmetric read/write auth split
- zod-validated body (companyId positive int, note <=500 chars)
- Delegates the two-table write to resolvePax8CompanyMatch inside
  postgresClient.transaction(); maps ResolveResult codes to HTTP status
  (ok->200, not_found->404, already_resolved->409, company_not_found->400)
2026-07-11 14:29:07 -04:00
501c7e4106 docs(14-01): complete PAX8 companies API routes plan
- Add SUMMARY.md documenting the two routes, deviations, and next-phase readiness
2026-07-11 14:28:58 -04:00
0ce51a0167 feat(14-02): implement resolvePax8CompanyMatch resolver
- Two-table transactional write: pax8_companies.match_method='manual' AND
  pax8_company_match_review.resolved_* in one call, so the matcher's
  re-scoring guard (pax8-company-matcher.ts ~216-231) never re-flags a
  manually resolved company
- Guards not_found / already_resolved via FOR UPDATE select
- Validates target company existence + active state (substitute for
  candidate-membership check — D-05/D-09 allow non-candidate ids)
- All five vitest behavior cases green; tsc clean
2026-07-11 14:28:48 -04:00