From 45657ba39ce4e20b16a0ee04826e6c6f2481c408 Mon Sep 17 00:00:00 2001 From: Ouyang Xingyuan Date: Tue, 14 Jul 2026 18:15:40 +0800 Subject: [PATCH] fix: refresh Security Seed before replication Reason: - A client that remains open during a remote database rebuild can retain the previous Security Seed and upload documents encrypted with the wrong key. Changes: - Fetch the remote Security Seed at every replication preflight instead of reusing the process cache. - Add a regression test and an unreleased change note for issue #1018. --- src/modules/core/ModuleReplicator.ts | 3 +- .../core/ModuleReplicator.unit.spec.ts | 48 +++++++++++++++++++ updates.md | 4 ++ 3 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/modules/core/ModuleReplicator.unit.spec.ts diff --git a/src/modules/core/ModuleReplicator.ts b/src/modules/core/ModuleReplicator.ts index 806a825f..13b1b1da 100644 --- a/src/modules/core/ModuleReplicator.ts +++ b/src/modules/core/ModuleReplicator.ts @@ -49,7 +49,8 @@ async function canReplicateWithPBKDF2( // Showing message is false: that because be shown here. (And it is a fatal error, no way to hide it). // tagged as network error at beginning for error filtering with NetworkWarningStyles const ensureMessage = `${MARK_LOG_NETWORK_ERROR}Failed to initialise the encryption key, preventing replication.`; - const ensureResult = await replicator.ensurePBKDF2Salt(currentSettings, showMessage, true); + // A remote database rebuild replaces the Security Seed while this process may still hold the previous one. + const ensureResult = await replicator.ensurePBKDF2Salt(currentSettings, showMessage, false); if (!ensureResult) { errorManager.showError(ensureMessage, showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO); return false; diff --git a/src/modules/core/ModuleReplicator.unit.spec.ts b/src/modules/core/ModuleReplicator.unit.spec.ts new file mode 100644 index 00000000..820fdd95 --- /dev/null +++ b/src/modules/core/ModuleReplicator.unit.spec.ts @@ -0,0 +1,48 @@ +import { describe, expect, it, vi } from "vitest"; +import { ModuleReplicator } from "./ModuleReplicator"; + +describe("ModuleReplicator", () => { + it("refreshes the remote Security Seed before replication", async () => { + const ensurePBKDF2Salt = vi.fn(async () => true); + let beforeReplicate: ((showMessage: boolean) => Promise) | undefined; + const addHandler = vi.fn((handler: (showMessage: boolean) => Promise, priority?: number) => { + if (priority === 20) { + beforeReplicate = handler; + } + }); + const services = { + API: { isOnline: true }, + replicator: { + onReplicatorInitialised: { addHandler: vi.fn() }, + getActiveReplicator: () => ({ ensurePBKDF2Salt }), + }, + setting: { currentSettings: () => ({}) }, + databaseEvents: { onDatabaseInitialised: { addHandler: vi.fn() } }, + appLifecycle: { onSettingLoaded: { addHandler: vi.fn() } }, + replication: { + parseSynchroniseResult: { addHandler: vi.fn() }, + onBeforeReplicate: { addHandler }, + onReplicationFailed: { addHandler: vi.fn() }, + }, + }; + const module = { + _unresolvedErrorManager: { + showError: vi.fn(), + clearError: vi.fn(), + }, + _onReplicatorInitialised: vi.fn(), + _everyOnDatabaseInitialized: vi.fn(), + _everyOnloadAfterLoadSettings: vi.fn(), + _parseReplicationResult: vi.fn(), + _everyBeforeReplicate: vi.fn(), + onReplicationFailed: vi.fn(), + }; + + ModuleReplicator.prototype.onBindFunction.call(module, {} as never, services as never); + expect(beforeReplicate).toBeDefined(); + + await beforeReplicate!(false); + + expect(ensurePBKDF2Salt).toHaveBeenCalledWith({}, false, false); + }); +}); diff --git a/updates.md b/updates.md index c0631ea8..07093fd0 100644 --- a/updates.md +++ b/updates.md @@ -5,6 +5,10 @@ The head note of 0.25 is now in [updates_old.md](https://github.com/vrtmrz/obsid ## Unreleased +### Fixed + +- Refresh the remote Security Seed before each replication, preventing a client that remained open during a remote database rebuild from uploading data encrypted with the previous seed (#1018). + ## 0.25.81 14th July, 2026