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
23 changes: 22 additions & 1 deletion electron/ipc/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ const appLauncher = new AutoLaunch({
path: process.execPath,
});

const ALLOWED_EXTERNAL_URL_PROTOCOLS = new Set(["http:", "https:", "mailto:"]);

export const validateExternalUrl = (url) => {
if (typeof url !== "string" || url.trim() !== url || url.length === 0) {
return null;
}

try {
const parsed = new URL(url);
return ALLOWED_EXTERNAL_URL_PROTOCOLS.has(parsed.protocol) ? parsed.toString() : null;
} catch {
return null;
}
};

/**
* Create a filesystem-safe filename segment for Windows/macOS/Linux.
* Removes characters that are invalid on Windows and trims length.
Expand Down Expand Up @@ -871,7 +886,13 @@ export const registerHandlers = () => {
// Open external URL in default browser
ipcMain.handle("app:openExternal", async (_, url) => {
try {
await shell.openExternal(url);
const safeUrl = validateExternalUrl(url);
if (!safeUrl) {
logger.warn(`Blocked unsafe external URL: ${String(url).slice(0, 200)}`);
return false;
}

await shell.openExternal(safeUrl);
return true;
} catch (error) {
logger.error(`Failed to open external URL ${url}:`, error);
Expand Down
11 changes: 9 additions & 2 deletions electron/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { app, BrowserWindow, ipcMain, shell, protocol, net } from "electron";
import path from "path";
import fs from "fs";
import { fileURLToPath, pathToFileURL } from "url";
import { registerHandlers } from "./ipc/handlers.js";
import { registerHandlers, validateExternalUrl } from "./ipc/handlers.js";
import { initializeDatabase } from "./services/database.js";
import { initTray } from "./tray/tray.js";
import { stopAllProjects } from "./services/projectsManager.js";
Expand Down Expand Up @@ -168,7 +168,14 @@ async function createWindow() {
mainWindow.webContents.send("window:unmaximize");
});
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);
const safeUrl = validateExternalUrl(url);
if (safeUrl) {
shell.openExternal(safeUrl).catch((err) => {
logger.error(`Failed to open external URL: ${err.message}`);
});
} else {
logger.warn(`Blocked unsafe window open URL: ${String(url).slice(0, 200)}`);
}
return { action: "deny" };
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Expand Down
4 changes: 2 additions & 2 deletions src/pages/settings/AboutSection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { motion } from "framer-motion";
import {
Terminal,
Globe,
Github,
GithubIcon,
MessageCircle,
Users,
Mail,
Expand Down Expand Up @@ -35,7 +35,7 @@ const LINKS = [
label: "GitHub",
value: "DevRoots-Studio / SelfHost-Helper",
href: "https://github.com/DevRoots-Studio/SelfHost-Helper",
Icon: Github,
Icon: GithubIcon,
accent: "bg-white/8 text-foreground/80",
badge: "Open Source",
},
Expand Down
Loading