Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 53 additions & 7 deletions electron/ipc/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,35 @@ const sanitizeFileName = (value) => {
return cleaned.length > 0 ? cleaned.slice(0, 120) : "project";
};

const normalizePathForComparison = (targetPath) => {
const normalizedPath = path.normalize(path.resolve(targetPath));
return process.platform === "win32" ? normalizedPath.toLowerCase() : normalizedPath;
};

const isUnderRoot = (fullPath, root) => {
const normalized = normalizePathForComparison(fullPath);
const normalizedRoot = normalizePathForComparison(root);
return normalized === normalizedRoot || normalized.startsWith(normalizedRoot + path.sep);
};

const getProjectRoots = async () => {
const projects = await getProjects();
return projects.map((project) => project?.path).filter(Boolean);
};

const isInsideProjectRoot = async (targetPath) => {
if (!targetPath || typeof targetPath !== "string") return false;
const resolvedPath = path.resolve(targetPath);
const roots = await getProjectRoots();
return roots.some((root) => isUnderRoot(resolvedPath, root));
};

const assertInsideProjectRoot = async (targetPath) => {
if (!(await isInsideProjectRoot(targetPath))) {
throw new Error("Path must be inside a registered project");
}
};

export const registerHandlers = () => {
// Helper to log all IPC calls
const originalHandle = ipcMain.handle.bind(ipcMain);
Expand Down Expand Up @@ -242,6 +271,7 @@ export const registerHandlers = () => {
if (!filePath) {
throw new Error("File path is required");
}
await assertInsideProjectRoot(filePath);
const content = await fs.readFile(filePath, "utf-8");
return content;
} catch (e) {
Expand All @@ -257,6 +287,7 @@ export const registerHandlers = () => {

ipcMain.handle("file:write", async (_, filePath, content) => {
try {
await assertInsideProjectRoot(filePath);
await fs.writeFile(filePath, content, "utf-8");
return true;
} catch (e) {
Expand All @@ -270,11 +301,13 @@ export const registerHandlers = () => {
});

ipcMain.handle("watcher:watch", async (_, folderPath) => {
await assertInsideProjectRoot(folderPath);
watchFolder(folderPath);
return true;
});

ipcMain.handle("watcher:stop", async (_, folderPath) => {
await assertInsideProjectRoot(folderPath);
await stopWatching(folderPath);
return true;
});
Expand Down Expand Up @@ -549,13 +582,6 @@ export const registerHandlers = () => {
}
});

// Help ensure paths stay under project root
const isUnderRoot = (fullPath, root) => {
const normalized = path.normalize(path.resolve(fullPath));
const normalizedRoot = path.normalize(path.resolve(root));
return normalized === normalizedRoot || normalized.startsWith(normalizedRoot + path.sep);
};

ipcMain.handle("file:create", async (_, { projectRoot, targetPath, type, content }) => {
try {
if (!projectRoot || !targetPath || !type) {
Expand Down Expand Up @@ -687,6 +713,7 @@ export const registerHandlers = () => {
// Git
ipcMain.handle("git:status", async (_, projectPath) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitStatus(projectPath);
} catch (e) {
logger.error("Git status error:", e);
Expand All @@ -695,6 +722,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:diff", async (_, projectPath, filePath) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitDiff(projectPath, filePath);
} catch (e) {
logger.error("Git diff error:", e);
Expand All @@ -703,6 +731,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:add", async (_, projectPath, paths) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitAdd(projectPath, paths ?? []);
} catch (e) {
logger.error("Git add error:", e);
Expand All @@ -711,6 +740,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:unstage", async (_, projectPath, paths) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitUnstage(projectPath, paths ?? []);
} catch (e) {
logger.error("Git unstage error:", e);
Expand All @@ -719,6 +749,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:commit", async (_, projectPath, message) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitCommit(projectPath, message);
} catch (e) {
logger.error("Git commit error:", e);
Expand All @@ -727,6 +758,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:push", async (_, projectPath) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitPush(projectPath);
} catch (e) {
logger.error("Git push error:", e);
Expand All @@ -735,6 +767,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:pull", async (_, projectPath) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitPull(projectPath);
} catch (e) {
logger.error("Git pull error:", e);
Expand All @@ -743,6 +776,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:branches", async (_, projectPath) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitBranches(projectPath);
} catch (e) {
logger.error("Git branches error:", e);
Expand All @@ -751,6 +785,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:checkout", async (_, projectPath, branchOrRef) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitCheckout(projectPath, branchOrRef);
} catch (e) {
logger.error("Git checkout error:", e);
Expand All @@ -759,6 +794,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:clone", async (_, repoUrl, targetPath) => {
try {
await assertInsideProjectRoot(targetPath);
return await gitService.gitClone(repoUrl, targetPath);
} catch (e) {
logger.error("Git clone error:", e);
Expand All @@ -767,6 +803,7 @@ export const registerHandlers = () => {
});
ipcMain.handle("git:remoteUrl", async (_, projectPath) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitRemoteUrl(projectPath);
} catch (e) {
logger.error("Git remoteUrl error:", e);
Expand All @@ -776,6 +813,7 @@ export const registerHandlers = () => {

ipcMain.handle("git:init", async (_, projectPath) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitInit(projectPath);
} catch (e) {
logger.error("Git init error:", e);
Expand All @@ -785,6 +823,7 @@ export const registerHandlers = () => {

ipcMain.handle("git:addRemote", async (_, projectPath, name, url) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitAddRemote(projectPath, name, url);
} catch (e) {
logger.error("Git addRemote error:", e);
Expand All @@ -794,6 +833,7 @@ export const registerHandlers = () => {

ipcMain.handle("git:remotes", async (_, projectPath) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitRemotes(projectPath);
} catch (e) {
logger.error("Git remotes error:", e);
Expand All @@ -803,6 +843,7 @@ export const registerHandlers = () => {

ipcMain.handle("git:removeRemote", async (_, projectPath, name) => {
try {
await assertInsideProjectRoot(projectPath);
return await gitService.gitRemoveRemote(projectPath, name);
} catch (e) {
logger.error("Git removeRemote error:", e);
Expand Down Expand Up @@ -850,6 +891,7 @@ export const registerHandlers = () => {
}

try {
await assertInsideProjectRoot(dirPath);
return await getFiles(dirPath);
} catch (e) {
console.error(e);
Expand Down Expand Up @@ -902,6 +944,10 @@ export const registerHandlers = () => {

ipcMain.handle("shell:openPath", async (_, path) => {
try {
const userDataPath = getUserDataPath();
if (!isUnderRoot(path, userDataPath)) {
await assertInsideProjectRoot(path);
}
await shell.openPath(path);
return true;
} catch (error) {
Expand Down
Loading