Files
livedash-node/app/api/admin/security-monitoring/alerts/route.ts
Kaj Kowalski 7a3eabccd9 feat: enhance security, performance, and stability
This commit introduces a range of improvements across the application:

- **Security:**
  - Adds authentication to the CSP metrics endpoint.
  - Hardens CSP bypass detection regex to prevent ReDoS attacks.
  - Improves CORS headers for the CSP metrics API.
  - Adds filtering for acknowledged alerts in security monitoring.

- **Performance:**
  - Optimizes database connection pooling for NeonDB.
  - Improves session fetching with abort controller.

- **Stability:**
  - Adds error handling to the tRPC demo component.
  - Fixes type inconsistencies in session data mapping.

- **Docs & DX:**
  - Ignores  files in git.
  - Fixes a token placeholder in the documentation.
2025-07-12 01:03:52 +02:00

151 lines
3.9 KiB
TypeScript

import { type NextRequest, NextResponse } from "next/server";
import { getServerSession } from "next-auth";
import { z } from "zod";
import { authOptions } from "@/lib/auth";
import {
AuditOutcome,
createAuditContext,
securityAuditLogger,
} from "@/lib/securityAuditLogger";
import {
type AlertSeverity,
securityMonitoring,
} from "@/lib/securityMonitoring";
const alertQuerySchema = z.object({
severity: z.enum(["LOW", "MEDIUM", "HIGH", "CRITICAL"]).optional(),
acknowledged: z.enum(["true", "false"]).optional(),
limit: z
.string()
.transform((val) => Number.parseInt(val, 10))
.optional(),
offset: z
.string()
.transform((val) => Number.parseInt(val, 10))
.optional(),
});
const acknowledgeAlertSchema = z.object({
alertId: z.string().uuid(),
action: z.literal("acknowledge"),
});
export async function GET(request: NextRequest) {
try {
const session = await getServerSession(authOptions);
if (!session?.user || !session.user.isPlatformUser) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const url = new URL(request.url);
const params = Object.fromEntries(url.searchParams.entries());
const query = alertQuerySchema.parse(params);
const context = await createAuditContext(request, session);
// Get alerts based on filters
let alerts = securityMonitoring.getActiveAlerts(
query.severity as AlertSeverity
);
// Apply acknowledged filter if provided
if (query.acknowledged !== undefined) {
const showAcknowledged = query.acknowledged === "true";
alerts = alerts.filter((alert) =>
showAcknowledged ? alert.acknowledged : !alert.acknowledged
);
}
// Apply pagination
const limit = query.limit || 50;
const offset = query.offset || 0;
const paginatedAlerts = alerts.slice(offset, offset + limit);
// Log alert access
await securityAuditLogger.logPlatformAdmin(
"security_alerts_access",
AuditOutcome.SUCCESS,
context,
undefined,
{
alertCount: alerts.length,
filters: query,
}
);
return NextResponse.json({
alerts: paginatedAlerts,
total: alerts.length,
limit,
offset,
});
} catch (error) {
console.error("Security alerts API error:", error);
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: "Invalid query parameters", details: error.errors },
{ status: 400 }
);
}
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}
export async function POST(request: NextRequest) {
try {
const session = await getServerSession(authOptions);
if (!session?.user || !session.user.isPlatformUser) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const body = await request.json();
const { alertId, action } = acknowledgeAlertSchema.parse(body);
const context = await createAuditContext(request, session);
if (action === "acknowledge") {
const success = await securityMonitoring.acknowledgeAlert(
alertId,
session.user.id
);
if (!success) {
return NextResponse.json({ error: "Alert not found" }, { status: 404 });
}
// Log alert acknowledgment
await securityAuditLogger.logPlatformAdmin(
"security_alert_acknowledged",
AuditOutcome.SUCCESS,
context,
undefined,
{ alertId }
);
return NextResponse.json({ success: true });
}
return NextResponse.json({ error: "Invalid action" }, { status: 400 });
} catch (error) {
console.error("Security alert action error:", error);
if (error instanceof z.ZodError) {
return NextResponse.json(
{ error: "Invalid request", details: error.errors },
{ status: 400 }
);
}
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}