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
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();
|
|
});
|
|
});
|