File size: 6,937 Bytes
3f9f85b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
"""
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!")