27 lines
1.0 KiB
Python
27 lines
1.0 KiB
Python
"""Logging helpers that prevent sensitive values from reaching application logs."""
|
|
import logging
|
|
import re
|
|
|
|
_SENSITIVE_KEY = r"(?:password|passwd|secret|token|authorization|credential|client_secret|corpsecret|code)"
|
|
_KEY_VALUE_PATTERN = re.compile(
|
|
rf"(?i)([\"']?{_SENSITIVE_KEY}[\"']?\s*[:=]\s*)([\"']?)([^\s,;\]\}}\"']+)([\"']?)"
|
|
)
|
|
_BEARER_PATTERN = re.compile(r"(?i)(authorization\s*[:=]\s*bearer\s+)[^\s,;]+")
|
|
_QUERY_PATTERN = re.compile(rf"(?i)([?&]{_SENSITIVE_KEY}=)[^&\s]+")
|
|
|
|
|
|
def redact_log_message(message: str) -> str:
|
|
"""Mask common secret formats while keeping enough context for operations."""
|
|
masked = _BEARER_PATTERN.sub(r"\1***", message)
|
|
masked = _QUERY_PATTERN.sub(r"\1***", masked)
|
|
return _KEY_VALUE_PATTERN.sub(r"\1\2***\4", masked)
|
|
|
|
|
|
class SensitiveDataFilter(logging.Filter):
|
|
"""Redact sensitive values after interpolation and before formatter output."""
|
|
|
|
def filter(self, record: logging.LogRecord) -> bool:
|
|
record.msg = redact_log_message(record.getMessage())
|
|
record.args = ()
|
|
return True
|