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

@@ -1,6 +1,7 @@
import type { Template } from '@pdfme/common';
import type { PipelineData } from '@/lib/services/report-generators';
import { stageLabel } from '@/lib/constants';
export const pipelineReportTemplate: Template = {
basePdf: 'BLANK_PDF' as unknown as string,
@@ -69,8 +70,7 @@ export function buildPipelineInputs(
const summaryLines = stageOrder
.filter((stage) => (data.stageCounts[stage] ?? 0) > 0)
.map((stage) => {
const label = stage.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
return `${label}: ${data.stageCounts[stage] ?? 0} interest(s)`;
return `${stageLabel(stage)}: ${data.stageCounts[stage] ?? 0} interest(s)`;
});
// Include stages not in standard order

View File

@@ -1,6 +1,7 @@
import type { Template } from '@pdfme/common';
import type { RevenueData } from '@/lib/services/report-generators';
import { stageLabel } from '@/lib/constants';
export const revenueReportTemplate: Template = {
basePdf: 'BLANK_PDF' as unknown as string,
@@ -74,12 +75,11 @@ export function buildRevenueInputs(data: RevenueData, portName?: string): Record
breakdownLines.push('No revenue data available.');
} else {
for (const stage of orderedStages) {
const label = stage.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
const amount = Number(data.stageRevenue[stage] ?? 0).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
breakdownLines.push(`${label}: ${amount}`);
breakdownLines.push(`${stageLabel(stage)}: ${amount}`);
}
}