from __future__ import annotations import re from typing import Any REDACTED = "[REDACTED]" SENSITIVE_KEY_PARTS = ( "authorization", "api_key", "apikey", "access_token", "refresh_token", "token", "password", "secret", "cookie", "set-cookie", ) BEARER_RE = re.compile(r"(?i)\bBearer\s+[A-Za-z0-9._~+/=-]+") def _looks_sensitive_key(key: str) -> bool: lowered = key.strip().lower() return any(part in lowered for part in SENSITIVE_KEY_PARTS) def _sanitize_string(value: str) -> str: return BEARER_RE.sub("Bearer " + REDACTED, value) def sanitize_for_logging(value: Any) -> Any: if isinstance(value, dict): cleaned: dict[str, Any] = {} for key, item in value.items(): key_text = str(key) if _looks_sensitive_key(key_text): cleaned[key_text] = REDACTED else: cleaned[key_text] = sanitize_for_logging(item) return cleaned if isinstance(value, list): return [sanitize_for_logging(item) for item in value] if isinstance(value, tuple): return [sanitize_for_logging(item) for item in value] if isinstance(value, str): return _sanitize_string(value) return value