fix(security): mitigate GHSA-9gf9-7xcc-xcq9 & GHSA-vf6c-fgmq-xm78 + bug fixes (#667)

Co-authored-by: zuolingxuan <zuolingxuan@bytedance.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lingxuan Zuo
2026-03-25 22:02:28 +08:00
committed by GitHub
Unverified
parent 83858fdf73
commit b786b773f1
7 changed files with 141 additions and 20 deletions

View File

@@ -170,9 +170,19 @@ function createWindow(): BrowserWindow {
show: false,
});
// Handle external links
// Handle external links — only allow safe protocols to prevent arbitrary
// command execution via shell.openExternal() (e.g. file://, ms-msdt:, etc.)
win.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url);
try {
const parsed = new URL(url);
if (parsed.protocol === 'https:' || parsed.protocol === 'http:') {
shell.openExternal(url);
} else {
logger.warn(`Blocked openExternal for disallowed protocol: ${parsed.protocol}`);
}
} catch {
logger.warn(`Blocked openExternal for malformed URL: ${url}`);
}
return { action: 'deny' };
});

View File

@@ -1,6 +1,7 @@
import { ipcMain } from 'electron';
import { proxyAwareFetch } from '../../utils/proxy-fetch';
import { PORTS } from '../../utils/config';
import { getHostApiToken } from '../../api/server';
type HostApiFetchRequest = {
path: string;
@@ -10,6 +11,10 @@ type HostApiFetchRequest = {
};
export function registerHostApiProxyHandlers(): void {
// Expose the per-session auth token to the renderer so the browser-fallback
// path in host-api.ts can authenticate against the Host API server.
ipcMain.handle('hostapi:token', () => getHostApiToken());
ipcMain.handle('hostapi:fetch', async (_, request: HostApiFetchRequest) => {
try {
const path = typeof request?.path === 'string' ? request.path : '';
@@ -19,6 +24,8 @@ export function registerHostApiProxyHandlers(): void {
const method = (request.method || 'GET').toUpperCase();
const headers: Record<string, string> = { ...(request.headers || {}) };
// Inject the per-session auth token so the Host API server accepts this request.
headers['Authorization'] = `Bearer ${getHostApiToken()}`;
let body: string | undefined;
if (request.body !== undefined && request.body !== null) {
@@ -26,9 +33,11 @@ export function registerHostApiProxyHandlers(): void {
body = request.body;
} else {
body = JSON.stringify(request.body);
if (!headers['Content-Type'] && !headers['content-type']) {
headers['Content-Type'] = 'application/json';
}
}
// Ensure Content-Type is set for requests with a body so the
// server's anti-CSRF Content-Type gate does not reject them.
if (!headers['Content-Type'] && !headers['content-type']) {
headers['Content-Type'] = 'application/json';
}
}