diff --git a/apps/ignis-server/server/routes/fs.js b/apps/ignis-server/server/routes/fs.js index 14f382d..36654b7 100644 --- a/apps/ignis-server/server/routes/fs.js +++ b/apps/ignis-server/server/routes/fs.js @@ -263,8 +263,12 @@ router.post("/rename", async (req, res) => { return; } - const oldResolved = resolveVaultPath(vaultRoot, req.body?.oldPath); - const newResolved = resolveVaultPath(vaultRoot, req.body?.newPath); + if (!req.body?.oldPath || !req.body?.newPath) { + return res.status(400).json({ error: "Missing oldPath or newPath" }); + } + + const oldResolved = resolveVaultPath(vaultRoot, req.body.oldPath); + const newResolved = resolveVaultPath(vaultRoot, req.body.newPath); if (!oldResolved || !newResolved) { return res.status(403).json({ error: "Invalid path" }); @@ -288,8 +292,12 @@ router.post("/copyFile", async (req, res) => { return; } - const srcResolved = resolveVaultPath(vaultRoot, req.body?.src); - const destResolved = resolveVaultPath(vaultRoot, req.body?.dest); + if (!req.body?.src || !req.body?.dest) { + return res.status(400).json({ error: "Missing src or dest" }); + } + + const srcResolved = resolveVaultPath(vaultRoot, req.body.src); + const destResolved = resolveVaultPath(vaultRoot, req.body.dest); if (!srcResolved || !destResolved) { return res.status(403).json({ error: "Invalid path" }); diff --git a/apps/ignis-server/server/routes/fs.test.mjs b/apps/ignis-server/server/routes/fs.test.mjs index 70f58b0..9a41d6a 100644 --- a/apps/ignis-server/server/routes/fs.test.mjs +++ b/apps/ignis-server/server/routes/fs.test.mjs @@ -77,12 +77,12 @@ describe("resolveVaultPath", () => { expect(resolveVaultPath(root, "")).toBe(path.resolve(root)); }); - it("treats null input as vault root", () => { - expect(resolveVaultPath(root, null)).toBe(path.resolve(root)); + it("returns null for null input", () => { + expect(resolveVaultPath(root, null)).toBe(null); }); - it("treats undefined input as vault root", () => { - expect(resolveVaultPath(root, undefined)).toBe(path.resolve(root)); + it("returns null for undefined input", () => { + expect(resolveVaultPath(root, undefined)).toBe(null); }); it("strips leading slashes", () => { diff --git a/packages/server-core/src/path-utils.js b/packages/server-core/src/path-utils.js index 63e17a9..0c9e5e6 100644 --- a/packages/server-core/src/path-utils.js +++ b/packages/server-core/src/path-utils.js @@ -46,8 +46,13 @@ function encodeContentDispositionFilename(filename) { // Resolve a client-provided path to an absolute path within a vault. // Strips leading slashes so paths from the client are always treated as relative to the vault root. +// Rejects nullish input so missing-field bugs in callers don't silently target the vault root. function resolveVaultPath(vaultRoot, relativePath) { - const cleaned = (relativePath || "").replace(/^\/+/, ""); + if (relativePath === null || relativePath === undefined) { + return null; + } + + const cleaned = relativePath.replace(/^\/+/, ""); const resolved = path.resolve(vaultRoot, cleaned); const resolvedRoot = path.resolve(vaultRoot);