




























@@ -24,6 +24,17 @@ afterEach(async () => {
2424await tempDirs.cleanup();
2525});
262627+async function expectRejectCode(promise: Promise<unknown>, expected: string | RegExp) {
28+const err = await promise.catch((caught: unknown) => caught);
29+expect(err).toBeDefined();
30+const code = (err as NodeJS.ErrnoException).code;
31+if (typeof expected === "string") {
32+expect(code).toBe(expected);
33+} else {
34+expect(code).toMatch(expected);
35+}
36+}
37+2738async function runWriteOpenRace(params: {
2839slotPath: string;
2940outsideDir: string;
@@ -38,11 +49,9 @@ async function runWriteOpenRace(params: {
3849try {
3950await params.runWrite();
4051} catch (err) {
41-expect(err).toMatchObject({
42-code: expect.stringMatching(
43-/outside-workspace|path-mismatch|path-alias|invalid-path|not-file/,
44-),
45-});
52+expect((err as NodeJS.ErrnoException).code).toMatch(
53+/outside-workspace|path-mismatch|path-alias|invalid-path|not-file/,
54+);
4655}
4756},
4857});
@@ -121,9 +130,7 @@ describe("fs-safe", () => {
121130122131it("rejects directories", async () => {
123132const dir = await tempDirs.make("openclaw-fs-safe-");
124-await expect(readLocalFileSafely({ filePath: dir })).rejects.toMatchObject({
125-code: "not-file",
126-});
133+await expectRejectCode(readLocalFileSafely({ filePath: dir }), "not-file");
127134const err = await readLocalFileSafely({ filePath: dir }).catch((e: unknown) => e);
128135expect(err).toBeInstanceOf(FsSafeError);
129136expect((err as FsSafeError).message).not.toMatch(/EISDIR/i);
@@ -149,9 +156,7 @@ describe("fs-safe", () => {
149156const file = path.join(dir, "big.bin");
150157await fs.writeFile(file, Buffer.alloc(8));
151158152-await expect(readLocalFileSafely({ filePath: file, maxBytes: 4 })).rejects.toMatchObject({
153-code: "too-large",
154-});
159+await expectRejectCode(readLocalFileSafely({ filePath: file, maxBytes: 4 }), "too-large");
155160});
156161157162it.runIf(process.platform !== "win32")("rejects symlinks", async () => {
@@ -161,9 +166,7 @@ describe("fs-safe", () => {
161166await fs.writeFile(target, "target");
162167await fs.symlink(target, link);
163168164-await expect(readLocalFileSafely({ filePath: link })).rejects.toMatchObject({
165-code: "symlink",
166-});
169+await expectRejectCode(readLocalFileSafely({ filePath: link }), "symlink");
167170});
168171169172it.runIf(process.platform !== "win32")(
@@ -200,17 +203,15 @@ describe("fs-safe", () => {
200203201204await expect(
202205(await openRoot(root)).open(path.join("..", path.basename(outside), "outside.txt")),
203-).rejects.toMatchObject({ code: "outside-workspace" });
206+).rejects.toSatisfy((err: NodeJS.ErrnoException) => err.code === "outside-workspace");
204207});
205208206209it("rejects directory path within root without leaking EISDIR (issue #31186)", async () => {
207210const root = await tempDirs.make("openclaw-fs-safe-root-");
208211await fs.mkdir(path.join(root, "memory"), { recursive: true });
209212210213const rootFs = await openRoot(root);
211-await expect(rootFs.open("memory")).rejects.toMatchObject({
212-code: expect.stringMatching(/invalid-path|not-file/),
213-});
214+await expectRejectCode(rootFs.open("memory"), /invalid-path|not-file/);
214215215216const err = await rootFs.open("memory").catch((e: unknown) => e);
216217expect(err).toBeInstanceOf(FsSafeError);
@@ -246,9 +247,7 @@ describe("fs-safe", () => {
246247await fs.writeFile(target, "outside");
247248await fs.symlink(target, link);
248249249-await expect((await openRoot(root)).open("link.txt")).rejects.toMatchObject({
250-code: "symlink",
251-});
250+await expectRejectCode((await openRoot(root)).open("link.txt"), "symlink");
252251});
253252254253it.runIf(process.platform !== "win32")(
@@ -273,7 +272,7 @@ describe("fs-safe", () => {
273272(await openRoot(root)).read("link.txt", {
274273symlinks: "follow-within-root",
275274}),
276-).rejects.toMatchObject({ code: "path-mismatch" });
275+).rejects.toSatisfy((err: NodeJS.ErrnoException) => err.code === "path-mismatch");
277276},
278277);
279278@@ -294,9 +293,7 @@ describe("fs-safe", () => {
294293if (openedHandle === undefined) {
295294throw new Error("expected opened file handle");
296295}
297-await expect(openedHandle.readFile({ encoding: "utf8" })).rejects.toMatchObject({
298-code: "EBADF",
299-});
296+await expectRejectCode(openedHandle.readFile({ encoding: "utf8" }), "EBADF");
300297});
301298302299it("rejects setting fs-safe test hooks outside test mode", () => {
@@ -316,9 +313,7 @@ describe("fs-safe", () => {
316313await withOutsideHardlinkAlias({
317314aliasPath: hardlinkPath,
318315run: async () => {
319-await expect((await openRoot(root)).open("link.txt")).rejects.toMatchObject({
320-code: "hardlink",
321-});
316+await expectRejectCode((await openRoot(root)).open("link.txt"), "hardlink");
322317},
323318});
324319});
@@ -365,7 +360,7 @@ describe("fs-safe", () => {
365360366361await (await openRoot(root)).remove("nested/out.txt");
367362368-await expect(fs.stat(targetPath)).rejects.toMatchObject({ code: "ENOENT" });
363+await expectRejectCode(fs.stat(targetPath), "ENOENT");
369364});
370365371366it("creates directories within root safely", async () => {
@@ -405,9 +400,7 @@ describe("fs-safe", () => {
405400406401await (await openRoot(root)).remove(path.join("alias", "target.txt"));
407402408-await expect(fs.stat(path.join(realDir, "target.txt"))).rejects.toMatchObject({
409-code: "ENOENT",
410-});
403+await expectRejectCode(fs.stat(path.join(realDir, "target.txt")), "ENOENT");
411404},
412405);
413406@@ -421,10 +414,8 @@ describe("fs-safe", () => {
421414(await openRoot(root)).copyIn("nested/big.bin", sourcePath, {
422415maxBytes: 4,
423416}),
424-).rejects.toMatchObject({ code: "too-large" });
425-await expect(fs.stat(path.join(root, "nested", "big.bin"))).rejects.toMatchObject({
426-code: "ENOENT",
427-});
417+).rejects.toSatisfy((err: NodeJS.ErrnoException) => err.code === "too-large");
418+await expectRejectCode(fs.stat(path.join(root, "nested", "big.bin")), "ENOENT");
428419});
429420430421it("writes a file within root from another local source path safely", async () => {
@@ -439,9 +430,7 @@ describe("fs-safe", () => {
439430});
440431it("rejects write traversal outside root", async () => {
441432const root = await tempDirs.make("openclaw-fs-safe-root-");
442-await expect((await openRoot(root)).write("../escape.txt", "x")).rejects.toMatchObject({
443-code: "outside-workspace",
444-});
433+await expectRejectCode((await openRoot(root)).write("../escape.txt", "x"), "outside-workspace");
445434});
446435447436it.runIf(process.platform !== "win32")("rejects writing through hardlink aliases", async () => {
@@ -450,9 +439,7 @@ describe("fs-safe", () => {
450439await withOutsideHardlinkAlias({
451440aliasPath: hardlinkPath,
452441run: async (outsideFile) => {
453-await expect((await openRoot(root)).write("alias.txt", "pwned")).rejects.toMatchObject({
454-code: "path-alias",
455-});
442+await expectRejectCode((await openRoot(root)).write("alias.txt", "pwned"), "path-alias");
456443await expect(fs.readFile(outsideFile, "utf8")).resolves.toBe("outside");
457444},
458445});
@@ -468,7 +455,7 @@ describe("fs-safe", () => {
468455(await openRoot(root)).append("alias.txt", "pwned", {
469456prependNewlineIfNeeded: true,
470457}),
471-).rejects.toMatchObject({ code: "path-alias" });
458+).rejects.toSatisfy((err: NodeJS.ErrnoException) => err.code === "path-alias");
472459await expect(fs.readFile(outsideFile, "utf8")).resolves.toBe("outside");
473460},
474461});
@@ -526,11 +513,10 @@ describe("fs-safe", () => {
526513symlinkTarget: outside,
527514timing: "before-realpath",
528515run: async () => {
529-await expect(
516+await expectRejectCode(
530517(await openRoot(root)).remove(path.join("slot", "target.txt")),
531-).rejects.toMatchObject({
532-code: expect.stringMatching(/path-alias|not-found/),
533-});
518+/path-alias|not-found/,
519+);
534520},
535521});
536522@@ -557,15 +543,14 @@ describe("fs-safe", () => {
557543symlinkTarget: outside,
558544timing: "before-realpath",
559545run: async () => {
560-await expect(
546+await expectRejectCode(
561547(await openRoot(root)).mkdir(path.join("slot", "nested", "deep")),
562-).rejects.toMatchObject({
563-code: "path-alias",
564-});
548+"path-alias",
549+);
565550},
566551});
567552568-await expect(fs.stat(path.join(outside, "nested"))).rejects.toMatchObject({ code: "ENOENT" });
553+await expectRejectCode(fs.stat(path.join(outside, "nested")), "ENOENT");
569554},
570555);
571556@@ -594,9 +579,7 @@ describe("fs-safe", () => {
594579const missing = path.join(dir, "missing.txt");
595580596581await expect(readLocalFileSafely({ filePath: missing })).rejects.toBeInstanceOf(FsSafeError);
597-await expect(readLocalFileSafely({ filePath: missing })).rejects.toMatchObject({
598-code: "not-found",
599-});
582+await expectRejectCode(readLocalFileSafely({ filePath: missing }), "not-found");
600583});
601584});
602585@@ -637,8 +620,9 @@ describe("tilde expansion in file tools", () => {
637620}
638621639622const outsideRoot = await tempDirs.make("openclaw-tilde-outside-");
640-await expect((await openRoot(outsideRoot)).open("~/escape.txt")).rejects.toMatchObject({
641-code: expect.stringMatching(/outside-workspace|not-found|invalid-path/),
642-});
623+await expectRejectCode(
624+(await openRoot(outsideRoot)).open("~/escape.txt"),
625+/outside-workspace|not-found|invalid-path/,
626+);
643627});
644628});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。