71 lines
2.8 KiB
TypeScript
71 lines
2.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import * as harness from "../src/index.js";
|
|
|
|
type Resolver = (hostname: string) => Promise<Array<{ address: string; family: 4 | 6 }>>;
|
|
type Validate = (
|
|
url: string,
|
|
options?: { allowLocalhostHttp?: boolean; allowPrivateNetwork?: boolean },
|
|
resolver?: Resolver,
|
|
) => Promise<{ normalizedUrl: string; addresses: string[]; redirectPolicy: "same-origin" }>;
|
|
|
|
function validate(): Validate {
|
|
const value = Reflect.get(harness, "validateBaseUrl") as Validate | undefined;
|
|
expect(value, "validateBaseUrl must be exported").toBeDefined();
|
|
return value!;
|
|
}
|
|
|
|
const publicResolver: Resolver = () => Promise.resolve([{ address: "93.184.216.34", family: 4 }]);
|
|
|
|
describe("validateBaseUrl", () => {
|
|
it("normalizes an HTTPS URL and pins all validated addresses", async () => {
|
|
const result = await validate()("https://api.example.com/v1", {}, () => Promise.resolve([
|
|
{ address: "2606:2800:220:1:248:1893:25c8:1946", family: 6 },
|
|
{ address: "93.184.216.34", family: 4 },
|
|
]));
|
|
|
|
expect(result).toEqual({
|
|
normalizedUrl: "https://api.example.com/v1",
|
|
addresses: ["2606:2800:220:1:248:1893:25c8:1946", "93.184.216.34"],
|
|
redirectPolicy: "same-origin",
|
|
});
|
|
});
|
|
|
|
it.each([
|
|
"http://169.254.169.254/latest/meta-data",
|
|
"https://127.0.0.1/v1",
|
|
"file:///etc/passwd",
|
|
"https://user:pass@example.com/v1",
|
|
"https://example.com/v1#secret",
|
|
"https://example.com/v1?api_key=secret",
|
|
])("rejects restricted base URL %s", async (url) => {
|
|
await expect(validate()(url, {}, publicResolver)).rejects.toMatchObject({
|
|
code: "BASE_URL_NOT_ALLOWED",
|
|
});
|
|
});
|
|
|
|
it.each(["10.0.0.1", "100.64.0.1", "169.254.10.2", "172.16.0.1", "192.168.1.1", "::1", "::ffff:127.0.0.1", "fc00::1", "fe80::1"])(
|
|
"rejects a restricted resolved address %s",
|
|
async (address) => {
|
|
const family = address.includes(":") ? 6 : 4;
|
|
await expect(
|
|
validate()("https://api.example.com", {}, () => Promise.resolve([{ address, family }])),
|
|
).rejects.toMatchObject({ code: "BASE_URL_NOT_ALLOWED" });
|
|
},
|
|
);
|
|
|
|
it("permits explicit localhost HTTP only in development mode", async () => {
|
|
const result = await validate()("http://localhost:11434/v1", { allowLocalhostHttp: true, allowPrivateNetwork: true }, () => Promise.resolve([
|
|
{ address: "127.0.0.1", family: 4 },
|
|
]));
|
|
expect(result.normalizedUrl).toBe("http://localhost:11434/v1");
|
|
});
|
|
|
|
it("never permits the metadata address even when private networks are enabled", async () => {
|
|
await expect(
|
|
validate()("https://metadata.internal", { allowPrivateNetwork: true }, () => Promise.resolve([
|
|
{ address: "169.254.169.254", family: 4 },
|
|
])),
|
|
).rejects.toMatchObject({ code: "BASE_URL_NOT_ALLOWED" });
|
|
});
|
|
});
|