mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-20 05:42:19 +03:00
fix(security): sanitize agent-card topology, anti-spoof login rate-limit peer IP, and add 429 Retry-After (#S1 #S2 #S4) (#11418)
Validado em lote combinado (batch-0824g) contra o tip de release/v3.8.50: typecheck:core limpo, gates estáticos OK, 62/62 testes focados passando (S1/S2/S4, tests/unit/security-s1-s2-s4.test.ts, 9/9). Boa integração com o padrão já existente de peer IP stamped por HMAC (resolveStampedPeer/OMNIROUTE_PEER_STAMP_TOKEN) — reusa em vez de reimplementar, e o header confiável só é honrado quando o stamp token está configurado. S2 remove corretamente a disclosure de topologia hardcoded do agent-card. Obrigado pela contribuição!
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import type { NextRequest } from "next/server";
|
||||
import { getAuditRequestContext, logAuditEvent } from "@/lib/compliance/index";
|
||||
import { classifyIpScope } from "@/lib/ipUtils";
|
||||
import { getCachedSettings } from "@/lib/db/settings";
|
||||
@@ -13,6 +14,7 @@ import { isFeatureFlagEnabled } from "@/shared/utils/featureFlags";
|
||||
import { loginSchema } from "@/shared/validation/schemas";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
import { checkLoginGuard, clearLoginAttempts, recordLoginFailure } from "@/server/auth/loginGuard";
|
||||
import { AUTHZ_HEADER_TRUSTED_PEER_IP } from "@/server/authz/headers";
|
||||
|
||||
// SECURITY: No hardcoded fallback — JWT_SECRET must be configured.
|
||||
if (!process.env.JWT_SECRET) {
|
||||
@@ -28,7 +30,7 @@ export const authRouteInternals = {
|
||||
getCookieStore: cookies,
|
||||
};
|
||||
|
||||
export async function POST(request) {
|
||||
export async function POST(request: NextRequest) {
|
||||
const auditContext = getAuditRequestContext(request);
|
||||
|
||||
try {
|
||||
@@ -75,7 +77,10 @@ export async function POST(request) {
|
||||
return NextResponse.json({ error: "Invalid password payload" }, { status: 400 });
|
||||
}
|
||||
const settings = await getCachedSettings();
|
||||
const clientIp = auditContext.ipAddress || null;
|
||||
const trustedPeerIp = process.env.OMNIROUTE_PEER_STAMP_TOKEN
|
||||
? request.headers.get(AUTHZ_HEADER_TRUSTED_PEER_IP)
|
||||
: null;
|
||||
const clientIp = trustedPeerIp || auditContext.ipAddress || null;
|
||||
const oidcDisabledPassword =
|
||||
settings.oidcEnabled === true &&
|
||||
(settings.oidcDisablePasswordLogin === true ||
|
||||
@@ -118,9 +123,7 @@ export async function POST(request) {
|
||||
{ error: "Too many failed attempts. Try again later." },
|
||||
{
|
||||
status: 429,
|
||||
headers: guardCheck.retryAfterSeconds
|
||||
? { "Retry-After": String(guardCheck.retryAfterSeconds) }
|
||||
: {},
|
||||
headers: { "Retry-After": String(guardCheck.retryAfterSeconds || 60) },
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -220,9 +223,7 @@ export async function POST(request) {
|
||||
{ error: "Too many failed attempts. Try again later." },
|
||||
{
|
||||
status: 429,
|
||||
headers: failureDecision.retryAfterSeconds
|
||||
? { "Retry-After": String(failureDecision.retryAfterSeconds) }
|
||||
: {},
|
||||
headers: { "Retry-After": String(failureDecision.retryAfterSeconds || 60) },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user