67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import { assertCanonicalPathWithinBase, safePathSegmentHashed } from "./install-safe-path.js";
|
|
|
|
describe("safePathSegmentHashed", () => {
|
|
it("keeps safe names unchanged", () => {
|
|
expect(safePathSegmentHashed("demo-skill")).toBe("demo-skill");
|
|
});
|
|
|
|
it("normalizes separators and adds hash suffix", () => {
|
|
const result = safePathSegmentHashed("../../demo/skill");
|
|
expect(result.includes("/")).toBe(false);
|
|
expect(result.includes("\\")).toBe(false);
|
|
expect(result).toMatch(/-[a-f0-9]{10}$/);
|
|
});
|
|
|
|
it("hashes long names while staying bounded", () => {
|
|
const long = "a".repeat(100);
|
|
const result = safePathSegmentHashed(long);
|
|
expect(result.length).toBeLessThanOrEqual(61);
|
|
expect(result).toMatch(/-[a-f0-9]{10}$/);
|
|
});
|
|
});
|
|
|
|
describe("assertCanonicalPathWithinBase", () => {
|
|
it("accepts in-base directories", async () => {
|
|
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-install-safe-"));
|
|
try {
|
|
const candidate = path.join(baseDir, "tools");
|
|
await fs.mkdir(candidate, { recursive: true });
|
|
await expect(
|
|
assertCanonicalPathWithinBase({
|
|
baseDir,
|
|
candidatePath: candidate,
|
|
boundaryLabel: "install directory",
|
|
}),
|
|
).resolves.toBeUndefined();
|
|
} finally {
|
|
await fs.rm(baseDir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it.runIf(process.platform !== "win32")(
|
|
"rejects symlinked candidate directories that escape the base",
|
|
async () => {
|
|
const baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-install-safe-"));
|
|
const outsideDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-install-safe-outside-"));
|
|
try {
|
|
const linkDir = path.join(baseDir, "alias");
|
|
await fs.symlink(outsideDir, linkDir);
|
|
await expect(
|
|
assertCanonicalPathWithinBase({
|
|
baseDir,
|
|
candidatePath: linkDir,
|
|
boundaryLabel: "install directory",
|
|
}),
|
|
).rejects.toThrow(/must stay within install directory/i);
|
|
} finally {
|
|
await fs.rm(baseDir, { recursive: true, force: true });
|
|
await fs.rm(outsideDir, { recursive: true, force: true });
|
|
}
|
|
},
|
|
);
|
|
});
|