You bypassed the Cloudflare challenge!
${"content ".repeat(200)}
import { describe, expect, it } from "vitest"; import { assertUrlAllowed, isBlockedIP, isBlockedHostname, FetchaError, fetchRequestSchema, generateApiKey, hashApiKey, looksBlocked, htmlToText, normalizeGeo } from "../src/index"; describe("SSRF protection", () => { it("blocks private and reserved IPv4 ranges", () => { for (const ip of ["127.0.0.1", "10.1.2.3", "172.16.0.1", "172.31.255.255", "192.168.1.1", "169.254.169.254", "0.0.0.0", "100.64.0.1", "224.0.0.1", "255.255.255.255"]) { expect(isBlockedIP(ip), ip).toBe(true); } for (const ip of ["8.8.8.8", "1.1.1.1", "93.184.216.34", "172.32.0.1", "11.0.0.1"]) { expect(isBlockedIP(ip), ip).toBe(false); } }); it("blocks IPv6 loopback, link-local, ULA and v4-mapped private", () => { for (const ip of ["::1", "::", "fe80::1", "fd00::1", "fc00::1", "::ffff:127.0.0.1", "::ffff:10.0.0.1", "ff02::1"]) { expect(isBlockedIP(ip), ip).toBe(true); } expect(isBlockedIP("2606:4700:4700::1111")).toBe(false); }); it("blocks internal hostnames", () => { for (const h of ["localhost", "LOCALHOST.", "foo.local", "metadata.google.internal", "api.internal", "intranet", "m3u96a.maclustr.io"]) { expect(isBlockedHostname(h), h).toBe(true); } expect(isBlockedHostname("example.com")).toBe(false); }); it("rejects non-http schemes and credentials", async () => { for (const u of ["file:///etc/passwd", "ftp://example.com/x", "gopher://x", "data:text/html,hi", "javascript:alert(1)"]) { await expect(assertUrlAllowed(u, { resolve: false })).rejects.toMatchObject({ code: "URL_NOT_ALLOWED" }); } await expect(assertUrlAllowed("https://user:pw@example.com/", { resolve: false })).rejects.toMatchObject({ code: "URL_NOT_ALLOWED" }); await expect(assertUrlAllowed("not a url", { resolve: false })).rejects.toMatchObject({ code: "INVALID_REQUEST" }); }); it("rejects literal private IPs and numeric encodings", async () => { await expect(assertUrlAllowed("http://169.254.169.254/latest/meta-data")).rejects.toBeInstanceOf(FetchaError); await expect(assertUrlAllowed("http://[::1]:8080/")).rejects.toMatchObject({ code: "URL_NOT_ALLOWED" }); await expect(assertUrlAllowed("http://2130706433/")).rejects.toMatchObject({ code: "URL_NOT_ALLOWED" }); await expect(assertUrlAllowed("http://0x7f000001/")).rejects.toMatchObject({ code: "URL_NOT_ALLOWED" }); }); it("allows public URLs without resolving when asked", async () => { const r = await assertUrlAllowed("https://example.com/path?q=1", { resolve: false }); expect(r.hostname).toBe("example.com"); }); }); describe("request schema", () => { it("applies defaults and uppercases country", () => { const r = fetchRequestSchema.parse({ url: "https://example.com", country: "ca" }); expect(r.method).toBe("GET"); expect(r.network).toBe("auto"); expect(r.timeout).toBe(30_000); expect(r.country).toBe("CA"); expect(r.follow_redirects).toBe(true); }); it("rejects unknown fields and bad values", () => { expect(fetchRequestSchema.safeParse({ url: "https://e.com", foo: 1 }).success).toBe(false); expect(fetchRequestSchema.safeParse({ url: "https://e.com", timeout: 10 }).success).toBe(false); expect(fetchRequestSchema.safeParse({ url: "https://e.com", network: "vpn" }).success).toBe(false); }); }); describe("api keys", () => { it("generates prefixed keys and stable hashes", () => { const k = generateApiKey("live"); expect(k.plaintext.startsWith("fch_live_")).toBe(true); expect(k.prefix).toBe(k.plaintext.slice(0, 13)); expect(hashApiKey(k.plaintext)).toBe(k.hash); expect(generateApiKey("test").plaintext.startsWith("fch_test_")).toBe(true); }); }); describe("block detection & text", () => { it("detects challenge pages and captchas", () => { expect(looksBlocked(403, "", {}).blocked).toBe(true); expect(looksBlocked(503, "
${"content ".repeat(200)}
There & back
")).toBe("Hi\nThere & back"); }); it("normalizes geography", () => { expect(normalizeGeo({ country: "ca", region: "QC", city: "Québec" })).toEqual({ country: "CA", region: "quebec", city: "quebec" }); expect(normalizeGeo({ country: "US", region: "ny" }).region).toBe("new_york"); }); });