diff --git a/lib/config.ts b/lib/config.ts index 3a76be4..42b771d 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -133,6 +133,8 @@ export function safeDomain(dn: unknown): string | null { .replace(/^https?:\/\//, "") .replace(/[/?#&].*$/, ""); if (!/^[a-z0-9.-]{3,253}$/.test(d) || !d.includes(".") || d.includes("..")) return null; + if (d.startsWith(".") || d.endsWith(".")) return null; + if (d.split(".").some((label) => label.startsWith("-") || label.endsWith("-"))) return null; return d; } diff --git a/tests/safe-domain.test.mjs b/tests/safe-domain.test.mjs new file mode 100644 index 0000000..11af543 --- /dev/null +++ b/tests/safe-domain.test.mjs @@ -0,0 +1,19 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { safeDomain } from "../lib/config.ts"; + +test("safeDomain strips forwarded paths and accepts ordinary domains", () => { + assert.equal(safeDomain("https://example.com/path?ref=abc"), "example.com"); + assert.equal(safeDomain("Sub.Example.COM#top"), "sub.example.com"); +}); + +test("safeDomain rejects empty labels and edge punctuation", () => { + assert.equal(safeDomain(".example.com"), null); + assert.equal(safeDomain("example.com."), null); + assert.equal(safeDomain("example..com"), null); + assert.equal(safeDomain("-example.com"), null); + assert.equal(safeDomain("example-.com"), null); + assert.equal(safeDomain("example.-com"), null); + assert.equal(safeDomain("example.com-"), null); +});