import { describe, expect, it } from "vitest"; import { assertSafeUrl, hostMatches, isBlockedAddress, isBlockedHostname, parseHostAllowlist, safeFetch, } from "../src/utils/net.js"; describe("parseHostAllowlist", () => { it("normalizes comma separated hosts", () => { expect(parseHostAllowlist(" CDN.Example.com , *.OSS.example.com.,, ")).toEqual([ "cdn.example.com", "*.oss.example.com", ]); }); it("returns an empty list for empty input", () => { expect(parseHostAllowlist("")).toEqual([]); expect(parseHostAllowlist(undefined)).toEqual([]); }); }); describe("hostMatches", () => { it("allows every host when the allowlist is empty", () => { expect(hostMatches("anything.example.com", [])).toBe(true); }); it("matches exact hosts case-insensitively", () => { expect(hostMatches("CDN.example.com", ["cdn.example.com"])).toBe(true); expect(hostMatches("other.example.com", ["cdn.example.com"])).toBe(false); }); it("matches wildcard subdomains and the apex", () => { expect(hostMatches("a.oss.example.com", ["*.oss.example.com"])).toBe(true); expect(hostMatches("oss.example.com", ["*.oss.example.com"])).toBe(true); expect(hostMatches("evil-oss.example.com", ["*.oss.example.com"])).toBe(false); }); }); describe("isBlockedAddress", () => { it.each([ "0.0.0.0", "10.1.2.3", "100.64.0.1", "127.0.0.1", "169.254.169.254", "172.16.0.1", "172.31.255.255", "192.168.1.1", "198.18.0.1", "224.0.0.1", "255.255.255.255", ])("blocks private or reserved IPv4 %s", (address) => { expect(isBlockedAddress(address)).toBe(true); }); it.each(["8.8.8.8", "1.1.1.1", "172.32.0.1", "93.184.216.34"])("allows public IPv4 %s", (address) => { expect(isBlockedAddress(address)).toBe(false); }); it.each(["::", "::1", "fe80::1", "fd00::1", "fc00::1", "ff02::1", "2001:db8::1"])( "blocks private or reserved IPv6 %s", (address) => { expect(isBlockedAddress(address)).toBe(true); }, ); it("blocks IPv4 mapped into IPv6", () => { expect(isBlockedAddress("::ffff:127.0.0.1")).toBe(true); expect(isBlockedAddress("::ffff:169.254.169.254")).toBe(true); expect(isBlockedAddress("::ffff:8.8.8.8")).toBe(false); }); it("blocks 6to4 addresses that embed a private IPv4", () => { expect(isBlockedAddress("2002:7f00:1::")).toBe(true); expect(isBlockedAddress("2002:0808:0808::")).toBe(false); }); }); describe("isBlockedHostname", () => { it.each([ "localhost", "api.localhost", "metadata.google.internal", "db.internal", "redis", "127.0.0.1", "169.254.169.254", "[::1]", ])("blocks %s", (host) => { expect(isBlockedHostname(host)).toBe(true); }); it.each(["example.com", "cdn.example.com", "8.8.8.8"])("allows %s", (host) => { expect(isBlockedHostname(host)).toBe(false); }); }); describe("assertSafeUrl", () => { it("rejects non-http protocols", async () => { await expect(assertSafeUrl("ftp://example.com/a")).rejects.toThrow(/http/); await expect(assertSafeUrl("file:///etc/passwd")).rejects.toThrow(/http/); }); it("rejects URLs with credentials", async () => { await expect(assertSafeUrl("https://user:pass@example.com/a")).rejects.toThrow(/账号|密码/); }); it("rejects loopback and cloud metadata endpoints", async () => { await expect(assertSafeUrl("http://127.0.0.1:3306")).rejects.toThrow(/禁止访问/); await expect(assertSafeUrl("http://169.254.169.254/latest/meta-data")).rejects.toThrow(/禁止访问/); await expect(assertSafeUrl("http://[::1]/")).rejects.toThrow(/禁止访问/); }); it("rejects hosts outside the allowlist", async () => { await expect(assertSafeUrl("https://evil.example.com/a", { allowedHosts: ["cdn.example.com"] })).rejects.toThrow( /白名单/, ); await expect( assertSafeUrl("https://93.184.216.34/a", { allowedHosts: ["93.184.216.34"] }), ).resolves.toBeInstanceOf(URL); }); it("accepts public IP literals", async () => { await expect(assertSafeUrl("https://93.184.216.34/a")).resolves.toBeInstanceOf(URL); }); }); describe("safeFetch", () => { it("refuses to connect to a blocked host before any request is made", async () => { await expect(safeFetch("http://127.0.0.1:9/")).rejects.toThrow(/禁止访问/); }); });