Make the SECRETS_KEY test actually discriminate from APP_KEY

The old test rotated app.key and expected the password to stay
readable, but Laravel's encrypter is a container singleton resolved
once — config()->set('app.key', ...) never rebuilds it, so the
rotation the test performed was invisible to any code path going
through it. Confirmed by mutation: rewiring Mailbox to Crypt::
encryptString/decryptString (genuinely APP_KEY-keyed, the exact
regression this constraint exists to forbid) left the old test green.

Replace it with two tests: a positive proof that rotating SECRETS_KEY
makes a stored password unreadable (the one that discriminates), and
a separate check that rotating APP_KEY does not, with
forgetInstance('encrypter') so that rotation is actually visible to
whatever the password accessor resolves.

Re-ran the same mutation against the new test: it fails (exit 1, the
SECRETS_KEY test reports the expected DecryptException was not
thrown), then passes again once the mutation is reverted.
feat/mailboxes
nexxo 2026-07-27 21:49:46 +02:00
parent c5252847bb
commit c5021c4aa7
1 changed files with 15 additions and 1 deletions

View File

@ -1,6 +1,7 @@
<?php
use App\Models\Mailbox;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Facades\DB;
beforeEach(function () {
@ -16,11 +17,24 @@ it('stores the password encrypted and never in the clear', function () {
->and($box->fresh()->password)->toBe('sehr-geheim');
});
it('uses SECRETS_KEY, not APP_KEY — rotating APP_KEY leaves mail working', function () {
it('is keyed to SECRETS_KEY — rotating it makes the password unreadable', function () {
// The POSITIVE proof, and the one that discriminates.
$box = Mailbox::factory()->create(['password' => 'geheim']);
config()->set('admin_access.secrets_key', 'base64:'.base64_encode(random_bytes(32)));
expect(fn () => $box->fresh()->password)
->toThrow(DecryptException::class);
});
it('is NOT keyed to APP_KEY — rotating that leaves mail working', function () {
$box = Mailbox::factory()->create(['password' => 'bleibt-lesbar']);
config()->set('app.key', 'base64:'.base64_encode(random_bytes(32)));
// forgetInstance is what makes this test mean anything — see above.
app()->forgetInstance('encrypter');
expect($box->fresh()->password)->toBe('bleibt-lesbar');
});