From 01c0f8b64c7944a1602fd2f79d4ee66c213993ad Mon Sep 17 00:00:00 2001 From: Maurice Date: Thu, 29 Oct 2026 09:09:00 +0100 Subject: [PATCH] fix(server): rotate the stored service key with ENCRYPTION_KEY and pin every encrypted app setting --- server/scripts/migrate-encryption.ts | 5 +++++ server/tests/unit/db/migrate-encryption-parity.test.ts | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/server/scripts/migrate-encryption.ts b/server/scripts/migrate-encryption.ts index d39937dee..d5724be9b 100644 --- a/server/scripts/migrate-encryption.ts +++ b/server/scripts/migrate-encryption.ts @@ -295,6 +295,10 @@ async function main() { // web_push_vapid_private_key is the Web Push signing key VapidKeysService // generates. Missed here it would no longer decrypt under the new key, and // push would stay off on every device until the old key came back. + // + // service_key is the installation's key towards the liketrek services. + // Missed here it reads back as unusable, the installation registers anew and + // loses its id, and a revoked key would come back as a fresh one. for (const key of [ 'oidc_client_secret', 'smtp_pass', @@ -304,6 +308,7 @@ async function main() { 'unsplash_api_key', 'amap_api_key', 'web_push_vapid_private_key', + 'service_key', ]) { const row = db.prepare('SELECT value FROM app_settings WHERE key = ?').get(key) as { value: string } | undefined; if (!row?.value) continue; diff --git a/server/tests/unit/db/migrate-encryption-parity.test.ts b/server/tests/unit/db/migrate-encryption-parity.test.ts index cefb11692..fceec9ad1 100644 --- a/server/tests/unit/db/migrate-encryption-parity.test.ts +++ b/server/tests/unit/db/migrate-encryption-parity.test.ts @@ -10,6 +10,7 @@ * key, decrypt_api_key returns null on the next read, and the instance quietly * behaves as if no key were configured. This pins the copy instead. */ +import { ENCRYPTED_APP_SETTING_KEYS } from '../../../src/nest/common/app-settings.registry'; import { INSTANCE_API_KEY_NAMES } from '../../../src/nest/settings/instance-api-keys'; import { storageSecretFields } from '@trek/shared'; @@ -110,6 +111,14 @@ describe('migrate-encryption.ts app_settings parity', () => { expect(INSTANCE_API_KEY_NAMES.length).toBeGreaterThan(0); }); + // The register marks every app setting stored encrypted, the service key among them. + it('ROTPAR-015: rotates every app setting the register stores encrypted', () => { + expect(ENCRYPTED_APP_SETTING_KEYS.size).toBeGreaterThan(0); + for (const key of ENCRYPTED_APP_SETTING_KEYS) { + expect(appSettingsLoop).toContain(`'${key}'`); + } + }); + it('ROTPAR-004: rotates every storage.backends secret field for s3', () => { const fields = storageSecretFields('s3'); expect(fields.length).toBeGreaterThan(0);