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:
241
orig/internal/logger/secure_audit.go
Normal file
241
orig/internal/logger/secure_audit.go
Normal file
@@ -0,0 +1,241 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
)
|
||||
|
||||
// FilterMessageEnhanced provides comprehensive filtering with audit logging
|
||||
func (sf *SecureFilter) FilterMessageEnhanced(message string, context map[string]interface{}) string {
|
||||
originalMessage := message
|
||||
filtered := sf.FilterMessage(message)
|
||||
|
||||
// Audit sensitive data detection if enabled
|
||||
if sf.auditEnabled {
|
||||
auditData := sf.detectSensitiveData(originalMessage, context)
|
||||
if len(auditData) > 0 {
|
||||
sf.logAuditEvent(auditData)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
// detectSensitiveData identifies and catalogs sensitive data found in messages
|
||||
func (sf *SecureFilter) detectSensitiveData(message string, context map[string]interface{}) map[string]interface{} {
|
||||
detected := make(map[string]interface{})
|
||||
detected["timestamp"] = time.Now().UTC().Format(time.RFC3339)
|
||||
detected["security_level"] = sf.level
|
||||
|
||||
if context != nil {
|
||||
detected["context"] = context
|
||||
}
|
||||
|
||||
// Check for different types of sensitive data
|
||||
sensitiveTypes := []string{}
|
||||
|
||||
// Check for private keys (CRITICAL)
|
||||
for _, pattern := range sf.privateKeyPatterns {
|
||||
if pattern.MatchString(message) {
|
||||
sensitiveTypes = append(sensitiveTypes, "private_key")
|
||||
detected["severity"] = "CRITICAL"
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check for transaction hashes BEFORE addresses (64 chars vs 40 chars)
|
||||
for _, pattern := range sf.hashPatterns {
|
||||
if pattern.MatchString(message) {
|
||||
sensitiveTypes = append(sensitiveTypes, "transaction_hash")
|
||||
if detected["severity"] == nil {
|
||||
detected["severity"] = "LOW"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check for addresses AFTER hashes
|
||||
for _, pattern := range sf.addressPatterns {
|
||||
if pattern.MatchString(message) {
|
||||
sensitiveTypes = append(sensitiveTypes, "address")
|
||||
if detected["severity"] == nil {
|
||||
detected["severity"] = "MEDIUM"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check for amounts/values
|
||||
for _, pattern := range sf.amountPatterns {
|
||||
if pattern.MatchString(message) {
|
||||
sensitiveTypes = append(sensitiveTypes, "amount")
|
||||
if detected["severity"] == nil {
|
||||
detected["severity"] = "LOW"
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(sensitiveTypes) > 0 {
|
||||
detected["types"] = sensitiveTypes
|
||||
detected["message_length"] = len(message)
|
||||
detected["filtered_length"] = len(sf.FilterMessage(message))
|
||||
return detected
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// logAuditEvent logs sensitive data detection events
|
||||
func (sf *SecureFilter) logAuditEvent(auditData map[string]interface{}) {
|
||||
// Create audit log entry
|
||||
auditEntry := map[string]interface{}{
|
||||
"event_type": "sensitive_data_detected",
|
||||
"timestamp": auditData["timestamp"],
|
||||
"severity": auditData["severity"],
|
||||
"types": auditData["types"],
|
||||
"message_stats": map[string]interface{}{
|
||||
"original_length": auditData["message_length"],
|
||||
"filtered_length": auditData["filtered_length"],
|
||||
},
|
||||
}
|
||||
|
||||
if auditData["context"] != nil {
|
||||
auditEntry["context"] = auditData["context"]
|
||||
}
|
||||
|
||||
// Encrypt audit data if encryption is enabled
|
||||
if sf.auditEncryption && len(sf.encryptionKey) > 0 {
|
||||
encrypted, err := sf.encryptAuditData(auditEntry)
|
||||
if err == nil {
|
||||
auditEntry = map[string]interface{}{
|
||||
"encrypted": true,
|
||||
"data": encrypted,
|
||||
"timestamp": auditData["timestamp"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log to audit trail (this would typically go to a separate audit log file)
|
||||
// For now, we'll add it to a structured format that can be easily extracted
|
||||
auditJSON, _ := json.Marshal(auditEntry)
|
||||
fmt.Printf("AUDIT_LOG: %s\n", string(auditJSON))
|
||||
}
|
||||
|
||||
// encryptAuditData encrypts sensitive audit data
|
||||
func (sf *SecureFilter) encryptAuditData(data map[string]interface{}) (string, error) {
|
||||
if len(sf.encryptionKey) == 0 {
|
||||
return "", fmt.Errorf("no encryption key available")
|
||||
}
|
||||
|
||||
// Serialize data to JSON
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal audit data: %w", err)
|
||||
}
|
||||
|
||||
// Create AES-GCM cipher (AEAD - authenticated encryption)
|
||||
key := sha256.Sum256(sf.encryptionKey)
|
||||
block, err := aes.NewCipher(key[:])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create cipher: %w", err)
|
||||
}
|
||||
|
||||
// Create GCM instance
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create GCM: %w", err)
|
||||
}
|
||||
|
||||
// Generate random nonce
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "", fmt.Errorf("failed to generate nonce: %w", err)
|
||||
}
|
||||
|
||||
// Encrypt and authenticate data
|
||||
encrypted := gcm.Seal(nonce, nonce, jsonData, nil)
|
||||
return hex.EncodeToString(encrypted), nil
|
||||
}
|
||||
|
||||
// DecryptAuditData decrypts audit data (for authorized access)
|
||||
func (sf *SecureFilter) DecryptAuditData(encryptedHex string) (map[string]interface{}, error) {
|
||||
if len(sf.encryptionKey) == 0 {
|
||||
return nil, fmt.Errorf("no encryption key available")
|
||||
}
|
||||
|
||||
// Decode hex
|
||||
encryptedData, err := hex.DecodeString(encryptedHex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode hex: %w", err)
|
||||
}
|
||||
|
||||
// Create AES-GCM cipher (AEAD - authenticated encryption)
|
||||
key := sha256.Sum256(sf.encryptionKey)
|
||||
block, err := aes.NewCipher(key[:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create cipher: %w", err)
|
||||
}
|
||||
|
||||
// Create GCM instance
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create GCM: %w", err)
|
||||
}
|
||||
|
||||
// Check minimum length (nonce + encrypted data + tag)
|
||||
if len(encryptedData) < gcm.NonceSize() {
|
||||
return nil, fmt.Errorf("encrypted data too short")
|
||||
}
|
||||
|
||||
// Extract nonce and encrypted data
|
||||
nonce := encryptedData[:gcm.NonceSize()]
|
||||
ciphertext := encryptedData[gcm.NonceSize():]
|
||||
|
||||
// Decrypt and authenticate data
|
||||
decrypted, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decrypt data: %w", err)
|
||||
}
|
||||
|
||||
// Unmarshal JSON
|
||||
var result map[string]interface{}
|
||||
err = json.Unmarshal(decrypted, &result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal decrypted data: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// EnableAuditLogging enables audit logging with optional encryption
|
||||
func (sf *SecureFilter) EnableAuditLogging(encryptionKey []byte) {
|
||||
sf.auditEnabled = true
|
||||
if len(encryptionKey) > 0 {
|
||||
sf.encryptionKey = encryptionKey
|
||||
sf.auditEncryption = true
|
||||
}
|
||||
}
|
||||
|
||||
// DisableAuditLogging disables audit logging
|
||||
func (sf *SecureFilter) DisableAuditLogging() {
|
||||
sf.auditEnabled = false
|
||||
sf.auditEncryption = false
|
||||
}
|
||||
|
||||
// SetSecurityLevel changes the security level dynamically
|
||||
func (sf *SecureFilter) SetSecurityLevel(level SecurityLevel) {
|
||||
sf.level = level
|
||||
}
|
||||
|
||||
// GetSecurityLevel returns the current security level
|
||||
func (sf *SecureFilter) GetSecurityLevel() SecurityLevel {
|
||||
return sf.level
|
||||
}
|
||||
Reference in New Issue
Block a user