Seguridad y docs: fix SQLi en updatePost, middleware por segmentos exactos, cover-cache directo al upstream, JWT sin fallback hardcodeado, password sudo fuera de deploy/setup.sh; reescritura AGENTS.md y CLAUDE.md

This commit is contained in:
Renato
2026-08-04 11:13:57 +08:00
parent 5d36c635cb
commit 451518004d
12 changed files with 419 additions and 928 deletions
+30 -13
View File
@@ -1,30 +1,47 @@
import { NextRequest, NextResponse } from "next/server"
import { verifySession } from "@/lib/auth"
const publicPaths = [
// Rutas públicas EXACTAS (coincidencia por segmento, no por prefijo).
const publicExact = new Set([
"/login",
"/api/auth/login",
"/api/auth/logout",
"/api/health",
"/api/proxy/health",
"/api/posts",
"/api/cover",
"/api/cron",
"/api/setup",
"/setup",
"/_next",
"/favicon.ico",
"/fonts",
])
// Prefijos públicos: /api/cover (imágenes de portada para el sitio público),
// assets de Next y fuentes. Coinciden a nivel de segmento: "/api/cover" NO
// destapa "/api/cover/evil" ni otras rutas.
const publicPrefixes = [
"/api/cover/",
"/_next/",
"/fonts/",
]
// Páginas públicas del sitio (server-side, sin login).
const publicPages = ["/", "/p/", "/tag/"]
function isPublicPath(pathname: string, method: string): boolean {
if (publicExact.has(pathname)) return true
if (publicPrefixes.some((p) => pathname.startsWith(p))) return true
if (publicPages.some((p) => pathname.startsWith(p))) return true
// /api/posts: GET de listado público; mutaciones requieren sesión.
if (pathname === "/api/posts" && method === "GET") return true
// /api/setup: GET (chequeo de config) público; POST (cambiar secrets) NO.
if (pathname === "/api/setup" && method === "GET") return true
return false
}
export async function middleware(req: NextRequest) {
const { pathname } = req.nextUrl
const method = req.method
const isPublic = publicPaths.some((p) => pathname.startsWith(p))
if (isPublic) return NextResponse.next()
const isPublicPage = pathname === "/" || pathname.startsWith("/p/") || pathname.startsWith("/tag/")
if (isPublicPage) return NextResponse.next()
if (isPublicPath(pathname, method)) return NextResponse.next()
const webPassword = process.env.WEB_PASSWORD
if (!webPassword) return NextResponse.next()