Skip to content
Open
Show file tree
Hide file tree
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
10 changes: 10 additions & 0 deletions apps/frontend/src/main/ipc-handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { registerTaskHandlers } from './task-handlers';
import { registerTerminalHandlers } from './terminal-handlers';
import { registerAgenteventsHandlers } from './agent-events-handlers';
import { registerSettingsHandlers } from './settings-handlers';
import { registerTemplateHandlers } from './template-handlers';
import { registerSecretsHandlers } from './secrets-handlers';
import { registerFileHandlers } from './file-handlers';
import { registerRoadmapHandlers } from './roadmap-handlers';
import { registerContextHandlers } from './context-handlers';
Expand Down Expand Up @@ -62,6 +64,12 @@ export function setupIpcHandlers(
// Settings and dialog handlers
registerSettingsHandlers(agentManager, getMainWindow);

// Template handlers
registerTemplateHandlers();

// Secrets handlers (encrypted storage)
registerSecretsHandlers();

// File explorer handlers
registerFileHandlers();

Expand Down Expand Up @@ -108,6 +116,8 @@ export {
registerTerminalHandlers,
registerAgenteventsHandlers,
registerSettingsHandlers,
registerTemplateHandlers,
registerSecretsHandlers,
registerFileHandlers,
registerRoadmapHandlers,
registerContextHandlers,
Expand Down
51 changes: 50 additions & 1 deletion apps/frontend/src/main/ipc-handlers/project-handlers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ipcMain, app } from 'electron';
import { existsSync, readFileSync } from 'fs';
import path from 'path';
import { execFileSync } from 'child_process';
import { execFileSync, execSync } from 'child_process';
import { is } from '@electron-toolkit/utils';
import { IPC_CHANNELS } from '../../shared/constants';
import type {
Expand Down Expand Up @@ -463,4 +463,53 @@ export function registerProjectHandlers(
}
}
);

// Clone a git repository
ipcMain.handle(
IPC_CHANNELS.GIT_CLONE_REPOSITORY,
async (_, repoUrl: string, destinationPath: string): Promise<IPCResult<{ path: string }>> => {
try {
if (!existsSync(destinationPath)) {
return { success: false, error: 'Destination directory does not exist' };
}

// Extract repo name from URL (remove .git extension if present)
const repoName = repoUrl
.split('/')
.pop()
?.replace(/\.git$/, '') || 'repo';

const clonePath = path.join(destinationPath, repoName);

// Check if directory already exists
if (existsSync(clonePath)) {
return { success: false, error: `Directory "${repoName}" already exists at this location` };
}

// Clone the repository
try {
execSync(`git clone "${repoUrl}" "${clonePath}"`, {
stdio: 'pipe',
encoding: 'utf-8'
});
} catch (gitError: any) {
// Parse git error message
const errorMessage = gitError.stderr || gitError.message || 'Git clone failed';
return { success: false, error: errorMessage };
}

// Verify the clone was successful
if (!existsSync(clonePath)) {
return { success: false, error: 'Repository cloned but directory not found' };
}

return { success: true, data: { path: clonePath } };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Failed to clone repository'
};
}
}
);
Comment on lines +467 to +514
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, verify the file exists and check the actual code at lines 489-536
cat -n apps/frontend/src/main/ipc-handlers/project-handlers.ts | sed -n '485,540p'

Repository: AndyMik90/Auto-Claude

Length of output: 2079


🏁 Script executed:

# Check the imports at the top of the file
head -30 apps/frontend/src/main/ipc-handlers/project-handlers.ts

Repository: AndyMik90/Auto-Claude

Length of output: 1048


🏁 Script executed:

# Search for other execSync calls with potential string interpolation
rg -n "execSync.*\`" apps/frontend/src/main/ -A 2

Repository: AndyMik90/Auto-Claude

Length of output: 13987


🏁 Script executed:

# Search for any git-related execSync calls
rg -n "execSync.*git" apps/frontend/src/main/ -A 2

Repository: AndyMik90/Auto-Claude

Length of output: 29374


🏁 Script executed:

# Check for spawnSync usage patterns in the codebase
rg -n "spawnSync|spawn" apps/frontend/src/main/ -A 2

Repository: AndyMik90/Auto-Claude

Length of output: 36930


Critical command injection vulnerability: user-provided repoUrl is directly interpolated into shell command without sanitization.

Line 513 executes git clone with user input via template literals. A malicious repoUrl like https://github.com/repo.git"; rm -rf / # would execute arbitrary commands.

Replace execSync with spawnSync using array syntax to prevent shell interpretation:

import { execSync, spawnSync } from 'child_process';
        try {
-         execSync(`git clone "${repoUrl}" "${clonePath}"`, {
-           stdio: 'pipe',
-           encoding: 'utf-8'
-         });
+         const result = spawnSync('git', ['clone', repoUrl, clonePath], {
+           stdio: 'pipe',
+           encoding: 'utf-8'
+         });
+         if (result.error) {
+           throw result.error;
+         }
        } catch (gitError: any) {

Also address:

  • Line 517: Change catch (gitError: any) to catch (gitError: unknown) for type safety
  • Lines 499-502: Add validation that repoUrl is a valid Git URL format

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
apps/frontend/src/main/ipc-handlers/project-handlers.ts around lines 489 to 536:
the current handler directly interpolates user-controlled repoUrl into execSync
which causes a command-injection risk; replace execSync with
child_process.spawnSync using argument array form (e.g. spawnSync('git',
['clone', repoUrl, clonePath'], { stdio: 'pipe', encoding: 'utf-8' })) so the
URL is passed as an argument not interpreted by a shell, validate repoUrl before
use (ensure it matches accepted git URL patterns such as HTTPS or SSH formats
and reject/return an error for invalid values), change the git error catch
signature from catch (gitError: any) to catch (gitError: unknown) and extract a
safe message (checking instance of Error or reading stderr if present), and keep
the existing existsSync checks and error returns.

}
Loading
Loading