fix(audit): H1 (webhook redirect SSRF), H6 (berth-status case), H7 (residential notes URL)

H1: webhook delivery fetch now uses redirect:'manual' and refuses to read
or expose a redirected (un-revalidated) response, closing the SSRF read
primitive. H6: dashboard report queries matched title-case 'Sold'/'Under
offer' that never match the lowercase canonical, silently reporting 0 sold
/ understated occupancy — now lowercase. H7: NotesList maps the entityType
discriminator to its REST path (residential_* -> residential/clients|
interests) instead of interpolating the raw underscore, which 404'd every
residential notes request.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 12:03:35 +02:00
parent 3c9310f81c
commit f4fb7aae84
3 changed files with 35 additions and 12 deletions

View File

@@ -286,7 +286,7 @@ export async function resolveDashboardReportData(
and(
eq(auditLogs.portId, portId),
eq(auditLogs.entityType, 'berth'),
sql`${auditLogs.newValue}->>'status' = 'Sold'`,
sql`${auditLogs.newValue}->>'status' = 'sold'`,
gte(auditLogs.createdAt, windowFrom),
lte(auditLogs.createdAt, windowTo),
),
@@ -457,12 +457,7 @@ export async function resolveDashboardReportData(
const [{ occCount = 0 } = {}] = await db
.select({ occCount: count() })
.from(berths)
.where(
and(
eq(berths.portId, portId),
sql`${berths.status} IN ('Sold', 'under_offer', 'Under offer')`,
),
);
.where(and(eq(berths.portId, portId), sql`${berths.status} IN ('sold', 'under_offer')`));
const currentRate = Number(totalCount) > 0 ? (Number(occCount) / Number(totalCount)) * 100 : 0;
const series: Array<{ date: string; rate: number }> = [];
const dayMs = 86_400_000;