1010--------------------------------------------------------------------------------
1111"""
1212
13+ import json
1314import logging
1415import os
16+ import re
1517
1618os .system ("" ) # nosec bandit B605 B607
1719
20+ SENSITIVE_FIELDS = [
21+ "secret" ,
22+ "mesh_psk" ,
23+ "psk" ,
24+ "default_psk" ,
25+ "broadnet_password" ,
26+ "gupshup_password" ,
27+ "lte_password" ,
28+ "password" ,
29+ "poser_password" ,
30+ "puzzel_password" ,
31+ "authentication_password" ,
32+ "encryption_password" ,
33+ "root_password" ,
34+ "mist_password" ,
35+ "apitoken" ,
36+ "cp_api_key" ,
37+ "ecm_api_key" ,
38+ "key" ,
39+ "fips_zeroize_password" ,
40+ "passphrase" ,
41+ "old_passphrase" ,
42+ "oauth2_client_secret" ,
43+ "oauth2_password" ,
44+ "splunk_token" ,
45+ "conductor_token" ,
46+ ]
47+
1848
1949def magenta (text ):
50+ """
51+ Docstring for magenta
52+
53+ :param text: Description
54+ """
2055 return "\033 [0;35m" + text + "\033 [0m"
2156
2257
2358def red (text ):
59+ """
60+ Docstring for red
61+
62+ :param text: Description
63+ """
2464 return "\033 [0;31m" + text + "\033 [0m"
2565
2666
2767def yellow (text ):
68+ """
69+ Docstring for yellow
70+
71+ :param text: Description
72+ """
2873 return "\033 [0;33m" + text + "\033 [0m"
2974
3075
3176def green (text ):
77+ """
78+ Docstring for green
79+
80+ :param text: Description
81+ """
3282 return "\033 [0;32m" + text + "\033 [0m"
3383
3484
3585def white (text ):
86+ """
87+ Docstring for white
88+
89+ :param text: Description
90+ """
3691 return "\033 [0;37m" + text + "\033 [0m"
3792
3893
3994def cyan (text ):
95+ """
96+ Docstring for cyan
97+
98+ :param text: Description
99+ """
40100 return "\033 [0;36m" + text + "\033 [0m"
41101
42102
43103def blue (text ):
104+ """
105+ Docstring for blue
106+
107+ :param text: Description
108+ """
44109 return "\033 [0;34m" + text + "\033 [0m"
45110
46111
47112class Console :
113+ """
114+ Console class for logging messages to the console with different log levels
115+ and sanitizing sensitive fields in the messages.
116+ """
117+
48118 def __init__ (self , level : int = 20 ) -> None :
49119 self .level : int = level
120+ self .sensitive_fields = SENSITIVE_FIELDS
121+ # compile regex pattern to match sensitive fields
122+ # example pattern: r'("password"\s*:\s*)"(.*?)"'
123+ # matches "password": "value"
124+ # and captures "password": and "value" separately
125+ # to replace "value" with "******"
126+ # case insensitive
127+ self .sensitive_pattern = re .compile (
128+ r'(["\']('
129+ + "|" .join (self .sensitive_fields )
130+ + r')["\']?\s*:\s*)["\']([^"\']*)["\']' ,
131+ re .IGNORECASE ,
132+ )
133+
134+ def sanitize (self , data ) -> str :
135+ """
136+ sanitize sensitive fields in a dictionary or string
137+
138+ PARAMS
139+ -----------
140+ data : str
141+
142+ RETURNS
143+ -----------
144+ sanitized data : str
145+ """
146+ if not isinstance (data , str ):
147+ data_str = json .dumps (data )
148+ else :
149+ data_str = str (data )
150+ sanitized_data = self .sensitive_pattern .sub (r'\1"******"' , data_str )
151+ return sanitized_data
152+
153+ def critical (self , message ) -> None :
154+ """
155+ Docstring for critical
50156
51- def critical (self , message : str ) -> None :
157+ :param self: Description
158+ :param message: Description
159+ :type message: str
160+ """
52161 if self .level <= 50 and self .level > 0 :
53- print (f"[{ magenta ('CRITICAL ' )} ] { message } " )
162+ print (f"[{ magenta ('CRITICAL ' )} ] { self .sanitize (message )} " )
163+
164+ def error (self , message ) -> None :
165+ """
166+ Docstring for error
54167
55- def error (self , message : str ) -> None :
168+ :param self: Description
169+ :param message: Description
170+ :type message: str
171+ """
56172 if self .level <= 40 and self .level > 0 :
57- print (f"[{ red (' ERROR ' )} ] { message } " )
173+ print (f"[{ red (' ERROR ' )} ] { self . sanitize ( message ) } " )
58174
59- def warning (self , message : str ) -> None :
175+ def warning (self , message ) -> None :
176+ """
177+ Docstring for warning
178+
179+ :param self: Description
180+ :param message: Description
181+ :type message: str
182+ """
60183 if self .level <= 30 and self .level > 0 :
61- print (f"[{ yellow (' WARNING ' )} ] { message } " )
184+ print (f"[{ yellow (' WARNING ' )} ] { self .sanitize (message )} " )
185+
186+ def info (self , message ) -> None :
187+ """
188+ Docstring for info
62189
63- def info (self , message : str ) -> None :
190+ :param self: Description
191+ :param message: Description
192+ :type message: str
193+ """
64194 if self .level <= 20 and self .level > 0 :
65- print (f"[{ green (' INFO ' )} ] { message } " )
195+ print (f"[{ green (' INFO ' )} ] { self .sanitize (message )} " )
196+
197+ def debug (self , message ) -> None :
198+ """
199+ Docstring for debug
66200
67- def debug (self , message : str ) -> None :
201+ :param self: Description
202+ :param message: Description
203+ :type message: str
204+ """
68205 if self .level <= 10 and self .level > 0 :
69- print (f"[{ white ('DEBUG ' )} ] { message } " )
206+ print (f"[{ white ('DEBUG ' )} ] { self . sanitize ( message ) } " )
70207
71208 def _set_log_level (
72209 self , console_log_level : int = 20 , logging_log_level : int = 10
@@ -91,6 +228,21 @@ def _set_log_level(
91228 logger .setLevel (logging_log_level )
92229
93230
231+ class LogSanitizer (logging .Filter ):
232+ """
233+ Logging filter that sanitizes sensitive fields in log messages
234+ """
235+
236+ def __init__ (self ):
237+ super ().__init__ ()
238+ self .console = Console ()
239+
240+ def filter (self , record ):
241+ record .msg = self .console .sanitize (record .msg )
242+ return True
243+
244+
94245console = Console ()
95246logger = logging .getLogger ("mistapi" )
96247logger .setLevel (logging .DEBUG )
248+ logger .addFilter (LogSanitizer ())
0 commit comments