mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Security & economy: - Credit()/Debit() reject non-positive amounts (closes infinite-money exploit) - Lottery ticket purchase uses in-transaction count check (closes TOCTOU race) - Arena bail channel ownership prevents double-close panic - Entry.ID validated before exec.Command in fetch-esteemed - Internal errors no longer leaked to users (forex, esteemed) Robustness: - safeGo() panic recovery added to 17 goroutine launch sites across 14 plugins - db.Close() added and called on shutdown - Miniflux mutex pattern fixed (snapshot-under-lock) - Silently ignored DB/JSON errors now logged (lottery_db) Performance: - Added indexes: idx_arena_runs_user(user_id, status), idx_euro_bal_user(user_id) UX: - Empty !buy args shows usage instead of "No item matching ''" - "Failed to load character" now suggests !adventure Test coverage: - internal/util/parser_test.go (19 tests: XP, levels, progress bar, commands, parsing) - internal/crypto/crypto_test.go (12 tests: encrypt/decrypt, HMAC, ParseKey) - internal/plugin/helpers_test.go (30 tests: formatNumber, calc, lottery, chat perks) - internal/plugin/audit_fixes_test.go (25 tests: safeGo, bail channels, error leaks, overflow) - internal/db/db_test.go (4 tests: Init, Close, schema indexes) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
166 lines
3.9 KiB
Go
166 lines
3.9 KiB
Go
package crypto
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"testing"
|
|
)
|
|
|
|
func testKey() []byte {
|
|
// 32 bytes for AES-256
|
|
return []byte("01234567890123456789012345678901")
|
|
}
|
|
|
|
// ── Encrypt / Decrypt Round-Trip ───────────────────────────────────────────
|
|
|
|
func TestEncryptDecrypt_RoundTrip(t *testing.T) {
|
|
key := testKey()
|
|
plaintext := []byte("hello, arena champion")
|
|
|
|
ciphertext, err := Encrypt(key, plaintext)
|
|
if err != nil {
|
|
t.Fatalf("Encrypt: %v", err)
|
|
}
|
|
|
|
got, err := Decrypt(key, ciphertext)
|
|
if err != nil {
|
|
t.Fatalf("Decrypt: %v", err)
|
|
}
|
|
|
|
if !bytes.Equal(got, plaintext) {
|
|
t.Errorf("round-trip failed: got %q, want %q", got, plaintext)
|
|
}
|
|
}
|
|
|
|
func TestEncryptDecrypt_EmptyPlaintext(t *testing.T) {
|
|
key := testKey()
|
|
ciphertext, err := Encrypt(key, []byte{})
|
|
if err != nil {
|
|
t.Fatalf("Encrypt empty: %v", err)
|
|
}
|
|
|
|
got, err := Decrypt(key, ciphertext)
|
|
if err != nil {
|
|
t.Fatalf("Decrypt empty: %v", err)
|
|
}
|
|
|
|
if len(got) != 0 {
|
|
t.Errorf("expected empty plaintext, got %d bytes", len(got))
|
|
}
|
|
}
|
|
|
|
func TestEncrypt_DifferentNonces(t *testing.T) {
|
|
key := testKey()
|
|
plaintext := []byte("same input")
|
|
|
|
ct1, _ := Encrypt(key, plaintext)
|
|
ct2, _ := Encrypt(key, plaintext)
|
|
|
|
if bytes.Equal(ct1, ct2) {
|
|
t.Error("two encryptions of the same plaintext should produce different ciphertexts (random nonces)")
|
|
}
|
|
}
|
|
|
|
func TestDecrypt_WrongKey(t *testing.T) {
|
|
key := testKey()
|
|
plaintext := []byte("secret data")
|
|
|
|
ciphertext, err := Encrypt(key, plaintext)
|
|
if err != nil {
|
|
t.Fatalf("Encrypt: %v", err)
|
|
}
|
|
|
|
wrongKey := []byte("99999999999999999999999999999999")
|
|
_, err = Decrypt(wrongKey, ciphertext)
|
|
if err == nil {
|
|
t.Error("Decrypt with wrong key should fail")
|
|
}
|
|
}
|
|
|
|
func TestDecrypt_TruncatedCiphertext(t *testing.T) {
|
|
key := testKey()
|
|
_, err := Decrypt(key, []byte{1, 2, 3})
|
|
if err == nil {
|
|
t.Error("Decrypt with too-short ciphertext should fail")
|
|
}
|
|
}
|
|
|
|
// ── HMAC ───────────────────────────────────────────────────────────────────
|
|
|
|
func TestHMAC_Deterministic(t *testing.T) {
|
|
key := testKey()
|
|
data := []byte("test data")
|
|
|
|
h1 := HMAC(key, data)
|
|
h2 := HMAC(key, data)
|
|
|
|
if h1 != h2 {
|
|
t.Error("HMAC should be deterministic")
|
|
}
|
|
}
|
|
|
|
func TestHMAC_DifferentData(t *testing.T) {
|
|
key := testKey()
|
|
|
|
h1 := HMAC(key, []byte("data1"))
|
|
h2 := HMAC(key, []byte("data2"))
|
|
|
|
if h1 == h2 {
|
|
t.Error("HMAC of different data should differ")
|
|
}
|
|
}
|
|
|
|
func TestHMAC_DifferentKeys(t *testing.T) {
|
|
data := []byte("same data")
|
|
|
|
h1 := HMAC([]byte("key1key1key1key1key1key1key1key1"), data)
|
|
h2 := HMAC([]byte("key2key2key2key2key2key2key2key2"), data)
|
|
|
|
if h1 == h2 {
|
|
t.Error("HMAC with different keys should differ")
|
|
}
|
|
}
|
|
|
|
func TestHMAC_Length(t *testing.T) {
|
|
h := HMAC(testKey(), []byte("x"))
|
|
// SHA-256 = 32 bytes = 64 hex chars
|
|
if len(h) != 64 {
|
|
t.Errorf("HMAC hex length = %d, want 64", len(h))
|
|
}
|
|
}
|
|
|
|
// ── ParseKey ───────────────────────────────────────────────────────────────
|
|
|
|
func TestParseKey_Valid(t *testing.T) {
|
|
raw := make([]byte, 32)
|
|
for i := range raw {
|
|
raw[i] = byte(i)
|
|
}
|
|
b64 := base64.StdEncoding.EncodeToString(raw)
|
|
|
|
key, err := ParseKey(b64)
|
|
if err != nil {
|
|
t.Fatalf("ParseKey: %v", err)
|
|
}
|
|
if !bytes.Equal(key, raw) {
|
|
t.Error("ParseKey returned wrong bytes")
|
|
}
|
|
}
|
|
|
|
func TestParseKey_WrongLength(t *testing.T) {
|
|
raw := make([]byte, 16) // too short
|
|
b64 := base64.StdEncoding.EncodeToString(raw)
|
|
|
|
_, err := ParseKey(b64)
|
|
if err == nil {
|
|
t.Error("ParseKey should reject non-32-byte keys")
|
|
}
|
|
}
|
|
|
|
func TestParseKey_InvalidBase64(t *testing.T) {
|
|
_, err := ParseKey("not!valid!base64!!!")
|
|
if err == nil {
|
|
t.Error("ParseKey should reject invalid base64")
|
|
}
|
|
}
|