diff --git a/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/column-rotation-site.handler.ts b/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/column-rotation-site.handler.ts index 6cfdcc5ea1..565b382f66 100644 --- a/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/column-rotation-site.handler.ts +++ b/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/column-rotation-site.handler.ts @@ -133,7 +133,7 @@ export class ColumnRotationSiteHandler< : undefined; try { - const plaintext = this.secretEncryptionService.decryptVersioned( + const plaintext = this.secretEncryptionService.decryptVersionedOrThrow( currentValue, cryptoOptions, ); diff --git a/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/connection-parameters-rotation.handler.ts b/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/connection-parameters-rotation.handler.ts index 26607d010f..5b14b7b614 100644 --- a/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/connection-parameters-rotation.handler.ts +++ b/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/connection-parameters-rotation.handler.ts @@ -16,11 +16,6 @@ import { type EncryptedImapSmtpCaldavParams, type ImapSmtpCaldavParams, } from 'src/engine/core-modules/imap-smtp-caldav-connection/types/imap-smtp-caldav-connection.type'; -import { SECRET_ENCRYPTION_ENVELOPE_V2_PREFIX } from 'src/engine/core-modules/secret-encryption/constants/secret-encryption.constant'; -import { - SecretEncryptionException, - SecretEncryptionExceptionCode, -} from 'src/engine/core-modules/secret-encryption/exceptions/secret-encryption.exception'; import { SecretEncryptionService } from 'src/engine/core-modules/secret-encryption/secret-encryption.service'; import { ConnectedAccountEntity } from 'src/engine/metadata-modules/connected-account/entities/connected-account.entity'; @@ -147,16 +142,7 @@ export class ConnectionParametersRotationHandler extends SecretEncryptionRotatio continue; } - // Refuse non-enc:v2 values up front: decryptVersioned would otherwise - // fall through to unauthenticated legacy CTR and corrupt the password. - if (!params.password.startsWith(SECRET_ENCRYPTION_ENVELOPE_V2_PREFIX)) { - throw new SecretEncryptionException( - `${protocol} password is not a versioned envelope (expected '${SECRET_ENCRYPTION_ENVELOPE_V2_PREFIX}…'), refusing to rotate.`, - SecretEncryptionExceptionCode.MALFORMED_ENVELOPE, - ); - } - - const plaintext = this.secretEncryptionService.decryptVersioned( + const plaintext = this.secretEncryptionService.decryptVersionedOrThrow( params.password, { workspaceId }, ); diff --git a/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/sensitive-config-storage-rotation.handler.ts b/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/sensitive-config-storage-rotation.handler.ts index 2feb50ceab..177ea1fa87 100644 --- a/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/sensitive-config-storage-rotation.handler.ts +++ b/packages/twenty-server/src/database/commands/secret-encryption-rotation/handlers/sensitive-config-storage-rotation.handler.ts @@ -118,7 +118,8 @@ export class SensitiveConfigStorageRotationHandler extends SecretEncryptionRotat } try { - const plaintext = this.secretEncryptionService.decryptVersioned(rawValue); + const plaintext = + this.secretEncryptionService.decryptVersionedOrThrow(rawValue); const reEncrypted = this.secretEncryptionService.encryptVersioned(plaintext); diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000005000-encrypt-application-variable.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000005000-encrypt-application-variable.ts index 0fb13b6a8a..a703653a63 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000005000-encrypt-application-variable.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000005000-encrypt-application-variable.ts @@ -85,10 +85,11 @@ export class EncryptApplicationVariableSlowInstanceCommand if (looksLikeLegacyCtrCiphertext(row.value)) { try { - plaintext = this.secretEncryptionService.decryptVersioned( - row.value as EncryptedString, - { workspaceId: row.workspaceId }, - ); + plaintext = + this.secretEncryptionService.legacyDecryptVersionedWithFallback( + row.value as EncryptedString, + { workspaceId: row.workspaceId }, + ); } catch (error) { this.logger.warn( `applicationVariable row ${row.id} value not valid ciphertext; treating as plaintext. ${ diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000006000-encrypt-application-registration-variable.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000006000-encrypt-application-registration-variable.ts index 9630798f56..cad38a8d2b 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000006000-encrypt-application-registration-variable.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000006000-encrypt-application-registration-variable.ts @@ -57,9 +57,10 @@ export class EncryptApplicationRegistrationVariableSlowInstanceCommand continue; } - const plaintext = this.secretEncryptionService.decryptVersioned( - row.encryptedValue as EncryptedString, - ); + const plaintext = + this.secretEncryptionService.legacyDecryptVersionedWithFallback( + row.encryptedValue as EncryptedString, + ); if (!isDefined(plaintext)) { continue; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000007000-encrypt-signing-key-private-keys.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000007000-encrypt-signing-key-private-keys.ts index fdc1e2c26c..b2b016526f 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000007000-encrypt-signing-key-private-keys.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000007000-encrypt-signing-key-private-keys.ts @@ -54,9 +54,10 @@ export class EncryptSigningKeyPrivateKeysSlowInstanceCommand implements SlowInst continue; } - const plaintext = this.secretEncryptionService.decryptVersioned( - row.privateKey as EncryptedString, - ); + const plaintext = + this.secretEncryptionService.legacyDecryptVersionedWithFallback( + row.privateKey as EncryptedString, + ); if (!isDefined(plaintext)) { continue; diff --git a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000008000-encrypt-sensitive-config-storage.ts b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000008000-encrypt-sensitive-config-storage.ts index 220e0fbb53..e8556797d1 100644 --- a/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000008000-encrypt-sensitive-config-storage.ts +++ b/packages/twenty-server/src/database/commands/upgrade-version-command/2-5/2-5-instance-command-slow-1798000008000-encrypt-sensitive-config-storage.ts @@ -57,9 +57,10 @@ export class EncryptSensitiveConfigStorageSlowInstanceCommand implements SlowIns continue; } - const plaintext = this.secretEncryptionService.decryptVersioned( - rawValue as EncryptedString, - ); + const plaintext = + this.secretEncryptionService.legacyDecryptVersionedWithFallback( + rawValue as EncryptedString, + ); if (!isDefined(plaintext)) { continue; diff --git a/packages/twenty-server/src/engine/core-modules/application/application-registration-variable/application-registration-variable.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-registration-variable/application-registration-variable.service.ts index 1747cc8df9..3703799095 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-registration-variable/application-registration-variable.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-registration-variable/application-registration-variable.service.ts @@ -255,7 +255,7 @@ export class ApplicationRegistrationVariableService { encryptedValue !== '' ? variable.isSecret ? '•••••••••••••' - : this.encryptionService.decryptVersioned(encryptedValue) + : this.encryptionService.decryptVersionedOrThrow(encryptedValue) : null, }; } diff --git a/packages/twenty-server/src/engine/core-modules/application/application-variable/__tests__/application-variable.service.spec.ts b/packages/twenty-server/src/engine/core-modules/application/application-variable/__tests__/application-variable.service.spec.ts index 3c4191b7f4..d4db112ca6 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-variable/__tests__/application-variable.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-variable/__tests__/application-variable.service.spec.ts @@ -44,7 +44,7 @@ describe('ApplicationVariableEntityService', () => { (value: string, opts?: { workspaceId?: string }) => `enc:v2:deadbeef:${value}|${opts?.workspaceId ?? 'instance'}`, ), - decryptVersioned: jest.fn( + decryptVersionedOrThrow: jest.fn( (value: string, _opts?: { workspaceId?: string }) => value.replace(/^enc:v2:[0-9a-f]+:/, '').replace(/\|.*$/, ''), ), diff --git a/packages/twenty-server/src/engine/core-modules/application/application-variable/application-variable.service.ts b/packages/twenty-server/src/engine/core-modules/application/application-variable/application-variable.service.ts index 8a36e22402..28c30d4b0d 100644 --- a/packages/twenty-server/src/engine/core-modules/application/application-variable/application-variable.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/application-variable/application-variable.service.ts @@ -36,7 +36,7 @@ export class ApplicationVariableEntityService { }); } - return this.secretEncryptionService.decryptVersioned( + return this.secretEncryptionService.decryptVersionedOrThrow( applicationVariable.value, { workspaceId: applicationVariable.workspaceId }, ); diff --git a/packages/twenty-server/src/engine/core-modules/application/connection-provider/connection-provider.service.ts b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connection-provider.service.ts index f847118bc2..0c076742bf 100644 --- a/packages/twenty-server/src/engine/core-modules/application/connection-provider/connection-provider.service.ts +++ b/packages/twenty-server/src/engine/core-modules/application/connection-provider/connection-provider.service.ts @@ -53,7 +53,9 @@ export class ConnectionProviderService { variables.map((v) => [ v.key, v.encryptedValue !== '' - ? this.secretEncryptionService.decryptVersioned(v.encryptedValue) + ? this.secretEncryptionService.decryptVersionedOrThrow( + v.encryptedValue, + ) : '', ]), ); diff --git a/packages/twenty-server/src/engine/core-modules/emailing-domain/services/unsubscribe-token.service.ts b/packages/twenty-server/src/engine/core-modules/emailing-domain/services/unsubscribe-token.service.ts index 7892fb12a7..6660c87cdf 100644 --- a/packages/twenty-server/src/engine/core-modules/emailing-domain/services/unsubscribe-token.service.ts +++ b/packages/twenty-server/src/engine/core-modules/emailing-domain/services/unsubscribe-token.service.ts @@ -26,7 +26,7 @@ export class UnsubscribeTokenService { verify(token: string): UnsubscribeTokenPayload | null { try { - const decrypted = this.secretEncryptionService.decryptVersioned( + const decrypted = this.secretEncryptionService.decryptVersionedOrThrow( Buffer.from(token, 'base64url').toString('utf8') as EncryptedString, ); diff --git a/packages/twenty-server/src/engine/core-modules/jwt/services/jwt-key-manager.service.ts b/packages/twenty-server/src/engine/core-modules/jwt/services/jwt-key-manager.service.ts index f08ffe14a9..8bbe68f550 100644 --- a/packages/twenty-server/src/engine/core-modules/jwt/services/jwt-key-manager.service.ts +++ b/packages/twenty-server/src/engine/core-modules/jwt/services/jwt-key-manager.service.ts @@ -191,7 +191,9 @@ export class JwtKeyManagerService { ); } - return this.secretEncryptionService.decryptVersioned(encryptedPrivateKey); + return this.secretEncryptionService.decryptVersionedOrThrow( + encryptedPrivateKey, + ); } private async generateAndPersistCurrent(): Promise { diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts index 624f4f5bd5..523767b2e9 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/logic-function-executor.service.ts @@ -359,9 +359,10 @@ export class LogicFunctionExecutorService { for (const variable of serverVariables) { if (variable.encryptedValue !== '') { - envMap[variable.key] = this.secretEncryptionService.decryptVersioned( - variable.encryptedValue, - ); + envMap[variable.key] = + this.secretEncryptionService.decryptVersionedOrThrow( + variable.encryptedValue, + ); } } diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/utils/__tests__/build-env-var.spec.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/utils/__tests__/build-env-var.spec.ts index c8cf288d27..bd10fe56b8 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/utils/__tests__/build-env-var.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/utils/__tests__/build-env-var.spec.ts @@ -12,7 +12,7 @@ describe('buildEnvVar', () => { (value: string, opts?: { workspaceId?: string }) => `enc:v2:deadbeef:${value}|${opts?.workspaceId ?? 'instance'}`, ), - decryptVersioned: jest.fn( + decryptVersionedOrThrow: jest.fn( (value: string, _opts?: { workspaceId?: string }) => value.replace(/^enc:v2:[0-9a-f]+:/, '').replace(/\|.*$/, ''), ), @@ -79,9 +79,9 @@ describe('buildEnvVar', () => { API_SECRET: 'secret-123', DEBUG: 'true', }); - expect(mockSecretEncryptionService.decryptVersioned).toHaveBeenCalledTimes( - 3, - ); + expect( + mockSecretEncryptionService.decryptVersionedOrThrow, + ).toHaveBeenCalledTimes(3); }); it('routes each secret variable to its own workspace HKDF context', () => { @@ -116,14 +116,16 @@ describe('buildEnvVar', () => { buildEnvVar(flatVariables, mockSecretEncryptionService); - expect(mockSecretEncryptionService.decryptVersioned).toHaveBeenCalledWith( - `enc:v2:deadbeef:value-a|${workspaceA}`, - { workspaceId: workspaceA }, - ); - expect(mockSecretEncryptionService.decryptVersioned).toHaveBeenCalledWith( - `enc:v2:deadbeef:value-b|${workspaceB}`, - { workspaceId: workspaceB }, - ); + expect( + mockSecretEncryptionService.decryptVersionedOrThrow, + ).toHaveBeenCalledWith(`enc:v2:deadbeef:value-a|${workspaceA}`, { + workspaceId: workspaceA, + }); + expect( + mockSecretEncryptionService.decryptVersionedOrThrow, + ).toHaveBeenCalledWith(`enc:v2:deadbeef:value-b|${workspaceB}`, { + workspaceId: workspaceB, + }); }); it('should handle null or undefined values', () => { diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/utils/build-env-var.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/utils/build-env-var.ts index bf82c85a89..58a862471c 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/utils/build-env-var.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-executor/utils/build-env-var.ts @@ -16,7 +16,7 @@ export const buildEnvVar = ( // the else branch into an invariant violation for non-empty values. acc[flatApplicationVariable.key] = isNonEmptyString(value) && isEncryptedString(value) - ? secretEncryptionService.decryptVersioned(value, { + ? secretEncryptionService.decryptVersionedOrThrow(value, { workspaceId: flatApplicationVariable.workspaceId, }) : value; diff --git a/packages/twenty-server/src/engine/core-modules/secret-encryption/secret-encryption.service.spec.ts b/packages/twenty-server/src/engine/core-modules/secret-encryption/secret-encryption.service.spec.ts index b8031e351d..2f87a1931c 100644 --- a/packages/twenty-server/src/engine/core-modules/secret-encryption/secret-encryption.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/secret-encryption/secret-encryption.service.spec.ts @@ -239,4 +239,49 @@ describe('SecretEncryptionService', () => { ).toBeUndefined(); }); }); + + describe('decryptVersionedOrThrow', () => { + it('round-trips a v2 envelope', () => { + const secret = 'sk-strict-secret-value'; + const encrypted = service.encryptVersioned(secret as PlaintextString); + + expect(service.decryptVersionedOrThrow(encrypted)).toBe(secret); + }); + + it('throws on a legacy non-v2 value instead of falling back to CTR', () => { + const legacyCiphertext = service.encrypt(testValue) as EncryptedString; + + expect(() => service.decryptVersionedOrThrow(legacyCiphertext)).toThrow(); + }); + + it('returns null/undefined values as-is', () => { + expect( + service.decryptVersionedOrThrow(null as unknown as EncryptedString), + ).toBeNull(); + expect( + service.decryptVersionedOrThrow( + undefined as unknown as EncryptedString, + ), + ).toBeUndefined(); + }); + }); + + describe('legacyDecryptVersionedWithFallback', () => { + it('round-trips a v2 envelope', () => { + const secret = 'sk-legacy-secret-value'; + const encrypted = service.encryptVersioned(secret as PlaintextString); + + expect(service.legacyDecryptVersionedWithFallback(encrypted)).toBe( + secret, + ); + }); + + it('falls back to legacy CTR decryption for non-v2 values', () => { + const legacyCiphertext = service.encrypt(testValue) as EncryptedString; + + expect(service.legacyDecryptVersionedWithFallback(legacyCiphertext)).toBe( + testValue, + ); + }); + }); }); diff --git a/packages/twenty-server/src/engine/core-modules/secret-encryption/secret-encryption.service.ts b/packages/twenty-server/src/engine/core-modules/secret-encryption/secret-encryption.service.ts index ff89ff7693..2d7f7cc0f1 100644 --- a/packages/twenty-server/src/engine/core-modules/secret-encryption/secret-encryption.service.ts +++ b/packages/twenty-server/src/engine/core-modules/secret-encryption/secret-encryption.service.ts @@ -4,6 +4,10 @@ import { isDefined } from 'twenty-shared/utils'; import { type EncryptedString } from 'src/engine/core-modules/secret-encryption/branded-strings/encrypted-string.type'; import { type PlaintextString } from 'src/engine/core-modules/secret-encryption/branded-strings/plaintext-string.type'; +import { + SecretEncryptionException, + SecretEncryptionExceptionCode, +} from 'src/engine/core-modules/secret-encryption/exceptions/secret-encryption.exception'; import { EnvironmentConfigDriver } from 'src/engine/core-modules/twenty-config/drivers/environment-config.driver'; import { computeEncryptionKeyId } from './utils/compute-encryption-key-id.util'; @@ -87,7 +91,7 @@ export class SecretEncryptionService { } return this.maskDecryptedValue( - this.decryptVersioned(value, { workspaceId }), + this.decryptVersionedOrThrow(value, { workspaceId }), mask, ); } @@ -127,7 +131,45 @@ export class SecretEncryptionService { }) as EncryptedString; } - public decryptVersioned( + public decryptVersionedOrThrow( + value: EncryptedString, + opts: VersionedOptions = {}, + ): PlaintextString { + if (!isDefined(value)) { + return value; + } + + const parsed = parseSecretEncryptionEnvelopeOrThrow({ value }); + + if (parsed.version !== 2) { + throw new SecretEncryptionException( + 'Expected an enc:v2 envelope but received a non-versioned value. The 2.5 encryption backfill instance commands must have run before this value can be decrypted.', + SecretEncryptionExceptionCode.UNKNOWN_ENVELOPE_VERSION, + ); + } + + const keys = resolveEncryptionKeysOrThrow({ + environmentConfigDriver: this.environmentConfigDriver, + }); + const rawKey = pickEncryptionKeyByKeyIdOrThrow({ + keyId: parsed.keyId, + keys, + }); + + return decryptAesGcmV2OrThrow({ + payloadBase64: parsed.payload, + rawKey, + workspaceId: opts.workspaceId, + }) as PlaintextString; + } + + /** + * @deprecated Legacy variant kept only for the 2.5 encryption backfill + * instance commands, which read pre-v2 rows (legacy AES-CTR ciphertext or + * plaintext) and re-encrypt them into the enc:v2 envelope. Runtime and + * rotation paths must use `decryptVersionedOrThrow` instead. + */ + public legacyDecryptVersionedWithFallback( value: EncryptedString, opts: VersionedOptions = {}, ): PlaintextString { diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/storage/__tests__/config-storage.service.spec.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/storage/__tests__/config-storage.service.spec.ts index c03da3e54e..50accfb4e3 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/storage/__tests__/config-storage.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/storage/__tests__/config-storage.service.spec.ts @@ -82,7 +82,7 @@ describe('ConfigStorageService', () => { { provide: SecretEncryptionService, useValue: { - decryptVersioned: jest.fn((value) => value), + decryptVersionedOrThrow: jest.fn((value) => value), encryptVersioned: jest.fn((value) => value), }, }, @@ -197,9 +197,9 @@ describe('ConfigStorageService', () => { const result = await service.get(key); expect(result).toBe(encryptedValue); - expect(secretEncryptionService.decryptVersioned).toHaveBeenCalledWith( - encryptedValue, - ); + expect( + secretEncryptionService.decryptVersionedOrThrow, + ).toHaveBeenCalledWith(encryptedValue); }); it('should handle decryption errors gracefully', async () => { @@ -572,9 +572,9 @@ describe('ConfigStorageService', () => { expect(result.get('NORMAL_CONFIG' as keyof ConfigVariables)).toBe( 'normal-value', ); - expect(secretEncryptionService.decryptVersioned).toHaveBeenCalledWith( - 'enc:v2:deadbeef:sensitive-value', - ); + expect( + secretEncryptionService.decryptVersionedOrThrow, + ).toHaveBeenCalledWith('enc:v2:deadbeef:sensitive-value'); }); }); diff --git a/packages/twenty-server/src/engine/core-modules/twenty-config/storage/config-storage.service.ts b/packages/twenty-server/src/engine/core-modules/twenty-config/storage/config-storage.service.ts index 7df6d4ca47..5ee8df107d 100644 --- a/packages/twenty-server/src/engine/core-modules/twenty-config/storage/config-storage.service.ts +++ b/packages/twenty-server/src/engine/core-modules/twenty-config/storage/config-storage.service.ts @@ -76,7 +76,7 @@ export class ConfigStorageService implements ConfigStorageInterface { this.isSensitiveStringValue(convertedValue, key) && isEncryptedString(convertedValue) ) { - return this.secretEncryptionService.decryptVersioned( + return this.secretEncryptionService.decryptVersionedOrThrow( convertedValue, ) as unknown as ConfigVariables[T]; } diff --git a/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.module.ts b/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.module.ts index eeefe4c46e..e10d915d68 100644 --- a/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.module.ts +++ b/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.module.ts @@ -3,7 +3,6 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { TokenModule } from 'src/engine/core-modules/auth/token/token.module'; import { WorkspaceDomainsModule } from 'src/engine/core-modules/domain/workspace-domains/workspace-domains.module'; -import { JwtModule } from 'src/engine/core-modules/jwt/jwt.module'; import { MetricsModule } from 'src/engine/core-modules/metrics/metrics.module'; import { SecretEncryptionModule } from 'src/engine/core-modules/secret-encryption/secret-encryption.module'; import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; @@ -15,7 +14,6 @@ import { TwoFactorAuthenticationResolver } from './two-factor-authentication.res import { TwoFactorAuthenticationService } from './two-factor-authentication.service'; import { TwoFactorAuthenticationMethodEntity } from './entities/two-factor-authentication-method.entity'; -import { SimpleSecretEncryptionUtil } from './utils/simple-secret-encryption.util'; @Module({ imports: [ @@ -23,9 +21,6 @@ import { SimpleSecretEncryptionUtil } from './utils/simple-secret-encryption.uti WorkspaceDomainsModule, MetricsModule, TokenModule, - // JwtModule is required by the deprecated SimpleSecretEncryptionUtil; drop - // it together with the util once the 2.5 cross-upgrade window closes. - JwtModule, SecretEncryptionModule, TypeOrmModule.forFeature([ UserEntity, @@ -37,7 +32,6 @@ import { SimpleSecretEncryptionUtil } from './utils/simple-secret-encryption.uti providers: [ TwoFactorAuthenticationService, TwoFactorAuthenticationResolver, - SimpleSecretEncryptionUtil, provideWorkspaceScopedRepository(TwoFactorAuthenticationMethodEntity), ], exports: [TwoFactorAuthenticationService], diff --git a/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.service.spec.ts b/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.service.spec.ts index df1e36c1b0..b4b5c6ceeb 100644 --- a/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.service.spec.ts @@ -18,7 +18,6 @@ import { TwoFactorAuthenticationService } from './two-factor-authentication.serv import { TwoFactorAuthenticationMethodEntity } from './entities/two-factor-authentication-method.entity'; import { OTPStatus } from './strategies/otp/otp.constants'; -import { SimpleSecretEncryptionUtil } from './utils/simple-secret-encryption.util'; const V2_ENVELOPE_PREFIX = 'enc:v2:'; @@ -60,7 +59,6 @@ describe('TwoFactorAuthenticationService', () => { let repository: any; let userWorkspaceService: any; let secretEncryptionService: any; - let simpleSecretEncryptionUtil: any; const mockUser = { id: 'user_123', email: 'test@example.com' }; const workspace = { id: 'ws_123', displayName: 'Test Workspace' }; @@ -71,7 +69,6 @@ describe('TwoFactorAuthenticationService', () => { const rawSecret = 'RAW_OTP_SECRET'; const encryptedSecret = `${V2_ENVELOPE_PREFIX}abcdef12:payload`; - const legacyCbcSecret = '0123456789abcdef0123456789abcdef:cafebabe'; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -96,13 +93,7 @@ describe('TwoFactorAuthenticationService', () => { provide: SecretEncryptionService, useValue: { encryptVersioned: jest.fn(), - decryptVersioned: jest.fn(), - }, - }, - { - provide: SimpleSecretEncryptionUtil, - useValue: { - decryptSecret: jest.fn(), + decryptVersionedOrThrow: jest.fn(), }, }, ], @@ -119,9 +110,6 @@ describe('TwoFactorAuthenticationService', () => { secretEncryptionService = module.get( SecretEncryptionService, ); - simpleSecretEncryptionUtil = module.get( - SimpleSecretEncryptionUtil, - ); jest.clearAllMocks(); }); @@ -295,7 +283,9 @@ describe('TwoFactorAuthenticationService', () => { }; repository.findOne.mockResolvedValue(existingMethod); - secretEncryptionService.decryptVersioned.mockReturnValue(rawSecret); + secretEncryptionService.decryptVersionedOrThrow.mockReturnValue( + rawSecret, + ); const expectedUri = 'otpauth://totp/test@example.com?secret=RAW_OTP_SECRET&issuer=Twenty%20-%20Test%20Workspace'; @@ -308,47 +298,14 @@ describe('TwoFactorAuthenticationService', () => { ); expect(uri).toBe(expectedUri); - expect(secretEncryptionService.decryptVersioned).toHaveBeenCalledWith( - encryptedSecret, - { workspaceId: workspace.id }, - ); - expect(simpleSecretEncryptionUtil.decryptSecret).not.toHaveBeenCalled(); + expect( + secretEncryptionService.decryptVersionedOrThrow, + ).toHaveBeenCalledWith(encryptedSecret, { workspaceId: workspace.id }); // Should not create new method or call initiate expect(totpStrategyMocks.initiate).not.toHaveBeenCalled(); expect(repository.save).not.toHaveBeenCalled(); }); - it('falls back to SimpleSecretEncryptionUtil when the stored secret is in the legacy AES-CBC format', async () => { - const recentTime = new Date(Date.now() - 5 * 60 * 1000); - const existingMethod = { - id: 'existing_method_id', - status: 'PENDING', - secret: legacyCbcSecret, - createdAt: recentTime, - }; - - repository.findOne.mockResolvedValue(existingMethod); - simpleSecretEncryptionUtil.decryptSecret.mockResolvedValue(rawSecret); - - const uri = await service.initiateStrategyConfiguration( - mockUser.id, - mockUser.email, - workspace.id, - workspace.displayName, - ); - - expect(uri).toBe( - 'otpauth://totp/test@example.com?secret=RAW_OTP_SECRET&issuer=Twenty%20-%20Test%20Workspace', - ); - expect(simpleSecretEncryptionUtil.decryptSecret).toHaveBeenCalledWith( - legacyCbcSecret, - `${mockUser.id}${workspace.id}otp-secret`, - ); - expect(secretEncryptionService.decryptVersioned).not.toHaveBeenCalled(); - expect(totpStrategyMocks.initiate).not.toHaveBeenCalled(); - expect(repository.save).not.toHaveBeenCalled(); - }); - it('should create new method when existing pending method is too old', async () => { const oldTime = new Date(Date.now() - 2 * 60 * 60 * 1000); const existingMethod = { @@ -398,7 +355,7 @@ describe('TwoFactorAuthenticationService', () => { repository.findOne.mockResolvedValue(existingMethod); const decryptionError = new Error('Decryption failed'); - secretEncryptionService.decryptVersioned.mockImplementation(() => { + secretEncryptionService.decryptVersionedOrThrow.mockImplementation(() => { throw decryptionError; }); @@ -463,7 +420,9 @@ describe('TwoFactorAuthenticationService', () => { it('should successfully validate a valid token', async () => { repository.findOne.mockResolvedValue(mock2FAMethod); - secretEncryptionService.decryptVersioned.mockReturnValue(rawSecret); + secretEncryptionService.decryptVersionedOrThrow.mockReturnValue( + rawSecret, + ); totpStrategyMocks.validate.mockReturnValue({ isValid: true, @@ -477,11 +436,9 @@ describe('TwoFactorAuthenticationService', () => { TwoFactorAuthenticationStrategy.TOTP, ); - expect(secretEncryptionService.decryptVersioned).toHaveBeenCalledWith( - encryptedSecret, - { workspaceId: workspace.id }, - ); - expect(simpleSecretEncryptionUtil.decryptSecret).not.toHaveBeenCalled(); + expect( + secretEncryptionService.decryptVersionedOrThrow, + ).toHaveBeenCalledWith(encryptedSecret, { workspaceId: workspace.id }); expect(totpStrategyMocks.validate).toHaveBeenCalledWith(otpToken, { status: mock2FAMethod.status, secret: rawSecret, @@ -495,36 +452,11 @@ describe('TwoFactorAuthenticationService', () => { ); }); - it('dispatches to SimpleSecretEncryptionUtil for legacy AES-CBC secrets', async () => { - const legacyMethod = { - ...mock2FAMethod, - secret: legacyCbcSecret, - }; - - repository.findOne.mockResolvedValue(legacyMethod); - simpleSecretEncryptionUtil.decryptSecret.mockResolvedValue(rawSecret); - totpStrategyMocks.validate.mockReturnValue({ - isValid: true, - context: { status: legacyMethod.status, secret: rawSecret }, - }); - - await service.validateStrategy( - mockUser.id, - otpToken, - workspace.id, - TwoFactorAuthenticationStrategy.TOTP, - ); - - expect(simpleSecretEncryptionUtil.decryptSecret).toHaveBeenCalledWith( - legacyCbcSecret, - `${mockUser.id}${workspace.id}otp-secret`, - ); - expect(secretEncryptionService.decryptVersioned).not.toHaveBeenCalled(); - }); - it('should throw if the token is invalid', async () => { repository.findOne.mockResolvedValue(mock2FAMethod); - secretEncryptionService.decryptVersioned.mockReturnValue(rawSecret); + secretEncryptionService.decryptVersionedOrThrow.mockReturnValue( + rawSecret, + ); totpStrategyMocks.validate.mockReturnValue({ isValid: false, context: mock2FAMethod, @@ -587,7 +519,7 @@ describe('TwoFactorAuthenticationService', () => { it('should handle secret decryption errors', async () => { repository.findOne.mockResolvedValue(mock2FAMethod); - secretEncryptionService.decryptVersioned.mockImplementation(() => { + secretEncryptionService.decryptVersionedOrThrow.mockImplementation(() => { throw new Error('Secret decryption failed'); }); @@ -614,7 +546,9 @@ describe('TwoFactorAuthenticationService', () => { it('should successfully verify and return success', async () => { repository.findOne.mockResolvedValue(mock2FAMethod); - secretEncryptionService.decryptVersioned.mockReturnValue(rawSecret); + secretEncryptionService.decryptVersionedOrThrow.mockReturnValue( + rawSecret, + ); totpStrategyMocks.validate.mockReturnValue({ isValid: true, @@ -644,7 +578,9 @@ describe('TwoFactorAuthenticationService', () => { it('should throw if the token is invalid', async () => { repository.findOne.mockResolvedValue(mock2FAMethod); - secretEncryptionService.decryptVersioned.mockReturnValue(rawSecret); + secretEncryptionService.decryptVersionedOrThrow.mockReturnValue( + rawSecret, + ); totpStrategyMocks.validate.mockReturnValue({ isValid: false, context: mock2FAMethod, diff --git a/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.service.ts b/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.service.ts index 80e56df3d8..0681cab32c 100644 --- a/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.service.ts +++ b/packages/twenty-server/src/engine/core-modules/two-factor-authentication/two-factor-authentication.service.ts @@ -10,7 +10,6 @@ import { } from 'src/engine/core-modules/auth/auth.exception'; import { type EncryptedString } from 'src/engine/core-modules/secret-encryption/branded-strings/encrypted-string.type'; import { type PlaintextString } from 'src/engine/core-modules/secret-encryption/branded-strings/plaintext-string.type'; -import { SECRET_ENCRYPTION_ENVELOPE_V2_PREFIX } from 'src/engine/core-modules/secret-encryption/constants/secret-encryption.constant'; import { SecretEncryptionService } from 'src/engine/core-modules/secret-encryption/secret-encryption.service'; import { UserEntity } from 'src/engine/core-modules/user/user.entity'; import { TwoFactorAuthenticationMethodEntity } from 'src/engine/core-modules/two-factor-authentication/entities/two-factor-authentication-method.entity'; @@ -28,19 +27,9 @@ import { import { twoFactorAuthenticationMethodsValidator } from './two-factor-authentication.validation'; import { OTPStatus } from './strategies/otp/otp.constants'; -import { SimpleSecretEncryptionUtil } from './utils/simple-secret-encryption.util'; const PENDING_METHOD_REUSE_WINDOW_MS = 60 * 60 * 1000; -// TODO: drop this helper, the `simpleSecretEncryptionUtil` dep, and the legacy -// branch in `decryptStoredSecret` below once the 2.5 cross-upgrade window -// closes and every TOTP secret row has been backfilled to enc:v2 by the -// matching slow instance command. -const buildLegacyTotpCbcPurpose = ( - userId: string, - workspaceId: string, -): string => `${userId}${workspaceId}otp-secret`; - @Injectable() // oxlint-disable-next-line twenty/inject-workspace-repository export class TwoFactorAuthenticationService { @@ -49,28 +38,18 @@ export class TwoFactorAuthenticationService { private readonly twoFactorAuthenticationMethodRepository: WorkspaceScopedRepository, private readonly userWorkspaceService: UserWorkspaceService, private readonly secretEncryptionService: SecretEncryptionService, - private readonly simpleSecretEncryptionUtil: SimpleSecretEncryptionUtil, ) {} private async decryptStoredSecret({ storedSecret, - userId, workspaceId, }: { storedSecret: EncryptedString; - userId: string; workspaceId: string; }): Promise { - if (storedSecret.startsWith(SECRET_ENCRYPTION_ENVELOPE_V2_PREFIX)) { - return this.secretEncryptionService.decryptVersioned(storedSecret, { - workspaceId, - }); - } - - return this.simpleSecretEncryptionUtil.decryptSecret( - storedSecret, - buildLegacyTotpCbcPurpose(userId, workspaceId), - ); + return this.secretEncryptionService.decryptVersionedOrThrow(storedSecret, { + workspaceId, + }); } /** @@ -141,7 +120,6 @@ export class TwoFactorAuthenticationService { ) { const existingSecret = await this.decryptStoredSecret({ storedSecret: existing2FAMethod.secret, - userId, workspaceId, }); @@ -207,7 +185,6 @@ export class TwoFactorAuthenticationService { const originalSecret = await this.decryptStoredSecret({ storedSecret: userTwoFactorAuthenticationMethod.secret, - userId, workspaceId, }); diff --git a/packages/twenty-server/src/engine/metadata-modules/connected-account/services/connected-account-token-encryption.service.ts b/packages/twenty-server/src/engine/metadata-modules/connected-account/services/connected-account-token-encryption.service.ts index 4837f5fa77..c7d41aaf3b 100644 --- a/packages/twenty-server/src/engine/metadata-modules/connected-account/services/connected-account-token-encryption.service.ts +++ b/packages/twenty-server/src/engine/metadata-modules/connected-account/services/connected-account-token-encryption.service.ts @@ -1,4 +1,4 @@ -import { Injectable, Logger } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { isDefined } from 'twenty-shared/utils'; @@ -20,10 +20,6 @@ import { ACCOUNT_TYPES } from 'twenty-shared/constants'; @Injectable() export class ConnectedAccountTokenEncryptionService { - private readonly logger = new Logger( - ConnectedAccountTokenEncryptionService.name, - ); - constructor( private readonly secretEncryptionService: SecretEncryptionService, ) {} @@ -75,7 +71,7 @@ export class ConnectedAccountTokenEncryptionService { ); } - return this.secretEncryptionService.decryptVersioned(ciphertext, { + return this.secretEncryptionService.decryptVersionedOrThrow(ciphertext, { workspaceId, }); } @@ -179,30 +175,6 @@ export class ConnectedAccountTokenEncryptionService { protocolParams: EncryptedConnectionParameters; workspaceId: string; }): PlaintextConnectionParameters { - const isEncrypted = protocolParams.password.startsWith( - SECRET_ENCRYPTION_ENVELOPE_PREFIX, - ); - - // TODO: Remove in follow-up PR once all legacy encryption fallbacks are dropped. - // TODO: Remove after 2-5 slow instance command has been run everywhere. - // During the rollout window protocolParams.password may be a legacy - // unencrypted plaintext value living in the same column. We trust the - // entity-level brand at the type layer (column is EncryptedString) but - // still re-validate at runtime to handle the un-backfilled tail; the - // assert above splits the two. - if (!isEncrypted) { - this.logger.warn( - 'Protocol password is not encrypted. Expected during the rollout window until the slow instance command finishes backfilling.', - ); - - const rawPassword: string = protocolParams.password; - - return { - ...protocolParams, - password: rawPassword as PlaintextString, - }; - } - return { ...protocolParams, password: this.decrypt({ diff --git a/packages/twenty-server/test/integration/secret-encryption/application-registration-variable-encryption.integration-spec.ts b/packages/twenty-server/test/integration/secret-encryption/application-registration-variable-encryption.integration-spec.ts index 0e48f3075d..7597f7efbd 100644 --- a/packages/twenty-server/test/integration/secret-encryption/application-registration-variable-encryption.integration-spec.ts +++ b/packages/twenty-server/test/integration/secret-encryption/application-registration-variable-encryption.integration-spec.ts @@ -135,7 +135,7 @@ describe('ApplicationRegistrationVariable encryption (integration)', () => { expect(variable.value).toBe(plaintext); }); - describe('legacy CTR fallback', () => { + describe('legacy CTR values are rejected at runtime', () => { let legacyVariableId: string; beforeAll(async () => { @@ -156,7 +156,7 @@ describe('ApplicationRegistrationVariable encryption (integration)', () => { ); }); - it('decrypts a legacy CTR-encrypted value through the live API', async () => { + it('rejects a legacy CTR-encrypted value through the live API', async () => { legacyVariableId = crypto.randomUUID(); const plaintext = 'legacy-ctr-registration-variable-secret'; @@ -189,16 +189,8 @@ describe('ApplicationRegistrationVariable encryption (integration)', () => { variables: { applicationRegistrationId }, }); - expect(findResponse.body.errors).toBeUndefined(); - - const variable = - findResponse.body.data.findApplicationRegistrationVariables.find( - (v: { id: string }) => v.id === legacyVariableId, - ); - - expect(variable).toBeDefined(); - expect(variable.isSecret).toBe(false); - expect(variable.value).toBe(plaintext); + expect(findResponse.body.errors).toBeDefined(); + expect(findResponse.body.errors[0].message).toContain('enc:v2 envelope'); }); }); }); diff --git a/packages/twenty-server/test/integration/secret-encryption/application-variable-encryption.integration-spec.ts b/packages/twenty-server/test/integration/secret-encryption/application-variable-encryption.integration-spec.ts index 2fd42d190c..ab7cda9d83 100644 --- a/packages/twenty-server/test/integration/secret-encryption/application-variable-encryption.integration-spec.ts +++ b/packages/twenty-server/test/integration/secret-encryption/application-variable-encryption.integration-spec.ts @@ -243,7 +243,7 @@ describe('ApplicationVariable encryption (integration)', () => { expect(variable.value).toBe(plaintext); }); - describe('legacy CTR fallback', () => { + describe('legacy CTR values are rejected at runtime', () => { beforeAll(async () => { await dataSource.query( `ALTER TABLE core."applicationVariable" @@ -264,7 +264,7 @@ describe('ApplicationVariable encryption (integration)', () => { ); }); - it('decrypts a legacy CTR-encrypted value through the live API read path', async () => { + it('rejects a legacy CTR-encrypted value through the live API read path', async () => { const plaintext = 'legacy-ctr-application-variable-secret-value-here'; await dataSource.query( @@ -293,16 +293,8 @@ describe('ApplicationVariable encryption (integration)', () => { variables: { id: applicationId }, }); - expect(findResponse.body.errors).toBeUndefined(); - - const variable = - findResponse.body.data.findOneApplication.applicationVariables.find( - (v: { key: string }) => v.key === LEGACY_VARIABLE_KEY, - ); - - expect(variable).toBeDefined(); - expect(variable.isSecret).toBe(true); - expect(variable.value).toBe(buildExpectedMask(plaintext)); + expect(findResponse.body.errors).toBeDefined(); + expect(findResponse.body.errors[0].message).toContain('enc:v2 envelope'); }); }); }); diff --git a/packages/twenty-server/test/integration/secret-encryption/connection-parameters-rotation.integration-spec.ts b/packages/twenty-server/test/integration/secret-encryption/connection-parameters-rotation.integration-spec.ts index e5525574f5..b0cd44159a 100644 --- a/packages/twenty-server/test/integration/secret-encryption/connection-parameters-rotation.integration-spec.ts +++ b/packages/twenty-server/test/integration/secret-encryption/connection-parameters-rotation.integration-spec.ts @@ -54,9 +54,12 @@ const expectAllPasswordsDecryptTo = ({ expect(params).toBeDefined(); expect(params?.password).toMatch(V2_ENVELOPE_REGEX); - const decrypted = secretEncryption.decryptVersioned(params!.password, { - workspaceId: row.workspaceId, - }); + const decrypted = secretEncryption.decryptVersionedOrThrow( + params!.password, + { + workspaceId: row.workspaceId, + }, + ); expect(decrypted).toBe(expectedPlaintextByProtocol[protocol]); } diff --git a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000005000-encrypt-application-variable.integration-spec.ts b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000005000-encrypt-application-variable.integration-spec.ts index 275b50a0d5..82cbcf28ac 100644 --- a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000005000-encrypt-application-variable.integration-spec.ts +++ b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000005000-encrypt-application-variable.integration-spec.ts @@ -147,7 +147,9 @@ describe('2-5 slow instance command 1798000005000 - EncryptApplicationVariableSl true, ); expect( - secretEncryptionService.decryptVersioned(row.value, { workspaceId }), + secretEncryptionService.decryptVersionedOrThrow(row.value, { + workspaceId, + }), ).toBe(plaintext); }); @@ -181,7 +183,9 @@ describe('2-5 slow instance command 1798000005000 - EncryptApplicationVariableSl true, ); expect( - secretEncryptionService.decryptVersioned(row.value, { workspaceId }), + secretEncryptionService.decryptVersionedOrThrow(row.value, { + workspaceId, + }), ).toBe(plaintext); }); diff --git a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000006000-encrypt-application-registration-variable.integration-spec.ts b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000006000-encrypt-application-registration-variable.integration-spec.ts index 88f65bc24b..d0794744d1 100644 --- a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000006000-encrypt-application-registration-variable.integration-spec.ts +++ b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000006000-encrypt-application-registration-variable.integration-spec.ts @@ -157,9 +157,9 @@ describe('2-5 slow instance command 1798000006000 - EncryptApplicationRegistrati expect( row.encryptedValue.startsWith(SECRET_ENCRYPTION_ENVELOPE_V2_PREFIX), ).toBe(true); - expect(secretEncryptionService.decryptVersioned(row.encryptedValue)).toBe( - plaintext, - ); + expect( + secretEncryptionService.decryptVersionedOrThrow(row.encryptedValue), + ).toBe(plaintext); }); it('leaves unfilled rows (encryptedValue = "") untouched', async () => { diff --git a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000007000-encrypt-signing-key-private-keys.integration-spec.ts b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000007000-encrypt-signing-key-private-keys.integration-spec.ts index d731fcf6bb..aed1145fd1 100644 --- a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000007000-encrypt-signing-key-private-keys.integration-spec.ts +++ b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000007000-encrypt-signing-key-private-keys.integration-spec.ts @@ -115,9 +115,9 @@ describe('2-5 slow instance command 1798000007000 - EncryptSigningKeyPrivateKeys expect( row.privateKey.startsWith(SECRET_ENCRYPTION_ENVELOPE_V2_PREFIX), ).toBe(true); - expect(secretEncryptionService.decryptVersioned(row.privateKey)).toBe( - plaintextPem, - ); + expect( + secretEncryptionService.decryptVersionedOrThrow(row.privateKey), + ).toBe(plaintextPem); }); it('leaves NULL private keys untouched (revoked / rotated keys)', async () => { diff --git a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000008000-encrypt-sensitive-config-storage.integration-spec.ts b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000008000-encrypt-sensitive-config-storage.integration-spec.ts index 2e6cdd03b2..5b38ea9c2e 100644 --- a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000008000-encrypt-sensitive-config-storage.integration-spec.ts +++ b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000008000-encrypt-sensitive-config-storage.integration-spec.ts @@ -103,7 +103,7 @@ describe('2-5 slow instance command 1798000008000 - EncryptSensitiveConfigStorag expect(value.startsWith(SECRET_ENCRYPTION_ENVELOPE_V2_PREFIX)).toBe(true); expect( - secretEncryptionService.decryptVersioned( + secretEncryptionService.decryptVersionedOrThrow( value as EncryptedString, ), ).toBe(plaintext); diff --git a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000009000-encrypt-totp-secrets.integration-spec.ts b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000009000-encrypt-totp-secrets.integration-spec.ts index e603357f43..a413887ac5 100644 --- a/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000009000-encrypt-totp-secrets.integration-spec.ts +++ b/packages/twenty-server/test/integration/upgrade/suites/2-5-instance-command-slow-1798000009000-encrypt-totp-secrets.integration-spec.ts @@ -190,7 +190,9 @@ describe('2-5 slow instance command 1798000009000 - EncryptTotpSecretsSlowInstan true, ); expect( - secretEncryptionService.decryptVersioned(row.secret, { workspaceId }), + secretEncryptionService.decryptVersionedOrThrow(row.secret, { + workspaceId, + }), ).toBe(plaintext); });