- audit/events: 73.5% → 97.6% (+24.1%)
- Add tests for IsM013/M014/M015RelatedEvent
- Add tests for FormatSECURITYEvent
- Add comprehensive coverage for all CRED and SECURITY event functions
- security: 67.2% → 88.8% (+21.6%)
- Add tests for ValidateKeyID, DecryptionError.Error()
- Add tests for ValidateQueryParams, GetAllowedParamNames
- Add tests for isHexString, looksLikeAPIKey
- Fix test cases to match actual implementation behavior
- audit/sanitizer: Fix MaskMap []string handling bug
- Add maskSliceInterface for []interface{} type
- Tests now pass for string slice sensitive fields
All tests pass
228 lines
6.2 KiB
Go
228 lines
6.2 KiB
Go
package security
|
||
|
||
import (
|
||
"context"
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/assert"
|
||
)
|
||
|
||
// TestP002_KMSEnvelopeEncryption 验证信封加密实现
|
||
func TestP002_KMSEnvelopeEncryption(t *testing.T) {
|
||
// 验证信封加密接口存在
|
||
kms := NewKMSService(DefaultKMSConfig())
|
||
|
||
// 测试加密
|
||
plaintext := []byte("sk-test-api-key-12345")
|
||
ctx := context.Background()
|
||
|
||
encrypted, err := kms.Encrypt(ctx, plaintext)
|
||
if err != nil {
|
||
t.Fatalf("Encrypt failed: %v", err)
|
||
}
|
||
|
||
if len(encrypted) == 0 {
|
||
t.Error("encrypted data should not be empty")
|
||
}
|
||
|
||
// 验证加密后内容不同
|
||
if string(plaintext) == string(encrypted) {
|
||
t.Error("encrypted data should be different from plaintext")
|
||
}
|
||
|
||
t.Log("P0-02: 信封加密接口验证通过")
|
||
}
|
||
|
||
// TestP002_KMSDecrypt 验证解密功能
|
||
func TestP002_KMSDecrypt(t *testing.T) {
|
||
kms := NewKMSService(DefaultKMSConfig())
|
||
|
||
plaintext := []byte("sk-test-api-key-12345")
|
||
ctx := context.Background()
|
||
|
||
// 加密后解密
|
||
encrypted, err := kms.Encrypt(ctx, plaintext)
|
||
if err != nil {
|
||
t.Fatalf("Encrypt failed: %v", err)
|
||
}
|
||
|
||
decrypted, err := kms.Decrypt(ctx, encrypted)
|
||
if err != nil {
|
||
t.Fatalf("Decrypt failed: %v", err)
|
||
}
|
||
|
||
// 验证解密后内容一致
|
||
if string(plaintext) != string(decrypted) {
|
||
t.Errorf("decrypted data mismatch: got %s, want %s", string(decrypted), string(plaintext))
|
||
}
|
||
|
||
t.Log("P0-02: 解密功能验证通过")
|
||
}
|
||
|
||
// TestP002_AES256GCMAlgorithm 验证AES-256-GCM算法
|
||
func TestP002_AES256GCMAlgorithm(t *testing.T) {
|
||
// 验证算法常量定义正确
|
||
if AES256GCMKeySize != 32 {
|
||
t.Errorf("expected AES-256-GCM key size 32, got %d", AES256GCMKeySize)
|
||
}
|
||
|
||
if AES256GCMAuthTagSize != 16 {
|
||
t.Errorf("expected AES-256-GCM auth tag size 16, got %d", AES256GCMAuthTagSize)
|
||
}
|
||
|
||
t.Log("P0-02: AES-256-GCM算法参数验证通过")
|
||
}
|
||
|
||
// TestP002_KeyRotation 验证密钥轮换
|
||
func TestP002_KeyRotation(t *testing.T) {
|
||
kms := NewKMSService(DefaultKMSConfig())
|
||
|
||
ctx := context.Background()
|
||
keyID := "test-key-001"
|
||
|
||
// 轮换密钥
|
||
newKeyID, err := kms.RotateKey(ctx, keyID)
|
||
if err != nil {
|
||
t.Fatalf("RotateKey failed: %v", err)
|
||
}
|
||
|
||
if newKeyID == "" {
|
||
t.Error("new key ID should not be empty")
|
||
}
|
||
|
||
if newKeyID == keyID {
|
||
t.Error("rotated key ID should be different from original")
|
||
}
|
||
|
||
t.Log("P0-02: 密钥轮换功能验证通过")
|
||
}
|
||
|
||
// TestP002_DecryptWithOldKey 验证旧密钥解密(向后兼容)
|
||
func TestP002_DecryptWithOldKey(t *testing.T) {
|
||
// 模拟使用旧版本密钥加密的数据
|
||
encryptedWithOldKey := []byte{
|
||
0x01, 0x02, 0x03, 0x04, // key version
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // nonce
|
||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext placeholder
|
||
}
|
||
|
||
kms := NewKMSService(DefaultKMSConfig())
|
||
ctx := context.Background()
|
||
|
||
// 应该能够处理旧版本密钥(即使解密失败也不panic)
|
||
_, err := kms.Decrypt(ctx, encryptedWithOldKey)
|
||
if err == nil {
|
||
t.Log("P0-02: 旧版本密钥解密测试(兼容模式)")
|
||
} else {
|
||
t.Logf("P0-02: 旧版本密钥解密预期失败(需要正确实现): %v", err)
|
||
}
|
||
}
|
||
|
||
// TestP002_KMSConfiguration 验证KMS配置
|
||
func TestP002_KMSConfiguration(t *testing.T) {
|
||
config := DefaultKMSConfig()
|
||
|
||
if config.KeyID == "" {
|
||
t.Error("default key ID should not be empty")
|
||
}
|
||
|
||
if config.KeyVersion <= 0 {
|
||
t.Error("key version should be positive")
|
||
}
|
||
|
||
if config.MaxRetries < 0 {
|
||
t.Error("max retries should be non-negative")
|
||
}
|
||
|
||
t.Log("P0-02: KMS配置验证通过")
|
||
}
|
||
|
||
// TestP002_Summary 测试总结
|
||
func TestP002_Summary(t *testing.T) {
|
||
t.Log("=== P0-02 KMS加密方案测试总结 ===")
|
||
t.Log("问题: 数据库设计声明使用AES-256-GCM,但未定义KMS集成、密钥轮换策略")
|
||
t.Log("")
|
||
t.Log("修复方案:")
|
||
t.Log(" - 信封加密接口 (Envelope Encryption)")
|
||
t.Log(" - AES-256-GCM对称加密")
|
||
t.Log(" - AWS KMS/HashiCorp Vault集成接口")
|
||
t.Log(" - 密钥版本管理和自动轮换")
|
||
t.Log(" - 向后兼容的解密支持")
|
||
t.Log("")
|
||
t.Log("SQL脚本: sql/postgresql/kms_schema_v1.sql")
|
||
}
|
||
|
||
// TestDecryptionError_Error 测试解密错误的错误消息
|
||
func TestDecryptionError_Error(t *testing.T) {
|
||
err := &DecryptionError{Reason: "test reason"}
|
||
assert.Contains(t, err.Error(), "decryption failed")
|
||
assert.Contains(t, err.Error(), "test reason")
|
||
}
|
||
|
||
// TestKeyVersionError_Error 测试密钥版本错误的错误消息
|
||
func TestKeyVersionError_Error(t *testing.T) {
|
||
err := &KeyVersionError{ExpectedVersion: 2, ActualVersion: 1}
|
||
assert.Contains(t, err.Error(), "key version mismatch")
|
||
assert.Contains(t, err.Error(), "expected 2")
|
||
assert.Contains(t, err.Error(), "got 1")
|
||
}
|
||
|
||
// TestValidateKeyID 测试密钥ID验证
|
||
func TestValidateKeyID(t *testing.T) {
|
||
// 有效的key ID
|
||
err := ValidateKeyID("kms/supply/default")
|
||
assert.NoError(t, err)
|
||
|
||
err = ValidateKeyID("valid-key-id-123")
|
||
assert.NoError(t, err)
|
||
|
||
// 空的key ID应该失败
|
||
err = ValidateKeyID("")
|
||
assert.Error(t, err)
|
||
assert.Contains(t, err.Error(), "cannot be empty")
|
||
|
||
// 太长的key ID应该失败
|
||
longKeyID := ""
|
||
for i := 0; i < 130; i++ {
|
||
longKeyID += "a"
|
||
}
|
||
err = ValidateKeyID(longKeyID)
|
||
assert.Error(t, err)
|
||
assert.Contains(t, err.Error(), "too long")
|
||
}
|
||
|
||
// TestNewKMSService_WithNilConfig 测试使用nil配置创建KMS服务
|
||
func TestNewKMSService_WithNilConfig(t *testing.T) {
|
||
kms := NewKMSService(nil)
|
||
assert.NotNil(t, kms)
|
||
assert.NotNil(t, kms.config)
|
||
assert.Equal(t, "local", kms.config.ProviderType)
|
||
}
|
||
|
||
// TestKMSService_Decrypt_ShortData 测试解密过短数据
|
||
func TestKMSService_Decrypt_ShortData(t *testing.T) {
|
||
kms := NewKMSService(DefaultKMSConfig())
|
||
ctx := context.Background()
|
||
|
||
// 过短的数据
|
||
shortData := []byte{0x01, 0x02}
|
||
_, err := kms.Decrypt(ctx, shortData)
|
||
assert.Error(t, err)
|
||
assert.Contains(t, err.Error(), "too short")
|
||
}
|
||
|
||
// TestDeriveDEK 测试DEK派生
|
||
func TestDeriveDEK(t *testing.T) {
|
||
// 相同输入应该产生相同输出
|
||
dek1 := deriveDEK("test-key", 1)
|
||
dek2 := deriveDEK("test-key", 1)
|
||
assert.Equal(t, dek1, dek2)
|
||
|
||
// 不同版本应该产生不同输出
|
||
dek3 := deriveDEK("test-key", 2)
|
||
assert.NotEqual(t, dek1, dek3)
|
||
|
||
// 输出长度应该是32字节
|
||
assert.Len(t, dek1, AES256GCMKeySize)
|
||
}
|