Add EpicNext CMS foundation layer and fix critical security gaps
Local Build and Deploy / deploy (push) Successful in 1m1s
Local Build and Deploy / deploy (push) Successful in 1m1s
- Create src/lib/foundation/ (860 LOC, 9 files): typed action wrappers, DbService with health checks, CSRF validation, safe redirects, AsyncLocalStorage request tracing, branded types, reusable Zod schemas - Migrate moderation.ts and user-settings.ts to foundation patterns - Fix abuse-guard.ts: bound in-memory Maps with LRU eviction (was unbounded) - Fix access-guard.ts: separate try/catch per check, log degradation instead of blanket fail-open - Replace raw redirect() calls with safeRedirect() in guard.ts and permissions.ts to prevent open-redirect attacks - Add CSRF validation to api-handler.ts for mutating methods - Add canonicalizeFormData() utility for FormData input sanitization
This commit is contained in:
1 parent
8bf1aa2fa7
commit
f6ad030c5b
16 files changed
+1012
-96
No files matched your search
+12
-1
@@ -3,6 +3,9 @@ import { NextResponse } from "next/server";
|
||||
import { logAuthorizationEvent } from "@/lib/admin/authorization-events";
|
||||
import { canAccess, getApiAdminContext } from "@/lib/permissions";
|
||||
import { logServerError } from "@/lib/server-log";
|
||||
import { validateCsrfToken } from "@/lib/foundation/security";
|
||||
|
||||
const MUTATING_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]);
|
||||
|
||||
type AdminContext = NonNullable<Awaited<ReturnType<typeof getApiAdminContext>>>;
|
||||
type RouteContext = { params?: Promise<Record<string, string | string[]>> };
|
||||
@@ -12,8 +15,16 @@ type AdminHandler = (
|
||||
routeContext: RouteContext,
|
||||
) => Promise<Response> | Response;
|
||||
|
||||
export function withAdmin(options: { permission?: string }, handler: AdminHandler) {
|
||||
export function withAdmin(options: { permission?: string; requireCsrf?: boolean }, handler: AdminHandler) {
|
||||
return async (request: NextRequest, routeContext: RouteContext = {}) => {
|
||||
if (options.requireCsrf === true && MUTATING_METHODS.has(request.method)) {
|
||||
const csrfToken = request.headers.get("x-csrf-token") ?? request.headers.get("csrf-token") ?? "";
|
||||
const valid = await validateCsrfToken(csrfToken);
|
||||
if (!valid) {
|
||||
return NextResponse.json({ ok: false, error: "Invalid or missing CSRF token" }, { status: 403 });
|
||||
}
|
||||
}
|
||||
|
||||
const context = await getApiAdminContext();
|
||||
if (!context) return NextResponse.json({ ok: false, error: "Unauthorized" }, { status: 401 });
|
||||
if (
|
||||
|
||||
Reference in new issue
Block a user