encrypt($secret); if ($ciphertext === $secret || $vault->decrypt($ciphertext) !== $secret) { throw new RuntimeException('Credentials must round-trip through authenticated encryption.'); } try { (new CredentialVault(base64_encode(random_bytes(32))))->decrypt($ciphertext); throw new RuntimeException('Decrypting with the wrong key should fail.'); } catch (RuntimeException $expected) { if (!str_contains($expected->getMessage(), 'decrypt')) { throw new RuntimeException('Wrong-key failure should be explicit.'); } } $masked = $vault->mask($secret); credential_assert_same('••••••••••••••••••••', $masked, 'Secrets must never be shown in plaintext.'); credential_assert_same('••••••••••••••••••••', $vault->display(['secret' => $secret])['secret'], 'Display must mask secret fields.'); $credential = [ 'id' => 7, 'category' => 'hosting', 'label' => ' Production Host ', 'username' => ' deploy ', 'notes' => ' SSH access ', 'secret' => $secret, 'internal_token' => 'do not expose', ]; $encrypted = $vault->encryptCredential($credential); if (array_key_exists('secret', $encrypted) || !isset($encrypted['secret_ciphertext'])) { throw new RuntimeException('Stored credential records must contain ciphertext, not plaintext.'); } credential_assert_same($secret, $vault->decryptCredential($encrypted)['secret'], 'Credential records must decrypt their secret.'); $projection = $vault->projectMetadata($encrypted); credential_assert_same(['id' => 7, 'category' => 'hosting', 'label' => 'Production Host', 'username' => 'deploy', 'notes' => 'SSH access'], $projection, 'Metadata projection must be allow-listed and plaintext-free.'); $info = new TechnicalInformation(); $valid = $info->validate(['category' => 'vpn', 'label' => ' Office VPN ', 'username' => ' alice ', 'notes' => ' MFA enabled ']); credential_assert_same(true, $valid['valid'], 'Valid technical information should pass.'); credential_assert_same('Office VPN', $valid['label'], 'Labels should be normalized.'); $invalid = $info->validate(['category' => 'unknown', 'label' => ' ', 'username' => "bad\nname", 'notes' => str_repeat('x', 2001)]); if ($invalid['valid'] || !isset($invalid['errors']['category'], $invalid['errors']['label'], $invalid['errors']['username'], $invalid['errors']['notes'])) { throw new RuntimeException('Technical information must validate category, label, username, and notes.'); } foreach (['missing' => null, 'short' => 'short', 'placeholder' => 'generate-a-long-random-secret'] as $name => $badKey) { try { new CredentialVault($badKey); throw new RuntimeException("{$name} APP_KEY material should fail explicitly."); } catch (RuntimeException $expected) { if (!str_contains($expected->getMessage(), 'APP_KEY')) { throw new RuntimeException("{$name} APP_KEY error should mention APP_KEY."); } } } printf("Credential vault tests: 6 passed\n");