refactor(infra): dedupe ssrf fetch guard test fixtures

This commit is contained in:
Peter Steinberger
2026-03-03 02:45:17 +00:00
parent 8b4cdbb21d
commit a50dd0bb06

View File

@@ -15,6 +15,43 @@ function okResponse(body = "ok"): Response {
describe("fetchWithSsrFGuard hardening", () => {
type LookupFn = NonNullable<Parameters<typeof fetchWithSsrFGuard>[0]["lookupFn"]>;
const createPublicLookup = (): LookupFn =>
vi.fn(async () => [{ address: "93.184.216.34", family: 4 }]) as unknown as LookupFn;
const getSecondRequestHeaders = (fetchImpl: ReturnType<typeof vi.fn>): Headers => {
const [, secondInit] = fetchImpl.mock.calls[1] as [string, RequestInit];
return new Headers(secondInit.headers);
};
async function runProxyModeDispatcherTest(params: {
mode: (typeof GUARDED_FETCH_MODE)[keyof typeof GUARDED_FETCH_MODE];
expectEnvProxy: boolean;
}): Promise<void> {
vi.stubEnv("HTTP_PROXY", "http://127.0.0.1:7890");
const lookupFn = createPublicLookup();
const fetchImpl = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
const requestInit = init as RequestInit & { dispatcher?: unknown };
if (params.expectEnvProxy) {
expect(requestInit.dispatcher).toBeInstanceOf(EnvHttpProxyAgent);
} else {
expect(requestInit.dispatcher).toBeDefined();
expect(requestInit.dispatcher).not.toBeInstanceOf(EnvHttpProxyAgent);
}
return okResponse();
});
const result = await fetchWithSsrFGuard({
url: "https://public.example/resource",
fetchImpl,
lookupFn,
mode: params.mode,
});
expect(fetchImpl).toHaveBeenCalledTimes(1);
await result.release();
}
afterEach(() => {
vi.unstubAllEnvs();
});
@@ -60,9 +97,7 @@ describe("fetchWithSsrFGuard hardening", () => {
});
it("blocks redirect chains that hop to private hosts", async () => {
const lookupFn = vi.fn(async () => [
{ address: "93.184.216.34", family: 4 },
]) as unknown as LookupFn;
const lookupFn = createPublicLookup();
const fetchImpl = vi.fn().mockResolvedValueOnce(redirectResponse("http://127.0.0.1:6379/"));
await expect(
@@ -88,9 +123,7 @@ describe("fetchWithSsrFGuard hardening", () => {
});
it("allows wildcard allowlisted hosts", async () => {
const lookupFn = vi.fn(async () => [
{ address: "93.184.216.34", family: 4 },
]) as unknown as LookupFn;
const lookupFn = createPublicLookup();
const fetchImpl = vi.fn(async () => new Response("ok", { status: 200 }));
const result = await fetchWithSsrFGuard({
url: "https://img.assets.example.com/pic.png",
@@ -105,9 +138,7 @@ describe("fetchWithSsrFGuard hardening", () => {
});
it("strips sensitive headers when redirect crosses origins", async () => {
const lookupFn = vi.fn(async () => [
{ address: "93.184.216.34", family: 4 },
]) as unknown as LookupFn;
const lookupFn = createPublicLookup();
const fetchImpl = vi
.fn()
.mockResolvedValueOnce(redirectResponse("https://cdn.example.com/asset"))
@@ -128,8 +159,7 @@ describe("fetchWithSsrFGuard hardening", () => {
},
});
const [, secondInit] = fetchImpl.mock.calls[1] as [string, RequestInit];
const headers = new Headers(secondInit.headers);
const headers = getSecondRequestHeaders(fetchImpl);
expect(headers.get("authorization")).toBeNull();
expect(headers.get("proxy-authorization")).toBeNull();
expect(headers.get("cookie")).toBeNull();
@@ -139,9 +169,7 @@ describe("fetchWithSsrFGuard hardening", () => {
});
it("keeps headers when redirect stays on same origin", async () => {
const lookupFn = vi.fn(async () => [
{ address: "93.184.216.34", family: 4 },
]) as unknown as LookupFn;
const lookupFn = createPublicLookup();
const fetchImpl = vi
.fn()
.mockResolvedValueOnce(redirectResponse("/next"))
@@ -158,54 +186,22 @@ describe("fetchWithSsrFGuard hardening", () => {
},
});
const [, secondInit] = fetchImpl.mock.calls[1] as [string, RequestInit];
const headers = new Headers(secondInit.headers);
const headers = getSecondRequestHeaders(fetchImpl);
expect(headers.get("authorization")).toBe("Bearer secret");
await result.release();
});
it("ignores env proxy by default to preserve DNS-pinned destination binding", async () => {
vi.stubEnv("HTTP_PROXY", "http://127.0.0.1:7890");
const lookupFn = vi.fn(async () => [
{ address: "93.184.216.34", family: 4 },
]) as unknown as LookupFn;
const fetchImpl = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
const requestInit = init as RequestInit & { dispatcher?: unknown };
expect(requestInit.dispatcher).toBeDefined();
expect(requestInit.dispatcher).not.toBeInstanceOf(EnvHttpProxyAgent);
return okResponse();
});
const result = await fetchWithSsrFGuard({
url: "https://public.example/resource",
fetchImpl,
lookupFn,
await runProxyModeDispatcherTest({
mode: GUARDED_FETCH_MODE.STRICT,
expectEnvProxy: false,
});
expect(fetchImpl).toHaveBeenCalledTimes(1);
await result.release();
});
it("uses env proxy only when dangerous proxy bypass is explicitly enabled", async () => {
vi.stubEnv("HTTP_PROXY", "http://127.0.0.1:7890");
const lookupFn = vi.fn(async () => [
{ address: "93.184.216.34", family: 4 },
]) as unknown as LookupFn;
const fetchImpl = vi.fn(async (_input: RequestInfo | URL, init?: RequestInit) => {
const requestInit = init as RequestInit & { dispatcher?: unknown };
expect(requestInit.dispatcher).toBeInstanceOf(EnvHttpProxyAgent);
return okResponse();
});
const result = await fetchWithSsrFGuard({
url: "https://public.example/resource",
fetchImpl,
lookupFn,
await runProxyModeDispatcherTest({
mode: GUARDED_FETCH_MODE.TRUSTED_ENV_PROXY,
expectEnvProxy: true,
});
expect(fetchImpl).toHaveBeenCalledTimes(1);
await result.release();
});
});