super-productivity/electron/full-screen-blocker.ts
Johannes Millan 4d8605baa3 fix(security): harden XSS sinks and add Origin check (#7413)
Address Snyk findings raised in discussion #7413:

- error-handler: inject error title, additionalLog, stackTrace, and
  meta into the global error alert via textContent/setAttribute instead
  of innerHTML interpolation. Also fixes a latent <h2> close-tag typo.
- break-reminder-overlay: use textContent (not innerHTML) for the
  reminder message; switch to decodeURIComponent to match a producer-
  side encodeURIComponent change (also fixes a latent parse bug for
  messages containing & or =).
- local-rest-api: reject any request that arrives with a web Origin
  header. Closes the simple-POST CSRF gap (text/plain bodies are not
  preflighted by CORS) on top of the existing Host allowlist.

The Snyk findings on Google OAuth CLIENT_SECRET (Desktop client per
RFC 8252) and proxy-agent rejectUnauthorized (opt-in for self-hosted
WebDAV with self-signed certs) are documented false positives and
intentional, respectively; both already carry explanatory comments.
2026-05-06 21:37:06 +02:00

74 lines
2.2 KiB
TypeScript

import { BrowserWindow, ipcMain } from 'electron';
import { IPC } from './shared-with-frontend/ipc-events.const';
import { TakeABreakConfig } from '../src/app/features/config/global-config.model';
import { join, normalize } from 'path';
export const initFullScreenBlocker = (IS_DEV: boolean): void => {
let isFullScreenWindowOpen = false;
ipcMain.on(
IPC.FULL_SCREEN_BLOCKER,
(
ipcEvent,
{ msg, takeABreakCfg }: { msg: string; takeABreakCfg: TakeABreakConfig },
): void => {
if (isFullScreenWindowOpen) {
return;
}
let isClosable = false;
const win = new BrowserWindow({
title: msg,
fullscreen: true,
alwaysOnTop: true,
transparent: true,
skipTaskbar: true,
frame: false,
});
const randomImgUrl = takeABreakCfg.motivationalImgs?.length
? takeABreakCfg.motivationalImgs[
Math.floor(Math.random() * takeABreakCfg.motivationalImgs.length)
]
: '';
win.setAlwaysOnTop(true, 'floating');
win.setVisibleOnAllWorkspaces(true);
win.setFullScreenable(false);
isFullScreenWindowOpen = true;
win.loadURL(
`file://${normalize(
join(
__dirname,
IS_DEV
? '../src/static/break-reminder-overlay.html'
: '../dist/static/break-reminder-overlay.html',
),
)}` +
`#msg=${encodeURIComponent(msg)}&img=${encodeURIComponent(randomImgUrl ?? '')}&time=${
takeABreakCfg.timedFullScreenBlockerDuration
}`,
);
const closeTimeout = setTimeout(() => {
isClosable = true;
win.close();
}, takeABreakCfg.timedFullScreenBlockerDuration || 5000);
win.on('close', (evI) => {
if (isClosable) {
if (closeTimeout) {
clearTimeout(closeTimeout);
}
isFullScreenWindowOpen = false;
} else {
evI.preventDefault();
}
});
win.on('closed', () => {
// Clean up references
isFullScreenWindowOpen = false;
if (closeTimeout) {
clearTimeout(closeTimeout);
}
});
},
);
};