You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

83 lines
2.5 KiB

import { describe, expect, it, beforeEach, afterEach } from "vitest";
import { assertUnderRoot, resolveDataFile } from "@/lib/safe-path";
import { assertSafeCcApiBase } from "@/lib/cc-api-base";
import { createOAuthState, parseOAuthState } from "@/services/oauth/providers";
import { resetEnvCache } from "@/lib/env";
import path from "path";
describe("safe-path", () => {
it("allows descendant under base", () => {
const base = path.resolve("data");
const target = path.join(base, "mails", "1", "a.pdf");
expect(assertUnderRoot(base, target)).toBe(path.resolve(target));
});
it("rejects sibling prefix bypass", () => {
const base = path.resolve("data");
const evil = path.resolve("data-evil", "secret");
expect(() => assertUnderRoot(base, evil)).toThrow("PATH_TRAVERSAL");
});
it("rejects absolute path outside data via resolveDataFile", () => {
expect(() => resolveDataFile("/etc/passwd")).toThrow("PATH_TRAVERSAL");
});
});
describe("cc-api-base", () => {
it("allows official carriercentral host", () => {
const u = assertSafeCcApiBase(
"https://test.saas.carriercentral.vip/api/",
);
expect(u).toContain("carriercentral.vip");
});
it("rejects private IP", () => {
expect(() => assertSafeCcApiBase("http://192.168.1.1/api")).toThrow(
/PRIVATE|HOST|PROTOCOL|ALLOWED/,
);
});
it("rejects metadata host", () => {
expect(() =>
assertSafeCcApiBase("http://169.254.169.254/latest/meta-data"),
).toThrow();
});
});
describe("oauth state", () => {
const prevSecret = process.env.SESSION_SECRET;
beforeEach(() => {
process.env.SESSION_SECRET =
"unit-test-session-secret-at-least-32-chars!!";
process.env.DATABASE_URL =
process.env.DATABASE_URL || "mysql://u:p@localhost:3306/t";
resetEnvCache();
});
afterEach(() => {
if (prevSecret === undefined) delete process.env.SESSION_SECRET;
else process.env.SESSION_SECRET = prevSecret;
resetEnvCache();
});
it("roundtrips with actor", () => {
const state = createOAuthState({
provider: "gmail",
actor: "admin",
name: "Gmail",
});
const parsed = parseOAuthState(state);
expect(parsed?.provider).toBe("gmail");
expect(parsed?.actor).toBe("admin");
expect(parsed?.exp).toBeTruthy();
});
it("rejects tampered sig", () => {
const state = createOAuthState({ provider: "gmail", actor: "admin" });
const parts = state.split(".");
parts[2] = "x".repeat(parts[2].length);
expect(parseOAuthState(parts.join("."))).toBeNull();
});
});