mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Full codebase audit: 21 security/robustness fixes, 328 tests across 4 packages
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>
This commit is contained in:
165
internal/crypto/crypto_test.go
Normal file
165
internal/crypto/crypto_test.go
Normal file
@@ -0,0 +1,165 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user