Skip to content
Merged
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
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2026-07-17 - [IPC Command and Process Injection Prevention in Electron]
**Vulnerability:** Unsanitized parameters exposed via IPC handlers in Electron allowed the renderer process to trigger arbitrary process execution (`restart-shell` with arbitrary executable path) and git command/argument injection (`git-cmd` with custom arguments like `--ext-diff`).
**Learning:** Even if context isolation is enabled and the renderer does not expose the raw `child_process` module, exposed high-level IPC handlers (like Git commands or custom shell selection handlers) can still be abused as a bridge for command/argument injection if they do not strictly sanitize or validate their inputs on the main process side.
**Prevention:** Always validate all parameters received from the IPC renderer on the main process side (the secure side). Restrict shell execution to a strict whitelist of known/safe shell binaries, and block dangerous git flags such as `--ext-diff`, `--textconv`, `--exec-path`, `--output`, `--pager`, `--config`, and short flags like `-c`/`-C`.
64 changes: 64 additions & 0 deletions electron.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,44 @@ ipcMain.handle('is-pty', () => {
return usingPty;
});

const ALLOWED_SHELLS = [
'powershell.exe',
'pwsh.exe',
'cmd.exe',
'powershell',
'pwsh',
'cmd',
'/bin/bash',
'/bin/zsh',
'/bin/sh',
'/usr/bin/bash',
'/usr/bin/zsh',
'/usr/bin/sh',
'bash',
'zsh',
'sh',
'c:/windows/system32/cmd.exe',
'c:/windows/system32/windowspowershell/v1.0/powershell.exe',
'c:/windows/syswow64/cmd.exe',
'c:/windows/syswow64/windowspowershell/v1.0/powershell.exe'
];

function isSafeShell(shellPath) {
if (typeof shellPath !== 'string') return false;
const normalized = shellPath.trim().replace(/\\/g, '/').toLowerCase();
if (normalized.includes('/')) {
return ALLOWED_SHELLS.includes(normalized);
}
return ALLOWED_SHELLS.includes(normalized);
}

ipcMain.handle('restart-shell', (event, shellPath) => {
if (shellPath) {
// Secure validation to prevent process execution of arbitrary binaries (Command/Process Injection vulnerability)
if (!isSafeShell(shellPath)) {
console.warn(`Blocked unsafe shell override attempt: "${shellPath}"`);
return false;
}
// Override the shell for this restart
const savedShell = workspaceRoot;
workspaceRoot = savedShell;
Expand Down Expand Up @@ -721,6 +757,34 @@ ipcMain.handle('git-cmd', (event, cmdArgs) => {
resolve({ success: false, stdout: '', stderr: `Blocked: '${subCmd}' is not allowed` });
return;
}

// Check for dangerous options to prevent git argument injection
const dangerousPatterns = [
'--ext-diff',
'--textconv',
'--exec-path',
'--output',
'--pager',
'--upload-pack',
'--receive-pack',
'--config',
'--git-dir',
'--work-tree'
];

for (const part of parts) {
const lowerPart = part.toLowerCase();
// Block exact short option matches that are dangerous (covers -c and -C case-insensitively)
if (lowerPart === '-c') {
resolve({ success: false, stdout: '', stderr: `Blocked: Dangerous option '${part}' is not allowed` });
return;
}
// Block any argument containing a dangerous pattern
if (dangerousPatterns.some(pattern => lowerPart.includes(pattern))) {
resolve({ success: false, stdout: '', stderr: `Blocked: Dangerous option '${part}' is not allowed` });
return;
}
}

// For commit, extract message without shell quoting artifacts
if (subCmd === 'commit') {
Expand Down
Loading
Loading