Files
light 6ec9778d45 chore: initial commit
Laimiao agent v2 service: agent orchestration, sessions, billing,
gateway client, skills catalog, auth, config, plus tests.
2026-11-07 10:40:07 +08:00

136 lines
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
assertSafeUrl,
hostMatches,
isBlockedAddress,
isBlockedHostname,
parseHostAllowlist,
safeFetch,
} from "../src/utils/net.js";
describe("parseHostAllowlist", () => {
it("normalizes comma separated hosts", () => {
expect(parseHostAllowlist(" CDN.Example.com , *.OSS.example.com.,, ")).toEqual([
"cdn.example.com",
"*.oss.example.com",
]);
});
it("returns an empty list for empty input", () => {
expect(parseHostAllowlist("")).toEqual([]);
expect(parseHostAllowlist(undefined)).toEqual([]);
});
});
describe("hostMatches", () => {
it("allows every host when the allowlist is empty", () => {
expect(hostMatches("anything.example.com", [])).toBe(true);
});
it("matches exact hosts case-insensitively", () => {
expect(hostMatches("CDN.example.com", ["cdn.example.com"])).toBe(true);
expect(hostMatches("other.example.com", ["cdn.example.com"])).toBe(false);
});
it("matches wildcard subdomains and the apex", () => {
expect(hostMatches("a.oss.example.com", ["*.oss.example.com"])).toBe(true);
expect(hostMatches("oss.example.com", ["*.oss.example.com"])).toBe(true);
expect(hostMatches("evil-oss.example.com", ["*.oss.example.com"])).toBe(false);
});
});
describe("isBlockedAddress", () => {
it.each([
"0.0.0.0",
"10.1.2.3",
"100.64.0.1",
"127.0.0.1",
"169.254.169.254",
"172.16.0.1",
"172.31.255.255",
"192.168.1.1",
"198.18.0.1",
"224.0.0.1",
"255.255.255.255",
])("blocks private or reserved IPv4 %s", (address) => {
expect(isBlockedAddress(address)).toBe(true);
});
it.each(["8.8.8.8", "1.1.1.1", "172.32.0.1", "93.184.216.34"])("allows public IPv4 %s", (address) => {
expect(isBlockedAddress(address)).toBe(false);
});
it.each(["::", "::1", "fe80::1", "fd00::1", "fc00::1", "ff02::1", "2001:db8::1"])(
"blocks private or reserved IPv6 %s",
(address) => {
expect(isBlockedAddress(address)).toBe(true);
},
);
it("blocks IPv4 mapped into IPv6", () => {
expect(isBlockedAddress("::ffff:127.0.0.1")).toBe(true);
expect(isBlockedAddress("::ffff:169.254.169.254")).toBe(true);
expect(isBlockedAddress("::ffff:8.8.8.8")).toBe(false);
});
it("blocks 6to4 addresses that embed a private IPv4", () => {
expect(isBlockedAddress("2002:7f00:1::")).toBe(true);
expect(isBlockedAddress("2002:0808:0808::")).toBe(false);
});
});
describe("isBlockedHostname", () => {
it.each([
"localhost",
"api.localhost",
"metadata.google.internal",
"db.internal",
"redis",
"127.0.0.1",
"169.254.169.254",
"[::1]",
])("blocks %s", (host) => {
expect(isBlockedHostname(host)).toBe(true);
});
it.each(["example.com", "cdn.example.com", "8.8.8.8"])("allows %s", (host) => {
expect(isBlockedHostname(host)).toBe(false);
});
});
describe("assertSafeUrl", () => {
it("rejects non-http protocols", async () => {
await expect(assertSafeUrl("ftp://example.com/a")).rejects.toThrow(/http/);
await expect(assertSafeUrl("file:///etc/passwd")).rejects.toThrow(/http/);
});
it("rejects URLs with credentials", async () => {
await expect(assertSafeUrl("https://user:pass@example.com/a")).rejects.toThrow(/账号|密码/);
});
it("rejects loopback and cloud metadata endpoints", async () => {
await expect(assertSafeUrl("http://127.0.0.1:3306")).rejects.toThrow(/禁止访问/);
await expect(assertSafeUrl("http://169.254.169.254/latest/meta-data")).rejects.toThrow(/禁止访问/);
await expect(assertSafeUrl("http://[::1]/")).rejects.toThrow(/禁止访问/);
});
it("rejects hosts outside the allowlist", async () => {
await expect(assertSafeUrl("https://evil.example.com/a", { allowedHosts: ["cdn.example.com"] })).rejects.toThrow(
/白名单/,
);
await expect(
assertSafeUrl("https://93.184.216.34/a", { allowedHosts: ["93.184.216.34"] }),
).resolves.toBeInstanceOf(URL);
});
it("accepts public IP literals", async () => {
await expect(assertSafeUrl("https://93.184.216.34/a")).resolves.toBeInstanceOf(URL);
});
});
describe("safeFetch", () => {
it("refuses to connect to a blocked host before any request is made", async () => {
await expect(safeFetch("http://127.0.0.1:9/")).rejects.toThrow(/禁止访问/);
});
});