spb/datacenterindex
Public
HTML 53.9%
TypeScript 44.5%
JavaScript 0.6%
SQL 0.5%
1import { describe, expect, it } from "vitest";2import { assertUrlAllowed, isBlockedHostname, isBlockedIP, isBlockedIPv4, isBlockedIPv6, UrlPolicyError } from "./ssrf.js";34describe("blocked hostnames", () => {5 it("blocks loopback / metadata / internal suffixes / bare labels", () => {6 for (const h of ["localhost", "LOCALHOST.", "metadata.google.internal", "instance-data", "kubernetes.default.svc", "foo.local", "db.internal", "printer.home.arpa", "node.maclustr.io", "x.ts.net", "1.0.0.10.in-addr.arpa", "intranet"]) expect(isBlockedHostname(h), h).toBe(true);7 });8 it("allows public hosts", () => {9 for (const h of ["www.equinix.com", "example.org", "a.b.c.d.e.io", "8.8.8.8"]) expect(isBlockedHostname(h), h).toBe(false);10 });11});1213describe("blocked IPv4 ranges", () => {14 it("private, loopback, link-local, CGNAT, multicast, reserved, documentation", () => {15 for (const ip of ["10.0.0.1", "10.255.255.255", "172.16.0.1", "172.31.255.254", "192.168.1.1", "127.0.0.1", "127.255.0.1", "169.254.169.254", "100.64.0.1", "100.127.255.255", "0.0.0.0", "224.0.0.1", "239.255.255.255", "240.0.0.1", "255.255.255.255", "192.0.2.10", "198.51.100.5", "203.0.113.9", "198.18.0.1", "192.0.0.1"]) expect(isBlockedIPv4(ip), ip).toBe(true);16 });17 it("public addresses pass, including neighbours of blocked ranges", () => {18 for (const ip of ["8.8.8.8", "1.1.1.1", "172.32.0.1", "172.15.255.255", "100.128.0.1", "11.0.0.1", "192.169.0.1", "223.255.255.255", "51.161.112.61"]) expect(isBlockedIPv4(ip), ip).toBe(false);19 });20});2122describe("blocked IPv6", () => {23 it("loopback, unspecified, link-local, unique-local, multicast, documentation", () => {24 for (const ip of ["::1", "::", "fe80::1", "fe9f::1", "feb0::1", "fc00::1", "fd12:3456::1", "ff02::1", "2001:db8::1"]) expect(isBlockedIPv6(ip), ip).toBe(true);25 expect(isBlockedIPv6("2606:4700::6810:84e5")).toBe(false);26 expect(isBlockedIPv6("2001:4860:4860::8888")).toBe(false);27 });28 it("IPv4-mapped addresses follow the IPv4 policy (dotted and hex forms)", () => {29 expect(isBlockedIPv6("::ffff:127.0.0.1")).toBe(true);30 expect(isBlockedIPv6("::ffff:10.1.2.3")).toBe(true);31 expect(isBlockedIPv6("::ffff:8.8.8.8")).toBe(false);32 expect(isBlockedIPv6("::ffff:7f00:1")).toBe(true); // 127.0.0.133 expect(isBlockedIPv6("::ffff:a9fe:a9fe")).toBe(true); // 169.254.169.25434 expect(isBlockedIPv6("::ffff:808:808")).toBe(false); // 8.8.8.835 expect(isBlockedIPv6("0:0:0:0:0:ffff:c0a8:1")).toBe(true); // 192.168.0.136 });37 it("NAT64 well-known and local-use prefixes embed IPv4 and follow the IPv4 policy", () => {38 expect(isBlockedIPv6("64:ff9b::808:808")).toBe(false); // 8.8.8.8 via NAT64 stays reachable39 expect(isBlockedIPv6("64:ff9b::a00:1")).toBe(true); // 10.0.0.140 expect(isBlockedIPv6("64:ff9b::7f00:1")).toBe(true); // 127.0.0.141 expect(isBlockedIPv6("64:ff9b:1:0:0:0:a9fe:a9fe")).toBe(true); // local-use prefix, 169.254.169.25442 expect(isBlockedIPv6("64:ff9b:1:0:0:0:808:808")).toBe(false);43 expect(isBlockedIPv6("64:ff9b:dead::")).toBe(true); // malformed NAT64 form is blocked44 });45 it("isBlockedIP dispatches by family and rejects non-IPs", () => {46 expect(isBlockedIP("10.0.0.1")).toBe(true);47 expect(isBlockedIP("::1")).toBe(true);48 expect(isBlockedIP("not-an-ip")).toBe(true);49 expect(isBlockedIP("9.9.9.9")).toBe(false);50 });51});5253describe("assertUrlAllowed (no DNS)", () => {54 const opts = { resolve: false };55 it("rejects bad schemes, credentials, blocked hosts and literal blocked IPs", async () => {56 await expect(assertUrlAllowed("ftp://x.com/", opts)).rejects.toMatchObject({ reason: "scheme" });57 await expect(assertUrlAllowed("file:///etc/passwd", opts)).rejects.toBeInstanceOf(UrlPolicyError);58 await expect(assertUrlAllowed("https://user:pw@x.com/", opts)).rejects.toMatchObject({ reason: "credentials" });59 await expect(assertUrlAllowed("http://localhost/", opts)).rejects.toMatchObject({ reason: "blocked_host" });60 await expect(assertUrlAllowed("http://169.254.169.254/latest/meta-data", opts)).rejects.toMatchObject({ reason: "blocked_ip" });61 await expect(assertUrlAllowed("http://[::ffff:127.0.0.1]/", opts)).rejects.toMatchObject({ reason: "blocked_ip" });62 await expect(assertUrlAllowed("http://[64:ff9b::a00:1]/", opts)).rejects.toMatchObject({ reason: "blocked_ip" });63 await expect(assertUrlAllowed("not a url", opts)).rejects.toMatchObject({ reason: "malformed" });64 await expect(assertUrlAllowed("http://x.com/", { ...opts, allowHttp: false })).rejects.toMatchObject({ reason: "scheme" });65 });66 it("accepts public hosts and public literal IPs", async () => {67 await expect(assertUrlAllowed("https://www.example.com/a", opts)).resolves.toMatchObject({ hostname: "www.example.com" });68 await expect(assertUrlAllowed("http://8.8.8.8/", opts)).resolves.toMatchObject({ addresses: ["8.8.8.8"] });69 await expect(assertUrlAllowed("http://[2606:4700::6810:84e5]/", opts)).resolves.toMatchObject({ addresses: ["2606:4700::6810:84e5"] });70 });71});72