veza/veza-backend-api/internal/services/crypto_service_test.go
senke 6823e5a30d
Some checks failed
Backend API CI / test-unit (push) Failing after 0s
Backend API CI / test-integration (push) Failing after 0s
release(v0.902): Sentinel - PKCE OAuth, token encryption, redirect validation, CHAT_JWT_SECRET
- PKCE (S256) in OAuth flow: code_verifier in oauth_states, code_challenge in auth URL
- CryptoService: AES-256-GCM encryption for OAuth provider tokens at rest
- OAuth redirect URL validated against OAUTH_ALLOWED_REDIRECT_DOMAINS
- CHAT_JWT_SECRET must differ from JWT_SECRET in production
- Migration script: cmd/tools/encrypt_oauth_tokens for existing tokens
- Fixes: VEZA-SEC-003, VEZA-SEC-004, VEZA-SEC-009, VEZA-SEC-010
2026-02-26 19:49:15 +01:00

161 lines
3.8 KiB
Go

package services
import (
"encoding/base64"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewCryptoService_InvalidKey(t *testing.T) {
_, err := NewCryptoService([]byte("short"))
require.Error(t, err)
assert.Contains(t, err.Error(), "32 bytes")
}
func TestNewCryptoService_ValidKey(t *testing.T) {
key := make([]byte, 32)
for i := range key {
key[i] = byte(i)
}
svc, err := NewCryptoService(key)
require.NoError(t, err)
assert.NotNil(t, svc)
}
func TestCryptoService_EncryptDecrypt_Roundtrip(t *testing.T) {
key := make([]byte, 32)
for i := range key {
key[i] = byte(i)
}
svc, err := NewCryptoService(key)
require.NoError(t, err)
plaintext := []byte("sensitive-oauth-token-value")
enc, err := svc.Encrypt(plaintext)
require.NoError(t, err)
assert.NotEqual(t, plaintext, enc)
assert.NotEmpty(t, enc)
dec, err := svc.Decrypt(enc)
require.NoError(t, err)
assert.Equal(t, plaintext, dec)
}
func TestCryptoService_EncryptDecrypt_DifferentEachTime(t *testing.T) {
key := make([]byte, 32)
for i := range key {
key[i] = byte(i)
}
svc, err := NewCryptoService(key)
require.NoError(t, err)
plaintext := []byte("token")
enc1, err := svc.Encrypt(plaintext)
require.NoError(t, err)
enc2, err := svc.Encrypt(plaintext)
require.NoError(t, err)
assert.NotEqual(t, enc1, enc2, "encryption should be non-deterministic (random nonce)")
dec1, err := svc.Decrypt(enc1)
require.NoError(t, err)
dec2, err := svc.Decrypt(enc2)
require.NoError(t, err)
assert.Equal(t, plaintext, dec1)
assert.Equal(t, plaintext, dec2)
}
func TestCryptoService_EncryptString_DecryptString_Roundtrip(t *testing.T) {
key := make([]byte, 32)
for i := range key {
key[i] = byte(i)
}
svc, err := NewCryptoService(key)
require.NoError(t, err)
plaintext := "my-access-token-123"
enc, err := svc.EncryptString(plaintext)
require.NoError(t, err)
assert.True(t, strings.HasPrefix(enc, EncryptedTokenPrefix()))
assert.NotEqual(t, plaintext, enc)
dec, err := svc.DecryptString(enc)
require.NoError(t, err)
assert.Equal(t, plaintext, dec)
}
func TestCryptoService_DecryptString_Plaintext(t *testing.T) {
key := make([]byte, 32)
for i := range key {
key[i] = byte(i)
}
svc, err := NewCryptoService(key)
require.NoError(t, err)
// Legacy plaintext (no prefix) -> returned as-is
plain := "legacy-token"
dec, err := svc.DecryptString(plain)
require.NoError(t, err)
assert.Equal(t, plain, dec)
}
func TestCryptoService_DecryptString_Empty(t *testing.T) {
key := make([]byte, 32)
svc, err := NewCryptoService(key)
require.NoError(t, err)
dec, err := svc.DecryptString("")
require.NoError(t, err)
assert.Empty(t, dec)
}
func TestCryptoService_EncryptString_Empty(t *testing.T) {
key := make([]byte, 32)
svc, err := NewCryptoService(key)
require.NoError(t, err)
enc, err := svc.EncryptString("")
require.NoError(t, err)
assert.Empty(t, enc)
}
func TestCryptoService_Decrypt_ModifiedCiphertext(t *testing.T) {
key := make([]byte, 32)
for i := range key {
key[i] = byte(i)
}
svc, err := NewCryptoService(key)
require.NoError(t, err)
enc, err := svc.Encrypt([]byte("token"))
require.NoError(t, err)
enc[20] ^= 0xff // flip a byte
_, err = svc.Decrypt(enc)
require.Error(t, err)
}
func TestNewCryptoServiceFromBase64(t *testing.T) {
key := make([]byte, 32)
for i := range key {
key[i] = byte(i)
}
keyB64 := base64.RawStdEncoding.EncodeToString(key)
svc, err := NewCryptoServiceFromBase64(keyB64)
require.NoError(t, err)
assert.NotNil(t, svc)
enc, err := svc.EncryptString("test")
require.NoError(t, err)
dec, err := svc.DecryptString(enc)
require.NoError(t, err)
assert.Equal(t, "test", dec)
}
func TestNewCryptoServiceFromBase64_Empty(t *testing.T) {
_, err := NewCryptoServiceFromBase64("")
require.Error(t, err)
}