fix(security): drop URLhaus feed, validate CIDR ranges, pass unknown client IPs
Gitea Actions Runner Test / test-job (push) Successful in 0s
CI / check (push) Successful in 31s
CI / tests-integration (push) Successful in 1m42s
CI / tests-unit (push) Successful in 1m46s
CI / tests-ui (push) Successful in 2m35s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 1m40s

This commit is contained in:
openhands committed 2026-09-24 19:19:22 +02:00
1 parent 7392b843ad
commit 7f6febf906
4 files changed
+78 -12

No files matched your search

+9
View File
@@ -248,6 +248,15 @@ describe("anti-DDoS automatic CrowdSec blocks", () => {
expect(fetchMock).not.toHaveBeenCalled();
});
it("passes the unknown-IP sentinel through even when a stale block key exists", async () => {
state.map.set("antiddos:block:0.0.0.0", "1");
const decision = await enforceDdosRateLimit(directRequest("0.0.0.0"));
expect(decision.outcome).toBe("pass");
expect(fetchMock).not.toHaveBeenCalled();
});
it("blocks immediately on a local LAPI ban decision (app-layer bouncer)", async () => {
vi.stubEnv("CROWDSEC_LOCAL_ENABLED", "true");
vi.stubEnv("CROWDSEC_LAPI_URL", "http://127.0.0.1:18080");
+7 -1
View File
@@ -4,7 +4,7 @@ import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { env } from "@/env";
import { getAntiddosConfig } from "@/lib/antiddos-config";
import { resolveClientIp } from "@/lib/client-ip";
import { resolveClientIp, UNKNOWN_CLIENT_IP } from "@/lib/client-ip";
import { isCloudflareProxied } from "@/lib/cloudflare";
import { maybeAutoBlockCloudflare } from "@/lib/cloudflare-api";
import { maybeAutoBlockCrowdsec } from "@/lib/crowdsec-api";
@@ -71,6 +71,12 @@ export async function enforceDdosRateLimit(
}
const ip = resolveClientIp(req.headers);
// A trusted ingress always resolves a real client address. `0.0.0.0` is the
// sentinel for header-less loopback traffic (health checks, CI browser
// gates, monitoring). If it were rate-limited or blocked it would occupy a
// single shared key, and any burst of synthetic local traffic could then
// shed all origin-verified requests — exactly what broke CI smoke tests.
if (ip === UNKNOWN_CLIENT_IP) return { outcome: "pass" };
const blockKey = `antiddos:block:${ip}`;
if (redis) {
try {