mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-22 18:30:09 +00:00
feat(electronSecurity): add security layer for exec commands
This commit is contained in:
parent
e8343d060e
commit
87ad3d59b5
3 changed files with 79 additions and 11 deletions
|
|
@ -2,6 +2,7 @@
|
|||
// ---------------
|
||||
import {
|
||||
app,
|
||||
dialog,
|
||||
globalShortcut,
|
||||
ipcMain,
|
||||
IpcMainEvent,
|
||||
|
|
@ -19,6 +20,7 @@ import { log } from 'electron-log/main';
|
|||
import { exec } from 'child_process';
|
||||
import { getWin } from './main-window';
|
||||
import { quitApp, showOrFocus } from './various-shared';
|
||||
import { loadSimpleStoreAll, saveSimpleStore } from './simple-store';
|
||||
|
||||
// HANDLER
|
||||
// -------
|
||||
|
|
@ -55,9 +57,6 @@ ipcMain.on(IPC.RELOAD_MAIN_WIN, () => getWin().reload());
|
|||
ipcMain.on(IPC.OPEN_PATH, (ev, path: string) => shell.openPath(path));
|
||||
ipcMain.on(IPC.OPEN_EXTERNAL, (ev, url: string) => shell.openExternal(url));
|
||||
|
||||
// TODO check
|
||||
ipcMain.on(IPC.EXEC, execWithFrontendErrorHandlerInform);
|
||||
|
||||
ipcMain.on(IPC.LOCK_SCREEN, () => {
|
||||
if ((app as any).isLocked) {
|
||||
return;
|
||||
|
|
@ -109,6 +108,8 @@ ipcMain.on(IPC.SHOW_OR_FOCUS, () => {
|
|||
showOrFocus(mainWin);
|
||||
});
|
||||
|
||||
ipcMain.on(IPC.EXEC, execWithFrontendErrorHandlerInform);
|
||||
|
||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||
function registerShowAppShortCuts(cfg: KeyboardConfig): void {
|
||||
// unregister all previous
|
||||
|
|
@ -179,12 +180,49 @@ function registerShowAppShortCuts(cfg: KeyboardConfig): void {
|
|||
}
|
||||
}
|
||||
|
||||
const COMMAND_MAP_PROP = 'allowedCommands';
|
||||
|
||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||
function execWithFrontendErrorHandlerInform(ev: IpcMainEvent, command: string): void {
|
||||
log('running command ' + command);
|
||||
exec(command, (err) => {
|
||||
if (err) {
|
||||
errorHandlerWithFrontendInform(err);
|
||||
async function execWithFrontendErrorHandlerInform(
|
||||
ev: IpcMainEvent,
|
||||
command: string,
|
||||
): Promise<void> {
|
||||
log('trying to run command ' + command);
|
||||
const existingData = await loadSimpleStoreAll();
|
||||
const allowedCommands: string[] = (existingData[COMMAND_MAP_PROP] as string[]) || [];
|
||||
|
||||
if (!Array.isArray(allowedCommands)) {
|
||||
throw new Error('allowedCommands is no array ???');
|
||||
}
|
||||
if (allowedCommands.includes(command)) {
|
||||
exec(command, (err) => {
|
||||
if (err) {
|
||||
errorHandlerWithFrontendInform(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const res = await dialog.showMessageBox(null, {
|
||||
type: 'question',
|
||||
buttons: ['Cancel', 'Yes, execute!'],
|
||||
defaultId: 2,
|
||||
title: 'Super Productivity – Exec',
|
||||
message:
|
||||
'Do you want to execute this command? ONLY confirm if you are sure you know what you are doing!!',
|
||||
detail: command,
|
||||
checkboxLabel: 'Remember my answer',
|
||||
checkboxChecked: true,
|
||||
});
|
||||
const { response, checkboxChecked } = res;
|
||||
|
||||
if (response === 1) {
|
||||
if (checkboxChecked) {
|
||||
await saveSimpleStore(COMMAND_MAP_PROP, [...allowedCommands, command]);
|
||||
}
|
||||
exec(command, (err) => {
|
||||
if (err) {
|
||||
errorHandlerWithFrontendInform(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,8 +77,7 @@ const ea: ElectronAPI = {
|
|||
|
||||
updateCurrentTask: (task) => _send('CURRENT_TASK_UPDATED', task),
|
||||
|
||||
// TODO make secure
|
||||
exec: () => _send('EXEC'),
|
||||
exec: (command: string) => _send('EXEC', command),
|
||||
};
|
||||
contextBridge.exposeInMainWorld('ea', ea);
|
||||
|
||||
|
|
|
|||
31
electron/simple-store.ts
Normal file
31
electron/simple-store.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { promises as fs } from 'fs';
|
||||
import { app } from 'electron';
|
||||
import * as path from 'path';
|
||||
|
||||
const DATA_PATH = path.join(app.getPath('userData'), 'simpleSettings');
|
||||
|
||||
type SimpleStoreData = { [key: string]: unknown };
|
||||
|
||||
export const saveSimpleStore = async (
|
||||
dataKey = 'main',
|
||||
data: unknown,
|
||||
): Promise<unknown> => {
|
||||
const prevData = await loadSimpleStoreAll();
|
||||
|
||||
return await fs.writeFile(DATA_PATH, JSON.stringify({ ...prevData, [dataKey]: data }), {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
};
|
||||
|
||||
export const loadSimpleStoreAll = async (): Promise<SimpleStoreData> => {
|
||||
try {
|
||||
const data = await fs.readFile(DATA_PATH, { encoding: 'utf8' });
|
||||
console.log(data);
|
||||
console.log(JSON.parse(data));
|
||||
|
||||
return JSON.parse(data);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue