""" Security utilities for API key management and validation. This module provides secure handling of sensitive configuration data. """ import hashlib import os import secrets from typing import Optional, Dict, Any from dataclasses import dataclass from datetime import datetime from enum import Enum class ErrorType(Enum): """Types of errors that can occur in the system.""" VALIDATION_ERROR = "validation_error" AUTHENTICATION_ERROR = "authentication_error" AUTHORIZATION_ERROR = "authorization_error" RATE_LIMIT_ERROR = "rate_limit_error" API_ERROR = "api_error" NETWORK_ERROR = "network_error" TIMEOUT_ERROR = "timeout_error" UNKNOWN_ERROR = "unknown_error" @dataclass class ErrorResponse: """Standardized error response for API operations.""" error_type: ErrorType error_code: str message: str details: Optional[str] = None timestamp: datetime = None request_id: Optional[str] = None def __post_init__(self): if self.timestamp is None: self.timestamp = datetime.now() def __iter__(self): """Prevent unpacking by raising a clear error message.""" raise TypeError( "ErrorResponse object cannot be unpacked. " "Access attributes directly: error_response.error_type, error_response.error_code, etc." ) def to_dict(self) -> Dict[str, Any]: """Convert ErrorResponse to dictionary for easy access.""" return { "error_type": self.error_type.value if hasattr(self.error_type, 'value') else str(self.error_type), "error_code": self.error_code, "message": self.message, "details": self.details, "timestamp": self.timestamp, "request_id": self.request_id } class SecurityUtils: """Security utilities for API key management.""" @staticmethod def mask_api_key(api_key: str, visible_chars: int = 4) -> str: """ Mask an API key for safe logging. Args: api_key: The API key to mask visible_chars: Number of characters to show at the end Returns: Masked API key string """ if not api_key or len(api_key) < visible_chars: return "***" masked_length = len(api_key) - visible_chars return "*" * masked_length + api_key[-visible_chars:] @staticmethod def generate_secret_key(length: int = 32) -> str: """ Generate a cryptographically secure secret key. Args: length: Length of the secret key Returns: Random secret key """ return secrets.token_urlsafe(length) @staticmethod def hash_api_key(api_key: str) -> str: """ Create a hash of an API key for identification purposes. Args: api_key: The API key to hash Returns: SHA-256 hash of the API key """ return hashlib.sha256(api_key.encode()).hexdigest()[:16] @staticmethod def validate_api_key_format(api_key: str, expected_prefix: Optional[str] = None) -> bool: """ Validate API key format. Args: api_key: The API key to validate expected_prefix: Expected prefix (e.g., "sk-", "pk-") Returns: True if valid, False otherwise """ if not api_key or len(api_key) < 10: return False if expected_prefix and not api_key.startswith(expected_prefix): return False return True @staticmethod def check_environment_security() -> dict: """ Check environment security settings. Returns: Dictionary with security status """ security_status = { "env_file_exists": os.path.exists(".env"), "env_file_permissions": None, "home_directory_secure": True, "temp_directory_secure": True, "recommendations": [] } # Check .env file permissions if security_status["env_file_exists"]: try: stat_info = os.stat(".env") permissions = oct(stat_info.st_mode)[-3:] security_status["env_file_permissions"] = permissions # Check if file is world-readable (security risk) if int(permissions[2]) > 4: # Others can read security_status["recommendations"].append( "⚠️ .env file is world-readable. Run: chmod 600 .env" ) except OSError: security_status["env_file_permissions"] = "unknown" # Check if running in secure environment if os.path.expanduser("~") == "/root": security_status["recommendations"].append( "⚠️ Running as root user. Consider using a non-root user." ) # Check for common security issues if not security_status["env_file_exists"]: security_status["recommendations"].append( "❌ .env file not found. Create one from env.example" ) return security_status def secure_log_api_key(api_key: str, key_name: str = "API_KEY") -> str: """ Create a secure log message for API keys. Args: api_key: The API key key_name: Name of the key for logging Returns: Safe log message """ if not api_key: return f"{key_name}: Not configured" masked_key = SecurityUtils.mask_api_key(api_key) key_hash = SecurityUtils.hash_api_key(api_key) return f"{key_name}: {masked_key} (hash: {key_hash})" # Example usage if __name__ == "__main__": # Test security utilities test_key = "sk-1234567890abcdef1234567890abcdef" print("🔒 Security Utilities Test:") print(f"Original key: {test_key}") print(f"Masked key: {SecurityUtils.mask_api_key(test_key)}") print(f"Key hash: {SecurityUtils.hash_api_key(test_key)}") print(f"Valid format: {SecurityUtils.validate_api_key_format(test_key, 'sk-')}") print(f"Secure log: {secure_log_api_key(test_key, 'ANTHROPIC_API_KEY')}") print("\n🛡️ Environment Security Check:") security_status = SecurityUtils.check_environment_security() print(f"Environment file exists: {security_status['env_file_exists']}") print(f"File permissions: {security_status['env_file_permissions']}") if security_status['recommendations']: print("\n📋 Security Recommendations:") for rec in security_status['recommendations']: print(f" {rec}") else: print("✅ No security issues detected!")