39 lines
1.5 KiB
TypeScript
39 lines
1.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { cloudAppUrl, cloudStackCreateUrl, cloudStackEnterUrl } from "./cloudLinks";
|
|
|
|
describe("cloudLinks", () => {
|
|
it("resolves stack links against the cloud origin", () => {
|
|
expect(cloudStackEnterUrl("https://app.paperclip.app", "acme")).toBe(
|
|
"https://app.paperclip.app/stacks/acme/enter",
|
|
);
|
|
expect(cloudStackCreateUrl("https://app.paperclip.app")).toBe(
|
|
"https://app.paperclip.app/stacks/new",
|
|
);
|
|
});
|
|
|
|
it("drops a control-plane path suffix on the configured origin", () => {
|
|
expect(cloudStackEnterUrl("https://cloud.example.test/control-plane", "acme")).toBe(
|
|
"https://cloud.example.test/stacks/acme/enter",
|
|
);
|
|
});
|
|
|
|
it("escapes slugs so a crafted portfolio entry cannot climb the path", () => {
|
|
expect(cloudStackEnterUrl("https://app.paperclip.app", "../../evil")).toBe(
|
|
"https://app.paperclip.app/stacks/..%2F..%2Fevil/enter",
|
|
);
|
|
});
|
|
|
|
it("returns null without a usable base or slug", () => {
|
|
expect(cloudStackEnterUrl(null, "acme")).toBeNull();
|
|
expect(cloudStackEnterUrl(" ", "acme")).toBeNull();
|
|
expect(cloudStackEnterUrl("https://app.paperclip.app", " ")).toBeNull();
|
|
expect(cloudStackEnterUrl("not a url", "acme")).toBeNull();
|
|
expect(cloudStackCreateUrl(undefined)).toBeNull();
|
|
});
|
|
|
|
it("refuses non-web schemes", () => {
|
|
expect(cloudAppUrl("javascript:alert(1)", "/stacks/new")).toBeNull();
|
|
expect(cloudAppUrl("file:///etc", "/stacks/new")).toBeNull();
|
|
});
|
|
});
|