File size: 5,254 Bytes
6d12932
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Safe logging utilities to prevent sensitive data leakage.



This module provides utilities for safely logging exceptions without exposing

sensitive information like passwords, tokens, or PII in application logs.

"""
import logging
from typing import Optional


def log_exception_safe(

    logger: logging.Logger,

    message: str,

    exception: Exception,

    level: str = "error",

    include_type: bool = True

) -> None:
    """

    Safely log an exception without exposing sensitive data.

    

    Instead of logging the full exception message (which may contain sensitive data),

    this function logs only the exception type or a generic message.

    

    Args:

        logger: Logger instance to use

        message: Context message describing what failed

        exception: Exception object to log safely

        level: Log level (error, warning, info, debug)

        include_type: Whether to include exception type name in the log

        

    Example:

        >>> try:

        >>>     create_user("admin", "SecretPassword123!")

        >>> except Exception as e:

        >>>     log_exception_safe(logger, "User creation failed", e)

        >>> # Logs: "User creation failed: IntegrityError"

        >>> # Does NOT log: "User creation failed: UNIQUE constraint failed: password=SecretPassword123!"

    """
    if include_type:
        safe_message = f"{message}: {type(exception).__name__}"
    else:
        safe_message = message
    
    log_func = getattr(logger, level.lower())
    log_func(safe_message)


def get_safe_error_message(exception: Exception) -> str:
    """

    Get a safe error message from an exception.

    

    Returns only the exception type name, not the potentially sensitive message.

    

    Args:

        exception: Exception object

        

    Returns:

        String containing only the exception type name

        

    Example:

        >>> try:

        >>>     raise ValueError("password=secret123")

        >>> except Exception as e:

        >>>     safe_msg = get_safe_error_message(e)

        >>> # safe_msg = "ValueError"

    """
    return type(exception).__name__


def mask_identifier(identifier: str, prefix: str = "id") -> str:
    """

    Mask an identifier for safe logging.

    

    Completely masks the identifier to prevent any sensitive data leakage.

    This security measure ensures no part of the identifier (which may contain

    or be derived from sensitive information) is exposed in logs.

    The prefix parameter is used to identify the type of identifier in logs.

    Completely masks the identifier value to prevent any sensitive data exposure.

    Returns only the prefix with masked content, without revealing any characters

    from the actual identifier.

    

    Args:

        identifier: The identifier to mask (patient_id, username, etc.)

        prefix: Prefix to use for the masked value (e.g., 'pat', 'user', 'id')

        

    Returns:

        Masked identifier string in the format: prefix_****

        Masked identifier string in format: "{prefix}_****"

        

    Example:

        >>> mask_identifier("patient12345", "pat")

        'pat_****'

        >>> mask_identifier("admin", "user")

        'user_****'

        >>> mask_identifier("", "id")  # Even empty strings are masked consistently

        'id_****'

    """
    return f"{prefix}_****"


def safe_log_info(

    logger: logging.Logger,

    message: str,

    **identifiers

) -> None:
    """

    Safely log an info message with masked identifiers.

    

    Args:

        logger: Logger instance to use

        message: Message template with {placeholders}

        **identifiers: Key-value pairs of identifiers to mask

        

    Example:

        >>> safe_log_info(logger, "Processing patient {patient_id}", 

        ...               patient_id=("patient12345", "pat"))

        # Logs: "Processing patient pat_****"

    """
    masked_values = {}
    for key, value in identifiers.items():
        if isinstance(value, tuple):
            identifier, prefix = value
            masked_values[key] = mask_identifier(identifier, prefix)
        else:
            masked_values[key] = mask_identifier(value, key)
    
    logger.info(message.format(**masked_values))


def safe_log_warning(

    logger: logging.Logger,

    message: str,

    **identifiers

) -> None:
    """

    Safely log a warning message with masked identifiers.

    

    Args:

        logger: Logger instance to use

        message: Message template with {placeholders}

        **identifiers: Key-value pairs of identifiers to mask

        

    Example:

        >>> safe_log_warning(logger, "No data for patient {patient_id}", 

        ...                  patient_id=("67890", "pat"))

        # Logs: "No data for patient pat_****"

    """
    masked_values = {}
    for key, value in identifiers.items():
        if isinstance(value, tuple):
            identifier, prefix = value
            masked_values[key] = mask_identifier(identifier, prefix)
        else:
            masked_values[key] = mask_identifier(value, key)
    
    logger.warning(message.format(**masked_values))