diff --git a/database/migrations/2026_06_20_105609_align_accounts_encrypted_flag_with_plaintext_names.php b/database/migrations/2026_06_20_105609_align_accounts_encrypted_flag_with_plaintext_names.php new file mode 100644 index 00000000..6021920b --- /dev/null +++ b/database/migrations/2026_06_20_105609_align_accounts_encrypted_flag_with_plaintext_names.php @@ -0,0 +1,35 @@ +where('encrypted', true) + ->whereNull('name_iv') + ->update(['encrypted' => false]); + } + + /** + * Irreversible: once flipped, a migration-corrected account is + * indistinguishable from an account that was always plaintext, so blanket + * re-flagging would wrongly encrypt legitimately unencrypted accounts. + */ + public function down(): void + { + // + } +}; diff --git a/tests/Feature/AlignAccountsEncryptedFlagMigrationTest.php b/tests/Feature/AlignAccountsEncryptedFlagMigrationTest.php new file mode 100644 index 00000000..70bd3566 --- /dev/null +++ b/tests/Feature/AlignAccountsEncryptedFlagMigrationTest.php @@ -0,0 +1,21 @@ +up(); +} + +it('clears the encrypted flag only for accounts whose name is plaintext', function () { + $staleFlag = Account::factory()->create(['encrypted' => true, 'name_iv' => null]); + $encryptedName = Account::factory()->create(['encrypted' => true, 'name_iv' => str_repeat('a', 16)]); + $alreadyPlaintext = Account::factory()->create(['encrypted' => false, 'name_iv' => null]); + + runAlignEncryptedFlagMigration(); + + expect($staleFlag->fresh()->encrypted)->toBeFalse() + ->and($encryptedName->fresh()->encrypted)->toBeTrue() + ->and($alreadyPlaintext->fresh()->encrypted)->toBeFalse(); +});