Repository navigation
Expand file tree
/
Copy pathlogging_utils.py
More file actions
97 lines (81 loc) · 3.35 KB
/
Copy pathlogging_utils.py
File metadata and controls
97 lines (81 loc) · 3.35 KB
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
"""Logging utilities for sanitizing sensitive data."""
import re
from typing import Any, Dict, List, Union
# Patterns for sensitive data
SENSITIVE_PATTERNS = [
(re.compile(r'\b[A-Fa-f0-9]{64}\b'), '***REDACTED_TOKEN***'), # 64-char hex tokens
(re.compile(r'\b[A-Fa-f0-9]{32}\b'), '***REDACTED_ID***'), # 32-char hex IDs
(re.compile(r'password["\']?\s*[:=]\s*["\']?[^"\s,}]+', re.IGNORECASE), 'password=***REDACTED***'),
(re.compile(r'token["\']?\s*[:=]\s*["\']?[^"\s,}]+', re.IGNORECASE), 'token=***REDACTED***'),
(re.compile(r'secret["\']?\s*[:=]\s*["\']?[^"\s,}]+', re.IGNORECASE), 'secret=***REDACTED***'),
(re.compile(r'key["\']?\s*[:=]\s*["\']?[^"\s,}]+', re.IGNORECASE), 'key=***REDACTED***'),
]
def sanitize_string(text: str) -> str:
"""Remove sensitive data from a string."""
if not isinstance(text, str):
return text
sanitized = text
for pattern, replacement in SENSITIVE_PATTERNS:
sanitized = pattern.sub(replacement, sanitized)
return sanitized
def sanitize_dict(data: Dict[str, Any]) -> Dict[str, Any]:
"""Remove sensitive data from a dictionary."""
if not isinstance(data, dict):
return data
sanitized = {}
for key, value in data.items():
# Check if key itself indicates sensitive data
key_lower = key.lower()
if any(sensitive_word in key_lower for sensitive_word in ['password', 'token', 'secret', 'key', 'auth']):
sanitized[key] = '***REDACTED***'
elif isinstance(value, str):
sanitized[key] = sanitize_string(value)
elif isinstance(value, dict):
sanitized[key] = sanitize_dict(value)
elif isinstance(value, list):
sanitized[key] = sanitize_list(value)
else:
sanitized[key] = value
return sanitized
def sanitize_list(data: List[Any]) -> List[Any]:
"""Remove sensitive data from a list."""
if not isinstance(data, list):
return data
sanitized = []
for item in data:
if isinstance(item, str):
sanitized.append(sanitize_string(item))
elif isinstance(item, dict):
sanitized.append(sanitize_dict(item))
elif isinstance(item, list):
sanitized.append(sanitize_list(item))
else:
sanitized.append(item)
return sanitized
def sanitize_log_data(data: Any) -> Any:
"""Sanitize any type of data for logging."""
if isinstance(data, str):
return sanitize_string(data)
elif isinstance(data, dict):
return sanitize_dict(data)
elif isinstance(data, list):
return sanitize_list(data)
else:
return data
def mask_token(token: str) -> str:
"""Mask a token for logging while preserving some identifying information."""
if not token or len(token) < 8:
return '***'
return f"{token[:4]}...{token[-4:]}"
def create_safe_log_context(**kwargs) -> Dict[str, Any]:
"""Create a sanitized context dict for logging."""
context = {}
for key, value in kwargs.items():
if key.lower() in ['token', 'password', 'secret', 'key']:
context[key] = '***REDACTED***'
elif isinstance(value, str) and len(value) > 50:
# Truncate very long strings
context[key] = f"{value[:47]}..."
else:
context[key] = sanitize_log_data(value)
return context