audit: 33-agent comprehensive audit + critical fixes

Full team audit run, all reports verbatim in docs/AUDIT-2026-05-12.md
(5900+ lines, 30+ critical findings). Already-fixed this commit:
- permission-overrides PUT: self-target block + RolePermissions allow-list + cross-tenant guard
- /api/auth/resolve-identifier: rate-limit + synthetic miss-email kill enumeration
- admin email-change: rotates account.accountId + revokes sessions
- middleware: token-gated email confirm/cancel routes whitelisted
- NAV_CATALOG: 10 dead-link sweeps to existing /admin/<x> targets

Feature work landing same commit: optional username sign-in
(migration 0054), per-user permission overrides (0055) with three-state
matrix tabbed inside UserForm, user disable button, role + outcome +
stage label normalisation across the platform, admin email-change
with auto-notification template.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 16:52:35 +02:00
parent 660553c074
commit 4b9743a594
31 changed files with 7042 additions and 81 deletions

View File

@@ -39,6 +39,7 @@ import {
import { apiFetch } from '@/lib/api/client';
import { cn } from '@/lib/utils';
import { formatCurrency } from '@/lib/utils/currency';
import { STAGE_LABELS, formatOutcome, type PipelineStage } from '@/lib/constants';
import {
useSearch,
type BucketType,
@@ -929,11 +930,15 @@ export function buildFlatRows(args: BuildFlatRowsArgs): FlatRow[] {
const badges: ResultBadge[] = [];
if (i.outcome) {
badges.push({
label: i.outcome.replace(/_/g, ' '),
label: formatOutcome(i.outcome) ?? i.outcome,
tone: i.outcome === 'won' ? 'success' : 'neutral',
});
} else {
badges.push({ label: i.pipelineStage.replace(/_/g, ' '), tone: 'warning' });
badges.push({
label:
STAGE_LABELS[i.pipelineStage as PipelineStage] ?? i.pipelineStage.replace(/_/g, ' '),
tone: 'warning',
});
}
rows.push({
kind: 'result',
@@ -956,7 +961,8 @@ export function buildFlatRows(args: BuildFlatRowsArgs): FlatRow[] {
bucket: 'residentialInterests',
icon: TrendingUp,
label: i.clientName,
sub: i.pipelineStage.replace(/_/g, ' '),
sub:
STAGE_LABELS[i.pipelineStage as PipelineStage] ?? i.pipelineStage.replace(/_/g, ' '),
href: `/${portSlug}/residential/interests/${i.id}`,
});
}