super-productivity/src/index.html
Johannes Millan 4a99e7e505
fix(security): stored XSS → RCE chain — note image, plugin nodeExecution, CSP (GHSA-78rv-m663-4fph) (#8178)
* fix(ui): prevent stored XSS in enlarge-img directive

The enlarged-image element was built by interpolating the image URL into an innerHTML string, so a crafted synced/imported note.imgUrl could break out of the src attribute and inject an event handler (stored DOM-XSS). Build the <img> with createElement and property assignment instead, so the URL is never parsed as HTML. Adds a regression spec.

Refs: GHSA-78rv-m663-4fph

* fix(plugins): authorize nodeExecution from main-process state

The Electron main process authorized Node execution from the manifest the renderer passes on each IPC call, and window.ea.pluginExecNodeScript is callable by any renderer code — so injected renderer JS could forge {permissions:['nodeExecution']} and run arbitrary Node (CWE-501).

The main process now keeps its own grantedPlugins set, populated via a dedicated PLUGIN_SET_NODE_CONSENT channel. The renderer registers a grant in _fireOnReady (before the first node call, covering every load path including zip upload) and revokes it on teardown. The executor requires set membership.

Defense in depth, not a hard boundary: the registration channel is itself renderer-reachable, so a fully-compromised renderer could register a grant itself. It blocks the disclosed PoC (forged manifest for a never-loaded plugin) and removes per-call manifest trust. A hard boundary needs a main-process consent dialog.

Refs: GHSA-78rv-m663-4fph

* fix(security): add object-src 'none' and document CSP constraints

Adds object-src 'none' (the app embeds no <object>/<embed>) and replaces the stale TODO with an accurate note on why 'unsafe-eval'/'unsafe-inline' cannot be dropped yet: the plugin runtime and the inline bootstrap script use new Function, and the packaged app loads the renderer over file:// (opaque origin), so tightening script-src to 'self' is unreliable.

Refs: GHSA-78rv-m663-4fph
2026-06-08 20:20:17 +02:00

427 lines
13 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Super Productivity</title>
<meta
content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
name="viewport"
/>
<link
href="favicon.ico"
rel="shortcut icon"
type="image/x-icon"
/>
<link
href="assets/icons/apple-touch-icon.png"
rel="apple-touch-icon"
sizes="180x180"
/>
<link
href="assets/icons/favicon-16x16.png"
rel="icon"
sizes="16x16"
type="image/png"
/>
<link
href="assets/icons/favicon-32x32.png"
rel="icon"
sizes="32x32"
type="image/png"
/>
<link
href="assets/icons/favicon-192x192.png"
rel="icon"
sizes="192x192"
type="image/png"
/>
<link
color="#5bbad5"
href="assets/icons/safari-pinned-tab.svg"
rel="mask-icon"
/>
<meta
content="assets/icons/s/mstile-150x150.png"
name="msapplication-TileImage"
/>
<meta
content="#0b77d2"
name="msapplication-TileColor"
/>
<link
href="manifest.json"
rel="manifest"
/>
<meta
content="#0b77d2"
name="theme-color"
/>
<meta
content="yes"
name="mobile-web-app-capable"
/>
<meta
content="#2b5797"
name="msapplication-TileColor"
/>
<meta
content="assets/icons/browserconfig.xml"
name="msapplication-config"
/>
<!-- Content-Security-Policy. See GHSA-78rv-m663-4fph.
'unsafe-eval' and 'unsafe-inline' currently CANNOT be dropped:
- the plugin runtime runs plugin code via `new Function`
(src/app/plugins/plugin-runner.ts) → needs 'unsafe-eval';
- the inline browser-compat bootstrap <script> below also uses
`new Function` and is inline → needs both.
The packaged app loads the renderer over file:// (electron/main-window.ts),
where 'self' is an opaque origin, so tightening script-src to 'self' is
unreliable. A real lockdown (dropping unsafe-inline/unsafe-eval) needs:
a registered app:// scheme instead of file://, plugin execution moved into
a sandboxed iframe/worker, and the inline bootstrap externalized.
object-src 'none' is safe to set now (the app embeds no <object>/<embed>).
@see https://content-security-policy.com/ -->
<meta
content="default-src * uploaded:;
connect-src * data: blob: filesystem: uploaded:;
frame-src * data: blob:;
font-src * data:;
img-src * data: blob: filesystem: file:;
style-src * 'unsafe-inline' ;
object-src 'none';
script-src * 'self' 'unsafe-inline' 'unsafe-eval' blob:"
http-equiv="Content-Security-Policy"
/>
<base href="./" />
<style type="text/css">
html,
body {
height: 100%;
}
body {
height: 100%;
margin: 0;
padding: 0;
background: rgb(18, 18, 18);
color: #fff;
}
.loading-full-page-wrapper {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
cursor: grab;
-webkit-app-region: drag;
}
.loading-full-page-wrapper:active {
cursor: grabbing;
}
.app-loading {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.app-loading .logo {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 90px;
height: 90px;
}
.app-loading .logo path {
fill: #fff;
}
@media (prefers-color-scheme: light) {
body {
background: transparent;
background-image: none;
color: #000000;
}
.app-loading .logo path {
fill: #000;
}
}
.app-loading .spinner {
position: relative;
margin: 100px auto;
width: 120px;
height: 120px;
}
.app-loading .spinner-pulse {
position: absolute;
top: 50%;
left: 50%;
width: 140px;
height: 140px;
margin: -70px 0 0 -70px;
border-radius: 50%;
background-color: #6495ed;
opacity: 0;
animation: sp-pulse 2s ease-in-out infinite;
}
.productivity-tip {
text-align: center;
position: absolute;
left: 50%;
bottom: 70%;
width: 400px;
max-width: 80vw;
transform: translate(-50%);
padding-bottom: 10px;
}
.app-loading .productivity-tip h2#heading {
font:
500 20px/32px 'Open Sans',
'Helvetica Neue',
sans-serif;
margin: 0 0 8px !important;
color: inherit;
}
.app-loading .productivity-tip p {
font:
400 14px/20px 'Open Sans',
'Helvetica Neue',
sans-serif;
margin: 0 0 8px !important;
}
@keyframes sp-pulse {
0%,
100% {
opacity: 0;
transform: scale(0.8);
}
50% {
opacity: 0.15;
transform: scale(1);
}
}
</style>
</head>
<body class="mat-typography">
<app-root>
<div class="loading-full-page-wrapper">
<div class="app-loading">
<div class="productivity-tip">
<h2 id="heading">Heading</h2>
<p id="text">Text</p>
</div>
<div class="spinner">
<div class="spinner-pulse"></div>
</div>
<svg
class="logo"
viewBox="0 0 64 64"
>
<path
d="M 0.65537046,34.582787 32.452267,63.855301 63.791548,0.94356422 30.222644,47.722023 Z"
id="path3395"
style="
fill-opacity: 1;
fill-rule: evenodd;
stroke: none;
stroke-width: 1.46761858;
stroke-linecap: butt;
stroke-linejoin: bevel;
stroke-miterlimit: 13.80000019;
stroke-dasharray: none;
stroke-opacity: 1;
"
/>
</svg>
</div>
</div>
</app-root>
<noscript>Please enable JavaScript to use this application.</noscript>
<script>
// Browser compatibility check — must use ES5 syntax only.
// The app targets ES2022+ (Safari 18+, Chrome 107+). On older browsers the
// compiled JS will fail to parse, leaving the user stuck on the splash screen.
// This detects unsupported browsers early and shows a helpful message.
(function () {
var unsupported = false;
if (typeof structuredClone !== 'function') {
unsupported = true;
}
if (!unsupported) {
try {
new Function('class C { static {} }')();
} catch (e) {
unsupported = true;
}
}
if (unsupported) {
var el = document.querySelector('.app-loading');
if (el) {
el.innerHTML =
'<div style="text-align:center;padding:32px 24px;max-width:400px">' +
'<h2 style="font:500 20px/28px sans-serif;margin:0 0 16px">Browser Update Required</h2>' +
'<p style="font:400 14px/22px sans-serif;margin:0 0 12px">' +
'Super Productivity requires a newer browser version to run.' +
'</p>' +
'<p style="font:400 14px/22px sans-serif;margin:0">' +
'Please update your operating system or browser and try again.' +
'</p>' +
'</div>';
}
}
})();
var productivityTips = [
[
'Be kind',
'The simple act of paying positive attention to people has a great deal to do with productivity. ― Tom Peters',
],
[
'Achieve something new',
'Productivity is being able to do things that you were never able to do before. ― Franz Kafka',
],
[
'Focus on output not activity',
'Stressing output is the key to improving productivity, while looking to increase activity can result in just the opposite. ― Paul Gauguin',
],
[
'There is only one solution to complex problems',
'Try and correct, try and correct, try and correct, try and correct, try and correct...',
],
[
'Two step super productivity',
'1. Physically get in your work position. 2. Do one small thing.',
],
['Prioritize', 'Do first things first.'],
[
'Procrastination',
'Procrastination is an emotion regulation problem, not a time management problem.',
],
[
"When in doubt, use the magic programmer's estimation factor",
'Programmers estimate task duration as π times the original estimate.',
],
['Be realistic', 'Almost everything will take longer than you think.'],
[
'Good is good enough',
"Don't aim for perfection. Focus on the important things.",
],
[
'Distractions',
"If something is distracting you, get rid of it. If you can't get it out of your head and you can deal with it later, write it down.",
],
['Fun things first', 'The dopamine can get you through the rest!'],
[
'Celebrate',
"After completing a task, don't rush to the next. Give yourself some time to feel good about yourself!",
],
[
'Establish routines',
'Decisions take time. So establishing routines can be very helpful.',
],
[
'Take breaks',
'They help to clear your mind and make you more productive in the long run. Go! Take a walk!',
],
['Mess creates stress', 'Remove junk from your work area!'],
['Focus on a single task', 'Multi-tasking is the worst.'],
[
'Set your goals',
'Having a goal to aim for (or even several smaller ones) increases motivation and focus. Remind yourself why you are doing something!',
],
[
'Try to get it right the first time',
'This can help you avoid dragging a task on forever.',
],
[
'We are what we repeatedly do',
'Excellence, then, is not an act, but a habit. ― Aristotle',
],
[
"You don't learn to walk by following rules",
'You learn by doing, and by falling over. ― Richard Branson',
],
['Define each task', 'A problem well-put is half-solved ― John Dewey'],
[
"Either like what you do or know why you're doing it",
'If not, consider doing something else.',
],
['Focus on what matters', 'Wisdom is learning what to overlook ― William James'],
[
'Action overcomes fear',
'Better to do it than live with the fear of it. ― Logen Ninefingers',
],
[
"It is not that I'm so smart",
'But I stay with the questions much longer. ― Albert Einstein',
],
[
'You can do it',
'No problem can withstand the assault of sustained thinking. ― Voltaire',
],
[
'I have not failed',
"I've just found 10,000 ways that won't work. ― Thomas A. Edison",
],
[
'Review and reflect',
"At the end of the day or week, review your progress and reflect on what worked and what didn't. Adjust your strategies accordingly.",
],
[
'Set realistic expectations',
"Don't overburden yourself with an unrealistic workload. Be honest about what you can accomplish in a given time frame.",
],
[
'Prioritize tasks',
"Use techniques like the Eisenhower Matrix (urgent vs. important) to prioritize your tasks. Focus on what's most important and time-sensitive.",
],
[
'Always be kind to yourself',
'Productivity is impossible without good self care (at the very least, in the long run).',
],
[
'The path to progress is all about self evaluation',
'From time to time reflect if your goals and methods serve you well.',
],
[
'Embrace progress over achievement',
"It's not about massive strides every day, but the accumulation of small, incremental progress that leads to significant achievements over time.",
],
[
'Decide',
"You don't need more time in your day. You need to decide. ― Seth Godin",
],
[
'Decide to decide what not to do',
'There is only is so much time in your day (and your life). Decide what you really want to do.',
],
];
var randomIndex = Math.floor(Math.random() * productivityTips.length);
var headingEl = document.getElementById('heading');
var textEl = document.getElementById('text');
if (headingEl && textEl) {
headingEl.innerText = productivityTips[randomIndex][0];
textEl.innerText = productivityTips[randomIndex][1];
}
(function () {})();
</script>
</body>
</html>