From fd332d604e3021d26d236ba77f9a93c904d30fe4 Mon Sep 17 00:00:00 2001 From: Alan Provable Date: Mon, 29 Jun 2026 01:17:43 -0700 Subject: [PATCH] test(lib): cover safeHref validation --- jest.config.ts | 6 +++ src/app/docs/page.tsx | 2 + src/lib/__tests__/url.test.ts | 84 ++++++++++++++--------------------- 3 files changed, 41 insertions(+), 51 deletions(-) diff --git a/jest.config.ts b/jest.config.ts index 9b81c53..955c164 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -53,6 +53,12 @@ const config: Config = { functions: 100, lines: 100, }, + "./src/lib/url.ts": { + statements: 100, + branches: 100, + functions: 100, + lines: 100, + }, }, coverageReporters: ["text", "json-summary", "lcov"], }; diff --git a/src/app/docs/page.tsx b/src/app/docs/page.tsx index 6769a87..ab17d1d 100644 --- a/src/app/docs/page.tsx +++ b/src/app/docs/page.tsx @@ -1,5 +1,7 @@ import { PageShell } from "@/components/PageShell"; +import { CurlBlock } from "@/components/CurlBlock"; import { messages } from "@/lib/messages"; +import { resolveApiBase } from "@/lib/resolveApiBase"; export const metadata = { title: "Docs — AgentPay" }; diff --git a/src/lib/__tests__/url.test.ts b/src/lib/__tests__/url.test.ts index f7d73b4..e13fb49 100644 --- a/src/lib/__tests__/url.test.ts +++ b/src/lib/__tests__/url.test.ts @@ -1,56 +1,38 @@ import { safeHref } from "../url"; describe("safeHref", () => { - it("rejects null/undefined/empty", () => { - expect(safeHref(undefined)).toEqual({ ok: false }); - expect(safeHref(null)).toEqual({ ok: false }); - expect(safeHref("")).toEqual({ ok: false }); - expect(safeHref(" ")).toEqual({ ok: false }); - }); - - it("allows in-page hash links", () => { - expect(safeHref("#section")).toEqual({ ok: true, href: "#section" }); - expect(safeHref(" #s2 ")).toEqual({ ok: true, href: "#s2" }); - }); - - it("allows internal relative links starting with slash", () => { - expect(safeHref("/settings")).toEqual({ ok: true, href: "/settings" }); - expect(safeHref(" /a/b?c=1 ")).toEqual({ ok: true, href: "/a/b?c=1" }); - }); - - it("allows absolute http(s) URLs", () => { - expect(safeHref("https://example.com")).toEqual({ ok: true, href: "https://example.com" }); - expect(safeHref("http://example.com/path?a=b")).toEqual({ ok: true, href: "http://example.com/path?a=b" }); - }); - - it("rejects javascript: scheme", () => { - expect(safeHref("javascript:alert(1)" as string)).toEqual({ ok: false }); - expect(safeHref(" javascript:alert(1) ")).toEqual({ ok: false }); - // Obfuscated/extra whitespace inside scheme should still be rejected. - expect(safeHref("java script:alert(1)" as string)).toEqual({ ok: false }); - }); - - it("rejects data: scheme", () => { - expect(safeHref("data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg==" as string)).toEqual({ ok: false }); - expect(safeHref(" data:text/plain,hi " as string)).toEqual({ ok: false }); - }); - - it("rejects non-http(s) schemes", () => { - expect(safeHref("mailto:test@example.com" as string)).toEqual({ ok: false }); - expect(safeHref("tel:+123456" as string)).toEqual({ ok: false }); - expect(safeHref("ftp://example.com" as string)).toEqual({ ok: false }); - expect(safeHref("chrome://extensions" as string)).toEqual({ ok: false }); - }); - - it("rejects malformed hrefs without a valid scheme", () => { - // Contains ':' but no scheme at the beginning. - expect(safeHref("//example.com" as string)).toEqual({ ok: false }); - expect(safeHref("example.com:80" as string)).toEqual({ ok: false }); - - // A protocol-relative URL ("//example.com") should be rejected since the - // scheme is not explicitly http(s). - expect(safeHref("//example.com" as string)).toEqual({ ok: false }); - expect(safeHref("//example.com" as string)).toEqual({ ok: false }); + it.each([ + ["https://example.com", "https://example.com"], + ["http://example.com/path?a=b", "http://example.com/path?a=b"], + ["HTTPS://example.com/docs", "HTTPS://example.com/docs"], + ["/settings", "/settings"], + [" /a/b?c=1 ", "/a/b?c=1"], + ["#section", "#section"], + [" #s2 ", "#s2"], + ])("allows safe navigation href %p", (input, expectedHref) => { + expect(safeHref(input)).toEqual({ ok: true, href: expectedHref }); + }); + + it.each([ + undefined, + null, + "", + " ", + "javascript:alert(1)", + " javascript:alert(1) ", + "data:text/html,", + " data:text/plain,hi ", + "//evil.com", + "mailto:test@example.com", + "tel:+123456", + "ftp://example.com", + "chrome://extensions", + "example.com", + "relative/path", + "http//missing-colon.example", + ":https://example.com", + "java script:alert(1)", + ])("rejects unsafe or malformed href %p", (input) => { + expect(safeHref(input)).toEqual({ ok: false }); }); }); -