openclaw/src/browser/navigation-guard.test.ts

105 lines
3.0 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { SsrFBlockedError, type LookupFn } from "../infra/net/ssrf.js";
import {
assertBrowserNavigationAllowed,
InvalidBrowserNavigationUrlError,
} from "./navigation-guard.js";
function createLookupFn(address: string): LookupFn {
const family = address.includes(":") ? 6 : 4;
return vi.fn(async () => [{ address, family }]) as unknown as LookupFn;
}
describe("browser navigation guard", () => {
it("blocks private loopback URLs by default", async () => {
await expect(
assertBrowserNavigationAllowed({
url: "http://127.0.0.1:8080",
}),
).rejects.toBeInstanceOf(SsrFBlockedError);
});
it("allows about:blank", async () => {
await expect(
assertBrowserNavigationAllowed({
url: "about:blank",
}),
).resolves.toBeUndefined();
});
it("blocks file URLs", async () => {
await expect(
assertBrowserNavigationAllowed({
url: "file:///etc/passwd",
}),
).rejects.toBeInstanceOf(InvalidBrowserNavigationUrlError);
});
it("blocks data URLs", async () => {
await expect(
assertBrowserNavigationAllowed({
url: "data:text/html,<h1>owned</h1>",
}),
).rejects.toBeInstanceOf(InvalidBrowserNavigationUrlError);
});
it("blocks javascript URLs", async () => {
await expect(
assertBrowserNavigationAllowed({
url: "javascript:alert(1)",
}),
).rejects.toBeInstanceOf(InvalidBrowserNavigationUrlError);
});
it("blocks non-blank about URLs", async () => {
await expect(
assertBrowserNavigationAllowed({
url: "about:srcdoc",
}),
).rejects.toBeInstanceOf(InvalidBrowserNavigationUrlError);
});
it("allows blocked hostnames when explicitly allowed", async () => {
const lookupFn = createLookupFn("127.0.0.1");
await expect(
assertBrowserNavigationAllowed({
url: "http://agent.internal:3000",
ssrfPolicy: {
allowedHostnames: ["agent.internal"],
},
lookupFn,
}),
).resolves.toBeUndefined();
expect(lookupFn).toHaveBeenCalledWith("agent.internal", { all: true });
});
it("blocks hostnames that resolve to private addresses by default", async () => {
const lookupFn = createLookupFn("127.0.0.1");
await expect(
assertBrowserNavigationAllowed({
url: "https://example.com",
lookupFn,
}),
).rejects.toBeInstanceOf(SsrFBlockedError);
});
it("allows hostnames that resolve to public addresses", async () => {
const lookupFn = createLookupFn("93.184.216.34");
await expect(
assertBrowserNavigationAllowed({
url: "https://example.com",
lookupFn,
}),
).resolves.toBeUndefined();
expect(lookupFn).toHaveBeenCalledWith("example.com", { all: true });
});
it("rejects invalid URLs", async () => {
await expect(
assertBrowserNavigationAllowed({
url: "not a url",
}),
).rejects.toBeInstanceOf(InvalidBrowserNavigationUrlError);
});
});