Files
fscan/webscan/lib/shiro_test.go
T
ZacharyZcR 0612255893 test: 补充单元测试覆盖率 29.9% → 36.6%
新建 18 个测试文件,追加 30 个已有测试文件,覆盖协议解析、
错误分类、CEL 表达式求值、YAML 反序列化、字节编码等纯函数。
2026-06-17 12:51:41 +08:00

238 lines
6.7 KiB
Go

package lib
import (
"encoding/base64"
"strings"
"testing"
)
// =============================================================================
// Padding 测试
// =============================================================================
func TestPadding_BasicBlockAlignment(t *testing.T) {
tests := []struct {
name string
input []byte
blockSize int
wantLen int // 期望长度
}{
{
name: "空输入填充整个块",
input: []byte{},
blockSize: 16,
wantLen: 16,
},
{
name: "15字节填充1字节",
input: make([]byte, 15),
blockSize: 16,
wantLen: 16,
},
{
name: "整块对齐追加完整块",
input: make([]byte, 16),
blockSize: 16,
wantLen: 32,
},
{
name: "1字节填充15字节",
input: []byte{0x01},
blockSize: 16,
wantLen: 16,
},
{
name: "blockSize=8时的对齐",
input: make([]byte, 5),
blockSize: 8,
wantLen: 8,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Padding(tt.input, tt.blockSize)
if len(result) != tt.wantLen {
t.Errorf("Padding() len=%d, want %d", len(result), tt.wantLen)
}
// 验证填充字节值符合 PKCS7 规范
if len(result) > 0 {
padLen := int(result[len(result)-1])
if padLen == 0 || padLen > tt.blockSize {
t.Errorf("填充字节值 %d 超出 blockSize=%d", padLen, tt.blockSize)
}
// 验证所有填充字节相同
for i := len(result) - padLen; i < len(result); i++ {
if result[i] != byte(padLen) {
t.Errorf("填充字节[%d]=%d 不等于 padLen=%d", i, result[i], padLen)
}
}
}
})
}
}
func TestPadding_ResultLength(t *testing.T) {
// 任意长度输入,结果都应该是 blockSize 的整数倍
blockSize := 16
for inputLen := 0; inputLen < 50; inputLen++ {
input := make([]byte, inputLen)
result := Padding(input, blockSize)
if len(result)%blockSize != 0 {
t.Errorf("输入长度 %d: 填充后长度 %d 不是 %d 的倍数", inputLen, len(result), blockSize)
}
}
}
// =============================================================================
// AESCBCEncrypt 测试
// =============================================================================
func TestAESCBCEncrypt_ValidKey128(t *testing.T) {
// 128-bit AES key (16 bytes)
key := base64.StdEncoding.EncodeToString(make([]byte, 16))
result := AESCBCEncrypt(key)
if result == "" {
t.Error("有效的128位密钥应返回非空结果")
}
// 结果应为有效的 base64
_, err := base64.StdEncoding.DecodeString(result)
if err != nil {
t.Errorf("AESCBCEncrypt 结果应为有效 base64: %v", err)
}
}
func TestAESCBCEncrypt_ValidKey256(t *testing.T) {
// 256-bit AES key (32 bytes)
key := base64.StdEncoding.EncodeToString(make([]byte, 32))
result := AESCBCEncrypt(key)
if result == "" {
t.Error("有效的256位密钥应返回非空结果")
}
}
func TestAESCBCEncrypt_InvalidBase64Key(t *testing.T) {
result := AESCBCEncrypt("!!!not-valid-base64!!!")
if result != "" {
t.Error("无效 base64 密钥应返回空字符串")
}
}
func TestAESCBCEncrypt_InvalidKeySize(t *testing.T) {
// AES 要求密钥为 16/24/32 字节,10 字节无效
key := base64.StdEncoding.EncodeToString(make([]byte, 10))
result := AESCBCEncrypt(key)
if result != "" {
t.Error("无效密钥长度应返回空字符串")
}
}
func TestAESCBCEncrypt_NonDeterministic(t *testing.T) {
// 因为 IV 是随机的,两次加密结果应不同
key := base64.StdEncoding.EncodeToString(make([]byte, 16))
r1 := AESCBCEncrypt(key)
r2 := AESCBCEncrypt(key)
if r1 == r2 {
// 极小概率相同,记录即可
t.Log("两次加密结果相同(极低概率事件)")
}
}
// =============================================================================
// AESGCMEncrypt 测试
// =============================================================================
func TestAESGCMEncrypt_ValidKey128(t *testing.T) {
key := base64.StdEncoding.EncodeToString(make([]byte, 16))
result := AESGCMEncrypt(key)
if result == "" {
t.Error("有效的128位密钥应返回非空结果")
}
_, err := base64.StdEncoding.DecodeString(result)
if err != nil {
t.Errorf("AESGCMEncrypt 结果应为有效 base64: %v", err)
}
}
func TestAESGCMEncrypt_InvalidKey(t *testing.T) {
result := AESGCMEncrypt("invalid-base64!!!")
if result != "" {
t.Error("无效 base64 密钥应返回空字符串")
}
}
func TestAESGCMEncrypt_NonDeterministic(t *testing.T) {
key := base64.StdEncoding.EncodeToString(make([]byte, 16))
r1 := AESGCMEncrypt(key)
r2 := AESGCMEncrypt(key)
// GCM nonce 随机,结果不应相同
if r1 == r2 {
t.Log("两次 GCM 加密结果相同(极低概率事件)")
}
}
// =============================================================================
// GetShrioCookie 测试
// =============================================================================
func TestGetShrioCookie_CBCMode(t *testing.T) {
key := base64.StdEncoding.EncodeToString(make([]byte, 16))
result := GetShrioCookie(key, "cbc")
if result == "" {
t.Error("CBC 模式应返回非空 cookie")
}
}
func TestGetShrioCookie_GCMMode(t *testing.T) {
key := base64.StdEncoding.EncodeToString(make([]byte, 16))
result := GetShrioCookie(key, "gcm")
if result == "" {
t.Error("GCM 模式应返回非空 cookie")
}
}
func TestGetShrioCookie_DefaultMode(t *testing.T) {
// 非 gcm 模式走 CBC
key := base64.StdEncoding.EncodeToString(make([]byte, 16))
result := GetShrioCookie(key, "other")
cbcResult := AESCBCEncrypt(key)
// 两个结果都应为非空 base64,但由于随机 IV 不一定相同
if result == "" {
t.Error("默认(非gcm)模式应使用 CBC 加密并返回非空结果")
}
_ = cbcResult
}
func TestGetShrioCookie_RealShiroKey(t *testing.T) {
// 使用真实的 Shiro 默认密钥
shiroDefaultKey := "kPH+bIxk5D2deZiIxcaaaA=="
result := GetShrioCookie(shiroDefaultKey, "cbc")
if result == "" {
t.Error("使用默认 Shiro 密钥应能生成有效 cookie")
}
// 验证结果是 base64 编码
decoded, err := base64.StdEncoding.DecodeString(result)
if err != nil {
t.Errorf("结果应为有效 base64: %v", err)
}
// CBC 模式:IV(16字节) + 密文,结果至少 32 字节
if len(decoded) < 32 {
t.Errorf("CBC 加密结果太短: %d 字节", len(decoded))
}
}
func TestGetShrioCookie_ResultIsBase64(t *testing.T) {
key := base64.StdEncoding.EncodeToString(make([]byte, 16))
for _, mode := range []string{"cbc", "gcm"} {
result := GetShrioCookie(key, mode)
if result == "" {
t.Errorf("mode=%s: 结果不应为空", mode)
continue
}
// base64 只含 [A-Za-z0-9+/=]
if strings.ContainsAny(result, " \t\n\r") {
t.Errorf("mode=%s: base64 结果不应含空白字符", mode)
}
}
}