mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 00:18:26 +00:00
refactor: unify boundary hardening for file reads
This commit is contained in:
@@ -137,6 +137,7 @@ function makeFileStat(params?: {
|
||||
mtimeMs?: number;
|
||||
dev?: number;
|
||||
ino?: number;
|
||||
nlink?: number;
|
||||
}): import("node:fs").Stats {
|
||||
return {
|
||||
isFile: () => true,
|
||||
@@ -145,6 +146,7 @@ function makeFileStat(params?: {
|
||||
mtimeMs: params?.mtimeMs ?? 1234,
|
||||
dev: params?.dev ?? 1,
|
||||
ino: params?.ino ?? 1,
|
||||
nlink: params?.nlink ?? 1,
|
||||
} as unknown as import("node:fs").Stats;
|
||||
}
|
||||
|
||||
@@ -649,4 +651,66 @@ describe("agents.files.get/set symlink safety", () => {
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects agents.files.get when allowlisted file is a hardlinked alias", async () => {
|
||||
const workspace = "/workspace/test-agent";
|
||||
const candidate = path.resolve(workspace, "AGENTS.md");
|
||||
mocks.fsRealpath.mockImplementation(async (p: string) => {
|
||||
if (p === workspace) {
|
||||
return workspace;
|
||||
}
|
||||
return p;
|
||||
});
|
||||
mocks.fsLstat.mockImplementation(async (...args: unknown[]) => {
|
||||
const p = typeof args[0] === "string" ? args[0] : "";
|
||||
if (p === candidate) {
|
||||
return makeFileStat({ nlink: 2 });
|
||||
}
|
||||
throw createEnoentError();
|
||||
});
|
||||
|
||||
const { respond, promise } = makeCall("agents.files.get", {
|
||||
agentId: "main",
|
||||
name: "AGENTS.md",
|
||||
});
|
||||
await promise;
|
||||
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
false,
|
||||
undefined,
|
||||
expect.objectContaining({ message: expect.stringContaining("unsafe workspace file") }),
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects agents.files.set when allowlisted file is a hardlinked alias", async () => {
|
||||
const workspace = "/workspace/test-agent";
|
||||
const candidate = path.resolve(workspace, "AGENTS.md");
|
||||
mocks.fsRealpath.mockImplementation(async (p: string) => {
|
||||
if (p === workspace) {
|
||||
return workspace;
|
||||
}
|
||||
return p;
|
||||
});
|
||||
mocks.fsLstat.mockImplementation(async (...args: unknown[]) => {
|
||||
const p = typeof args[0] === "string" ? args[0] : "";
|
||||
if (p === candidate) {
|
||||
return makeFileStat({ nlink: 2 });
|
||||
}
|
||||
throw createEnoentError();
|
||||
});
|
||||
|
||||
const { respond, promise } = makeCall("agents.files.set", {
|
||||
agentId: "main",
|
||||
name: "AGENTS.md",
|
||||
content: "x",
|
||||
});
|
||||
await promise;
|
||||
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
false,
|
||||
undefined,
|
||||
expect.objectContaining({ message: expect.stringContaining("unsafe workspace file") }),
|
||||
);
|
||||
expect(mocks.fsOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user