mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 18:24:47 +00:00
test: clear fs-safe broad matchers
This commit is contained in:
@@ -24,6 +24,17 @@ afterEach(async () => {
|
||||
await tempDirs.cleanup();
|
||||
});
|
||||
|
||||
async function expectRejectCode(promise: Promise<unknown>, expected: string | RegExp) {
|
||||
const err = await promise.catch((caught: unknown) => caught);
|
||||
expect(err).toBeDefined();
|
||||
const code = (err as NodeJS.ErrnoException).code;
|
||||
if (typeof expected === "string") {
|
||||
expect(code).toBe(expected);
|
||||
} else {
|
||||
expect(code).toMatch(expected);
|
||||
}
|
||||
}
|
||||
|
||||
async function runWriteOpenRace(params: {
|
||||
slotPath: string;
|
||||
outsideDir: string;
|
||||
@@ -38,11 +49,9 @@ async function runWriteOpenRace(params: {
|
||||
try {
|
||||
await params.runWrite();
|
||||
} catch (err) {
|
||||
expect(err).toMatchObject({
|
||||
code: expect.stringMatching(
|
||||
/outside-workspace|path-mismatch|path-alias|invalid-path|not-file/,
|
||||
),
|
||||
});
|
||||
expect((err as NodeJS.ErrnoException).code).toMatch(
|
||||
/outside-workspace|path-mismatch|path-alias|invalid-path|not-file/,
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -121,9 +130,7 @@ describe("fs-safe", () => {
|
||||
|
||||
it("rejects directories", async () => {
|
||||
const dir = await tempDirs.make("openclaw-fs-safe-");
|
||||
await expect(readLocalFileSafely({ filePath: dir })).rejects.toMatchObject({
|
||||
code: "not-file",
|
||||
});
|
||||
await expectRejectCode(readLocalFileSafely({ filePath: dir }), "not-file");
|
||||
const err = await readLocalFileSafely({ filePath: dir }).catch((e: unknown) => e);
|
||||
expect(err).toBeInstanceOf(FsSafeError);
|
||||
expect((err as FsSafeError).message).not.toMatch(/EISDIR/i);
|
||||
@@ -149,9 +156,7 @@ describe("fs-safe", () => {
|
||||
const file = path.join(dir, "big.bin");
|
||||
await fs.writeFile(file, Buffer.alloc(8));
|
||||
|
||||
await expect(readLocalFileSafely({ filePath: file, maxBytes: 4 })).rejects.toMatchObject({
|
||||
code: "too-large",
|
||||
});
|
||||
await expectRejectCode(readLocalFileSafely({ filePath: file, maxBytes: 4 }), "too-large");
|
||||
});
|
||||
|
||||
it.runIf(process.platform !== "win32")("rejects symlinks", async () => {
|
||||
@@ -161,9 +166,7 @@ describe("fs-safe", () => {
|
||||
await fs.writeFile(target, "target");
|
||||
await fs.symlink(target, link);
|
||||
|
||||
await expect(readLocalFileSafely({ filePath: link })).rejects.toMatchObject({
|
||||
code: "symlink",
|
||||
});
|
||||
await expectRejectCode(readLocalFileSafely({ filePath: link }), "symlink");
|
||||
});
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
@@ -200,7 +203,7 @@ describe("fs-safe", () => {
|
||||
|
||||
await expect(
|
||||
(await openRoot(root)).open(path.join("..", path.basename(outside), "outside.txt")),
|
||||
).rejects.toMatchObject({ code: "outside-workspace" });
|
||||
).rejects.toSatisfy((err: NodeJS.ErrnoException) => err.code === "outside-workspace");
|
||||
});
|
||||
|
||||
it("rejects directory path within root without leaking EISDIR (issue #31186)", async () => {
|
||||
@@ -208,9 +211,7 @@ describe("fs-safe", () => {
|
||||
await fs.mkdir(path.join(root, "memory"), { recursive: true });
|
||||
|
||||
const rootFs = await openRoot(root);
|
||||
await expect(rootFs.open("memory")).rejects.toMatchObject({
|
||||
code: expect.stringMatching(/invalid-path|not-file/),
|
||||
});
|
||||
await expectRejectCode(rootFs.open("memory"), /invalid-path|not-file/);
|
||||
|
||||
const err = await rootFs.open("memory").catch((e: unknown) => e);
|
||||
expect(err).toBeInstanceOf(FsSafeError);
|
||||
@@ -246,9 +247,7 @@ describe("fs-safe", () => {
|
||||
await fs.writeFile(target, "outside");
|
||||
await fs.symlink(target, link);
|
||||
|
||||
await expect((await openRoot(root)).open("link.txt")).rejects.toMatchObject({
|
||||
code: "symlink",
|
||||
});
|
||||
await expectRejectCode((await openRoot(root)).open("link.txt"), "symlink");
|
||||
});
|
||||
|
||||
it.runIf(process.platform !== "win32")(
|
||||
@@ -273,7 +272,7 @@ describe("fs-safe", () => {
|
||||
(await openRoot(root)).read("link.txt", {
|
||||
symlinks: "follow-within-root",
|
||||
}),
|
||||
).rejects.toMatchObject({ code: "path-mismatch" });
|
||||
).rejects.toSatisfy((err: NodeJS.ErrnoException) => err.code === "path-mismatch");
|
||||
},
|
||||
);
|
||||
|
||||
@@ -294,9 +293,7 @@ describe("fs-safe", () => {
|
||||
if (openedHandle === undefined) {
|
||||
throw new Error("expected opened file handle");
|
||||
}
|
||||
await expect(openedHandle.readFile({ encoding: "utf8" })).rejects.toMatchObject({
|
||||
code: "EBADF",
|
||||
});
|
||||
await expectRejectCode(openedHandle.readFile({ encoding: "utf8" }), "EBADF");
|
||||
});
|
||||
|
||||
it("rejects setting fs-safe test hooks outside test mode", () => {
|
||||
@@ -316,9 +313,7 @@ describe("fs-safe", () => {
|
||||
await withOutsideHardlinkAlias({
|
||||
aliasPath: hardlinkPath,
|
||||
run: async () => {
|
||||
await expect((await openRoot(root)).open("link.txt")).rejects.toMatchObject({
|
||||
code: "hardlink",
|
||||
});
|
||||
await expectRejectCode((await openRoot(root)).open("link.txt"), "hardlink");
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -365,7 +360,7 @@ describe("fs-safe", () => {
|
||||
|
||||
await (await openRoot(root)).remove("nested/out.txt");
|
||||
|
||||
await expect(fs.stat(targetPath)).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expectRejectCode(fs.stat(targetPath), "ENOENT");
|
||||
});
|
||||
|
||||
it("creates directories within root safely", async () => {
|
||||
@@ -405,9 +400,7 @@ describe("fs-safe", () => {
|
||||
|
||||
await (await openRoot(root)).remove(path.join("alias", "target.txt"));
|
||||
|
||||
await expect(fs.stat(path.join(realDir, "target.txt"))).rejects.toMatchObject({
|
||||
code: "ENOENT",
|
||||
});
|
||||
await expectRejectCode(fs.stat(path.join(realDir, "target.txt")), "ENOENT");
|
||||
},
|
||||
);
|
||||
|
||||
@@ -421,10 +414,8 @@ describe("fs-safe", () => {
|
||||
(await openRoot(root)).copyIn("nested/big.bin", sourcePath, {
|
||||
maxBytes: 4,
|
||||
}),
|
||||
).rejects.toMatchObject({ code: "too-large" });
|
||||
await expect(fs.stat(path.join(root, "nested", "big.bin"))).rejects.toMatchObject({
|
||||
code: "ENOENT",
|
||||
});
|
||||
).rejects.toSatisfy((err: NodeJS.ErrnoException) => err.code === "too-large");
|
||||
await expectRejectCode(fs.stat(path.join(root, "nested", "big.bin")), "ENOENT");
|
||||
});
|
||||
|
||||
it("writes a file within root from another local source path safely", async () => {
|
||||
@@ -439,9 +430,7 @@ describe("fs-safe", () => {
|
||||
});
|
||||
it("rejects write traversal outside root", async () => {
|
||||
const root = await tempDirs.make("openclaw-fs-safe-root-");
|
||||
await expect((await openRoot(root)).write("../escape.txt", "x")).rejects.toMatchObject({
|
||||
code: "outside-workspace",
|
||||
});
|
||||
await expectRejectCode((await openRoot(root)).write("../escape.txt", "x"), "outside-workspace");
|
||||
});
|
||||
|
||||
it.runIf(process.platform !== "win32")("rejects writing through hardlink aliases", async () => {
|
||||
@@ -450,9 +439,7 @@ describe("fs-safe", () => {
|
||||
await withOutsideHardlinkAlias({
|
||||
aliasPath: hardlinkPath,
|
||||
run: async (outsideFile) => {
|
||||
await expect((await openRoot(root)).write("alias.txt", "pwned")).rejects.toMatchObject({
|
||||
code: "path-alias",
|
||||
});
|
||||
await expectRejectCode((await openRoot(root)).write("alias.txt", "pwned"), "path-alias");
|
||||
await expect(fs.readFile(outsideFile, "utf8")).resolves.toBe("outside");
|
||||
},
|
||||
});
|
||||
@@ -468,7 +455,7 @@ describe("fs-safe", () => {
|
||||
(await openRoot(root)).append("alias.txt", "pwned", {
|
||||
prependNewlineIfNeeded: true,
|
||||
}),
|
||||
).rejects.toMatchObject({ code: "path-alias" });
|
||||
).rejects.toSatisfy((err: NodeJS.ErrnoException) => err.code === "path-alias");
|
||||
await expect(fs.readFile(outsideFile, "utf8")).resolves.toBe("outside");
|
||||
},
|
||||
});
|
||||
@@ -526,11 +513,10 @@ describe("fs-safe", () => {
|
||||
symlinkTarget: outside,
|
||||
timing: "before-realpath",
|
||||
run: async () => {
|
||||
await expect(
|
||||
await expectRejectCode(
|
||||
(await openRoot(root)).remove(path.join("slot", "target.txt")),
|
||||
).rejects.toMatchObject({
|
||||
code: expect.stringMatching(/path-alias|not-found/),
|
||||
});
|
||||
/path-alias|not-found/,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -557,15 +543,14 @@ describe("fs-safe", () => {
|
||||
symlinkTarget: outside,
|
||||
timing: "before-realpath",
|
||||
run: async () => {
|
||||
await expect(
|
||||
await expectRejectCode(
|
||||
(await openRoot(root)).mkdir(path.join("slot", "nested", "deep")),
|
||||
).rejects.toMatchObject({
|
||||
code: "path-alias",
|
||||
});
|
||||
"path-alias",
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
await expect(fs.stat(path.join(outside, "nested"))).rejects.toMatchObject({ code: "ENOENT" });
|
||||
await expectRejectCode(fs.stat(path.join(outside, "nested")), "ENOENT");
|
||||
},
|
||||
);
|
||||
|
||||
@@ -594,9 +579,7 @@ describe("fs-safe", () => {
|
||||
const missing = path.join(dir, "missing.txt");
|
||||
|
||||
await expect(readLocalFileSafely({ filePath: missing })).rejects.toBeInstanceOf(FsSafeError);
|
||||
await expect(readLocalFileSafely({ filePath: missing })).rejects.toMatchObject({
|
||||
code: "not-found",
|
||||
});
|
||||
await expectRejectCode(readLocalFileSafely({ filePath: missing }), "not-found");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -637,8 +620,9 @@ describe("tilde expansion in file tools", () => {
|
||||
}
|
||||
|
||||
const outsideRoot = await tempDirs.make("openclaw-tilde-outside-");
|
||||
await expect((await openRoot(outsideRoot)).open("~/escape.txt")).rejects.toMatchObject({
|
||||
code: expect.stringMatching(/outside-workspace|not-found|invalid-path/),
|
||||
});
|
||||
await expectRejectCode(
|
||||
(await openRoot(outsideRoot)).open("~/escape.txt"),
|
||||
/outside-workspace|not-found|invalid-path/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user