mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-30 17:36:53 +00:00
109 lines
3.1 KiB
TypeScript
109 lines
3.1 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { openExternalUrlSafe, resolveSafeExternalUrl } from "./open-external-url.ts";
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe("resolveSafeExternalUrl", () => {
|
|
const baseHref = "https://openclaw.ai/chat";
|
|
|
|
it("allows absolute https URLs", () => {
|
|
expect(resolveSafeExternalUrl("https://example.com/a.png?x=1#y", baseHref)).toBe(
|
|
"https://example.com/a.png?x=1#y",
|
|
);
|
|
});
|
|
|
|
it("allows relative URLs resolved against the current origin", () => {
|
|
expect(resolveSafeExternalUrl("/assets/pic.png", baseHref)).toBe(
|
|
"https://openclaw.ai/assets/pic.png",
|
|
);
|
|
});
|
|
|
|
it("allows blob URLs", () => {
|
|
expect(resolveSafeExternalUrl("blob:https://openclaw.ai/abc-123", baseHref)).toBe(
|
|
"blob:https://openclaw.ai/abc-123",
|
|
);
|
|
});
|
|
|
|
it("allows data image URLs when enabled", () => {
|
|
expect(
|
|
resolveSafeExternalUrl("data:image/png;base64,iVBORw0KGgo=", baseHref, {
|
|
allowDataImage: true,
|
|
}),
|
|
).toBe("data:image/png;base64,iVBORw0KGgo=");
|
|
});
|
|
|
|
it("rejects non-image data URLs", () => {
|
|
expect(
|
|
resolveSafeExternalUrl("data:text/html,<script>alert(1)</script>", baseHref, {
|
|
allowDataImage: true,
|
|
}),
|
|
).toBeNull();
|
|
});
|
|
|
|
it("rejects SVG data image URLs", () => {
|
|
expect(
|
|
resolveSafeExternalUrl(
|
|
"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' />",
|
|
baseHref,
|
|
{
|
|
allowDataImage: true,
|
|
},
|
|
),
|
|
).toBeNull();
|
|
});
|
|
|
|
it("rejects base64-encoded SVG data image URLs", () => {
|
|
expect(
|
|
resolveSafeExternalUrl(
|
|
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIC8+",
|
|
baseHref,
|
|
{
|
|
allowDataImage: true,
|
|
},
|
|
),
|
|
).toBeNull();
|
|
});
|
|
|
|
it("rejects data image URLs unless explicitly enabled", () => {
|
|
expect(resolveSafeExternalUrl("data:image/png;base64,iVBORw0KGgo=", baseHref)).toBeNull();
|
|
});
|
|
|
|
it("rejects javascript URLs", () => {
|
|
expect(resolveSafeExternalUrl("javascript:alert(1)", baseHref)).toBeNull();
|
|
});
|
|
|
|
it("rejects file URLs", () => {
|
|
expect(resolveSafeExternalUrl("file:///tmp/x.png", baseHref)).toBeNull();
|
|
});
|
|
|
|
it("rejects empty values", () => {
|
|
expect(resolveSafeExternalUrl(" ", baseHref)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("openExternalUrlSafe", () => {
|
|
it("nulls opener when window.open returns a proxy-like object", () => {
|
|
const openedLikeProxy = {
|
|
opener: { postMessage: () => void 0 },
|
|
} as unknown as WindowProxy;
|
|
const openMock = vi.fn(() => openedLikeProxy);
|
|
vi.stubGlobal("window", {
|
|
location: { href: "https://openclaw.ai/chat" },
|
|
open: openMock,
|
|
} as unknown as Window & typeof globalThis);
|
|
|
|
const opened = openExternalUrlSafe("https://example.com/safe.png");
|
|
|
|
expect(openMock).toHaveBeenCalledWith(
|
|
"https://example.com/safe.png",
|
|
"_blank",
|
|
"noopener,noreferrer",
|
|
);
|
|
expect(opened).toBe(openedLikeProxy);
|
|
expect(openedLikeProxy.opener).toBeNull();
|
|
});
|
|
});
|