refactor: unify boundary hardening for file reads

This commit is contained in:
Peter Steinberger
2026-02-26 13:04:33 +01:00
parent cf4853e2b8
commit eac86c2081
11 changed files with 455 additions and 56 deletions

View File

@@ -231,6 +231,46 @@ describe("discoverOpenClawPlugins", () => {
);
});
it("rejects package extension entries that are hardlinked aliases", async () => {
if (process.platform === "win32") {
return;
}
const stateDir = makeTempDir();
const globalExt = path.join(stateDir, "extensions", "pack");
const outsideDir = path.join(stateDir, "outside");
const outsideFile = path.join(outsideDir, "escape.ts");
const linkedFile = path.join(globalExt, "escape.ts");
fs.mkdirSync(globalExt, { recursive: true });
fs.mkdirSync(outsideDir, { recursive: true });
fs.writeFileSync(outsideFile, "export default {}", "utf-8");
try {
fs.linkSync(outsideFile, linkedFile);
} catch (err) {
if ((err as NodeJS.ErrnoException).code === "EXDEV") {
return;
}
throw err;
}
fs.writeFileSync(
path.join(globalExt, "package.json"),
JSON.stringify({
name: "@openclaw/pack",
openclaw: { extensions: ["./escape.ts"] },
}),
"utf-8",
);
const { candidates, diagnostics } = await withStateDir(stateDir, async () => {
return discoverOpenClawPlugins({});
});
expect(candidates.some((candidate) => candidate.idHint === "pack")).toBe(false);
expect(diagnostics.some((entry) => entry.message.includes("escapes package directory"))).toBe(
true,
);
});
it.runIf(process.platform !== "win32")("blocks world-writable plugin paths", async () => {
const stateDir = makeTempDir();
const globalExt = path.join(stateDir, "extensions");