diff --git a/src/electron-main/api/index.spec.ts b/src/electron-main/api/index.spec.ts index 30876a4d2..eac462216 100644 --- a/src/electron-main/api/index.spec.ts +++ b/src/electron-main/api/index.spec.ts @@ -244,6 +244,7 @@ const tests: Record) => Imple logout: async (t) => { const {deletePassword: deletePasswordSpy} = t.context.mocks["src/electron-main/keytar"]; + const {clearDefaultSessionCaches: clearDefaultSessionCachesSpy} = t.context.mocks["src/electron-main/session"]; const {endpoints} = t.context; const resetSpy = sinon.spy(t.context.ctx.db, "reset"); @@ -264,6 +265,7 @@ const tests: Record) => Imple t.is(deletePasswordSpy.callCount, 3); t.is(2, resetSpy.callCount); + t.is(2, clearDefaultSessionCachesSpy.callCount); }, openAboutWindow: async (t) => { @@ -468,6 +470,9 @@ async function buildMocks() { registerApi: sinon.spy(), }, } as any, + "src/electron-main/session": { + clearDefaultSessionCaches: sinon.stub().returns(Promise.resolve({})), + }, "src/electron-main/util": { buildSettingsAdapter, }, @@ -532,6 +537,7 @@ test.beforeEach(async (t) => { mock(() => import("src/electron-main/keytar"))/*.callThrough()*/.with(mocks["src/electron-main/keytar"]); mock(() => import("about-window")).callThrough().with(mocks["about-window"]); mock(() => import("src/shared/api/main")).callThrough().with(mocks["src/shared/api/main"]); + mock(() => import("src/electron-main/session")).callThrough().with(mocks["src/electron-main/session"]); mock(() => import("src/electron-main/util")).callThrough().with(mocks["src/electron-main/util"]); mock(() => import("src/electron-main/storage-upgrade")).callThrough().with(mocks["src/electron-main/storage-upgrade"]); mock(() => import("./endpoints-builders")).callThrough().with(mocks["./endpoints-builders"]); diff --git a/src/electron-main/api/index.ts b/src/electron-main/api/index.ts index 308fd1cb9..5736d891d 100644 --- a/src/electron-main/api/index.ts +++ b/src/electron-main/api/index.ts @@ -5,6 +5,7 @@ import {Account, Database, General, KeePass, TrayIcon} from "./endpoints-builder import {Context} from "src/electron-main/model"; import {Endpoints, IPC_MAIN_API} from "src/shared/api/main"; import {buildSettingsAdapter} from "src/electron-main/util"; +import {clearDefaultSessionCaches} from "src/electron-main/session"; import {deletePassword, getPassword, setPassword} from "src/electron-main/keytar"; import {upgradeConfig, upgradeSettings} from "src/electron-main/storage-upgrade"; @@ -46,6 +47,7 @@ export const initApi = async (ctx: Context): Promise => { await deletePassword(); ctx.settingsStore = ctx.settingsStore.clone({adapter: undefined}); ctx.db.reset(); + await clearDefaultSessionCaches(); return null; })()), diff --git a/src/electron-main/index.spec.ts b/src/electron-main/index.spec.ts index 5dcafacbb..cf903c950 100644 --- a/src/electron-main/index.spec.ts +++ b/src/electron-main/index.spec.ts @@ -29,7 +29,12 @@ test.serial("workflow", async (t) => { t.true(m.electron.app.makeSingleInstance.calledBefore(m["./util"].initContext), `"makeSingleInstance" called before "initContext"`); t.true(m["./util"].initContext.calledWithExactly(), `"initContext" called`); + + t.true(m["./session"].clearDefaultSessionCaches.calledWithExactly(), `"clearDefaultSessionCaches" called`); + t.true(m["./session"].clearDefaultSessionCaches.calledAfter(m["./util"].initContext), `"clearDefaultSessionCaches" called after "initContext"`); + t.true(m["./api"].initApi.calledWithExactly(t.context.ctx), `"initApi" called`); + t.true(m["./api"].initApi.calledAfter(m["./session"].clearDefaultSessionCaches), `"initApi" called after "clearDefaultSessionCaches"`); t.true(m["./web-content-context-menu"].initWebContentContextMenu.calledWithExactly(t.context.ctx), `"initWebContentContextMenu" called`); t.true(m["./web-content-context-menu"].initWebContentContextMenu.calledBefore(m["./window"].initBrowserWindow), `"initWebContentContextMenu" called before "initBrowserWindow"`); @@ -66,6 +71,7 @@ test.beforeEach(async (t) => { () => import("./index"), (mock) => { const mocks = t.context.mocks["~index"]; + mock(() => import("./session")).callThrough().with(mocks["./session"]); mock(() => import("./api")).callThrough().with(mocks["./api"]); mock(() => import("./util")).callThrough().with(mocks["./util"]); mock(() => import("./window")).callThrough().with(mocks["./window"]); @@ -86,6 +92,9 @@ test.beforeEach(async (t) => { function buildMocks(testContext: TestContext) { return { "~index": { + "./session": { + clearDefaultSessionCaches: sinon.stub().returns(Promise.resolve({})), + }, "./api": { initApi: sinon.stub().returns(Promise.resolve(testContext.endpoints)), }, diff --git a/src/electron-main/index.ts b/src/electron-main/index.ts index 0b753c0e1..789f3b3ab 100644 --- a/src/electron-main/index.ts +++ b/src/electron-main/index.ts @@ -3,6 +3,7 @@ import logger from "electron-log"; import {app} from "electron"; import {APP_NAME} from "src/shared/constants"; +import {clearDefaultSessionCaches} from "./session"; import {initApi} from "./api"; import {initAutoUpdate} from "./app-update"; import {initBrowserWindow} from "./window"; @@ -33,6 +34,8 @@ const secondInstanceExecutedCallbacks = (() => { const ctx = initContext(); app.on("ready", async () => { + await clearDefaultSessionCaches(); + const endpoints = await initApi(ctx); const {checkForUpdatesAndNotify} = await endpoints.readConfig().toPromise(); diff --git a/src/electron-main/session.ts b/src/electron-main/session.ts new file mode 100644 index 000000000..180d1481f --- /dev/null +++ b/src/electron-main/session.ts @@ -0,0 +1,15 @@ +import {session} from "electron"; + +export async function clearDefaultSessionCaches(): Promise { + const defaultSession = session.defaultSession; + + if (!defaultSession) { + throw new Error(`"session.defaultSession" is not defined`); + } + + await Promise.all([ + new Promise((resolve) => defaultSession.clearAuthCache({type: "password"}, resolve)), + new Promise((resolve) => defaultSession.clearCache(resolve)), + new Promise((resolve) => defaultSession.clearStorageData({}, resolve)), + ]); +}