mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-17 16:37:43 +00:00
* docs(plugins): add microsoft 365 calendar provider plan * fix(jira): gate Electron requests behind one-shot capability Claim privileged Jira IPC before plugin startup and return responses through invoke instead of a broadcast event. Keep arbitrary HTTP(S) Jira hosts supported while rejecting redirects and bounding request resources. * fix(jira): enforce Electron request capability Bind privileged Jira IPC to a main-issued renderer-document token and strip raw Electron events from renderer callbacks. Scope image authentication by origin, base path, and resource type while preserving safe redirects and legacy configurations. * fix(electron): handle payload-only IPC lifecycle Clear Jira image authentication before replacement and when a new renderer document claims the capability. Parse before-close IDs from payload-only events so pending sync and finish-day hooks can complete. * fix(electron): address Jira IPC capability review findings - electron.effects: read ANY_FILE_DOWNLOADED payload at [0] after the payload-only IPC refactor (was [1], now undefined -> TypeError on every download); guard against a malformed payload - jira-capability: rotate the token on re-register so a renderer reload that reuses the WebFrameMain object is not permanently locked out of Jira; invalidates any stale token - document that the one-shot consumption order, not the bypassable main-frame check, is the real capability boundary - jira-electron-bridge: skip the no-op clearImgHeaders IPC round-trip when image auth was never set up (non-Jira detail-panel open/close) - jira-api: route a synchronous _toElectronRequestInit throw through _handleResponse instead of leaking a dangling request-log entry * test(electron): cover ANY_FILE_DOWNLOADED payload parsing Extract parseDownloadedFilePayload from ElectronEffects and add a regression spec pinning the payload-only shape ([file], not [event, file]) that caused a TypeError on every download. Hardened against non-array input surfaced by the new test. * fix(electron): restore Node ambient globals for frontend build
107 lines
3.3 KiB
JavaScript
107 lines
3.3 KiB
JavaScript
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const path = require('node:path');
|
|
const Module = require('node:module');
|
|
|
|
require('ts-node/register/transpile-only');
|
|
|
|
const handlers = new Map();
|
|
const jiraImageAuth = require(path.resolve(__dirname, 'jira-image-auth.ts'));
|
|
const originalModuleLoad = Module._load;
|
|
Module._load = function patchedLoad(request, parent, isMain) {
|
|
if (request === 'electron') {
|
|
return {
|
|
ipcMain: {
|
|
handle: (channel, handler) => handlers.set(channel, handler),
|
|
on: () => undefined,
|
|
},
|
|
};
|
|
}
|
|
if (request === '../jira-image-auth') {
|
|
return jiraImageAuth;
|
|
}
|
|
return originalModuleLoad.call(this, request, parent, isMain);
|
|
};
|
|
|
|
const { initJiraIpc } = require(path.resolve(__dirname, 'ipc-handlers/jira.ts'));
|
|
const { IPC } = require(
|
|
path.resolve(__dirname, 'shared-with-frontend/ipc-events.const.ts'),
|
|
);
|
|
const { applyJiraImageAuth, setupRequestHeadersForImages } = jiraImageAuth;
|
|
Module._load = originalModuleLoad;
|
|
|
|
initJiraIpc();
|
|
|
|
test('Jira IPC issues a capability only to the main renderer frame', () => {
|
|
const mainFrame = {};
|
|
const event = { senderFrame: mainFrame, sender: { mainFrame } };
|
|
const register = handlers.get(IPC.JIRA_REGISTER_CAPABILITY);
|
|
|
|
const token = register(event);
|
|
assert.equal(typeof token, 'string');
|
|
// Re-registering (e.g. after a renderer reload) rotates the token instead of
|
|
// locking the frame out.
|
|
const rotated = register(event);
|
|
assert.equal(typeof rotated, 'string');
|
|
assert.notEqual(rotated, token);
|
|
|
|
const subFrame = {};
|
|
assert.equal(register({ senderFrame: subFrame, sender: { mainFrame } }), null);
|
|
});
|
|
|
|
test('Jira IPC rejects a request without the document capability', () => {
|
|
const mainFrame = {};
|
|
const event = { senderFrame: mainFrame, sender: { mainFrame } };
|
|
const register = handlers.get(IPC.JIRA_REGISTER_CAPABILITY);
|
|
const makeRequest = handlers.get(IPC.JIRA_MAKE_REQUEST_EVENT);
|
|
register(event);
|
|
|
|
assert.throws(
|
|
() =>
|
|
makeRequest(event, {
|
|
capabilityToken: 'forged-token',
|
|
payload: { requestId: 'request-1' },
|
|
}),
|
|
/unauthorized/i,
|
|
);
|
|
});
|
|
|
|
test('Jira IPC unwraps an authorized request before validation', async () => {
|
|
const mainFrame = {};
|
|
const event = { senderFrame: mainFrame, sender: { mainFrame } };
|
|
const register = handlers.get(IPC.JIRA_REGISTER_CAPABILITY);
|
|
const makeRequest = handlers.get(IPC.JIRA_MAKE_REQUEST_EVENT);
|
|
const token = register(event);
|
|
|
|
const result = await makeRequest(event, {
|
|
capabilityToken: token,
|
|
payload: { requestId: 'request-1' },
|
|
});
|
|
|
|
assert.deepEqual(result, {
|
|
requestId: 'request-1',
|
|
error: { message: 'Invalid Jira URL' },
|
|
});
|
|
});
|
|
|
|
test('issuing a capability to a new renderer document revokes stale image auth', () => {
|
|
setupRequestHeadersForImages({
|
|
host: 'https://jira.example.com/jira',
|
|
userName: 'user',
|
|
password: 'pass',
|
|
usePAT: false,
|
|
});
|
|
|
|
const mainFrame = {};
|
|
const event = { senderFrame: mainFrame, sender: { mainFrame } };
|
|
const register = handlers.get(IPC.JIRA_REGISTER_CAPABILITY);
|
|
assert.equal(typeof register(event), 'string');
|
|
|
|
const requestHeaders = {};
|
|
applyJiraImageAuth(
|
|
'https://jira.example.com/jira/image.png',
|
|
requestHeaders,
|
|
'image',
|
|
);
|
|
assert.deepEqual(requestHeaders, {});
|
|
});
|