2727def _get_secret_pattern () -> re .Pattern [str ]:
2828 """Lazy-compile strict patterns to minimize cold-boot overhead.
2929
30+ ReDoS prevented by eliminating overlapping quantifiers (+)
31+ and enforcing strict finite bounds ({1,5} and {1,200}).
32+
3033 Returns:
3134 re.Pattern[str]: Compiled regular expression for secret pattern matching.
3235 """
36+ # Group 1: The key/header name (e.g., Authorization)
37+ # Group 2: The separator and scheme (e.g., ': Bearer ')
38+ # Group 3: The actual secret (matched but NOT included in the substitution)
3339 return re .compile (
34- r"(?i)(Authorization|api[_-]key|api[_-]secret|token)([:\s=]+ (?:Bearer\s+ |Basic\s+ |Token\\s+ )?)([^\s'\"]+ )"
40+ r"(?i)(Authorization|api[_-]key|api[_-]secret|token)([:\s=]{1,5} (?:(?: Bearer|Basic|Token)\s{1,5} )?)([^\s'\"]{1,200} )"
3541 )
3642
3743
@@ -48,7 +54,7 @@ def init_poolmanager(self, connections: int, maxsize: int, block: bool = False,
4854
4955
5056class RedactingFilter (logging .Filter ):
51- """Filters out sensitive patterns from log messages and arguments ."""
57+ """Filter that intercepts and masks sensitive credentials in log records ."""
5258
5359 @override
5460 def filter (self , record : logging .LogRecord ) -> bool :
@@ -62,20 +68,28 @@ def filter(self, record: logging.LogRecord) -> bool:
6268 if isinstance (record .msg , str ):
6369 record .msg = _get_secret_pattern ().sub (r"\1\2********" , record .msg )
6470
65- # Redact arguments
66- if record .args :
67- new_args : list [Any ] = []
71+ if isinstance (record .args , tuple ) and record .args :
72+ new_args = []
6873 for arg in record .args :
6974 if isinstance (arg , str ):
7075 new_args .append (_get_secret_pattern ().sub (r"\1\2********" , arg ))
7176 else :
72- new_args .append (arg )
77+ new_args .append (arg ) # type: ignore[arg-type]
7378 record .args = tuple (new_args )
79+ elif isinstance (record .args , dict ) and record .args :
80+ new_dict_args = {}
81+ for k , v in record .args .items ():
82+ if isinstance (v , str ):
83+ new_dict_args [k ] = _get_secret_pattern ().sub (r"\1\2********" , v )
84+ else :
85+ new_dict_args [k ] = v
86+ record .args = new_dict_args
87+
7488 return True
7589
7690
7791class SecurityGuard :
78- """Centralized OWASP API security guardrails ."""
92+ """Centralized security validation and sanitization (Defense in Depth) ."""
7993
8094 _audit_hook_installed : ClassVar [bool ] = False
8195
@@ -132,11 +146,14 @@ def sanitize_log_trace(val: Any) -> str:
132146 Returns:
133147 str: The sanitized string value.
134148 """
135- s = str (val )
136- # If the input contains control characters, reject/scrub to prevent injection.
137- if _CRLF_RE .search (s ):
138- return "[INVALID_DATA_REDACTED]"
139- return s
149+ # CAST TO STRING FIRST to prevent TypeError on ints/floats
150+ val_str = str (val )
151+
152+ # Fail-fast on insanely large strings (CWE-400 Resource Exhaustion)
153+ if len (val_str ) > 1000 :
154+ val_str = val_str [:1000 ] + "... [TRUNCATED]"
155+
156+ return _CRLF_RE .sub ("" , val_str )
140157
141158 @staticmethod
142159 def check_request_security (kwargs : dict [str , Any ]) -> None :
@@ -220,8 +237,8 @@ def validate_crlf_headers(custom_headers: dict[str, str]) -> None:
220237 """
221238 for key , value in custom_headers .items ():
222239 if _CRLF_RE .search (str (value )):
223- err_msg = f"CRLF Injection detected in header '{ key } '"
224- raise ValueError (err_msg )
240+ msg = f"Security Violation: CRLF Injection detected in header '{ key } '. "
241+ raise ValueError (msg )
225242
226243 @staticmethod
227244 def validate_attachment_path (file_path : str | Path , safe_base_dir : str | Path | None = None ) -> Path :
@@ -276,5 +293,6 @@ def sanitize_segment(segment: Any) -> str:
276293 return ""
277294
278295 clean_str = str (segment ).replace ("\r " , "" ).replace ("\n " , "" )
279- # Safe is empty string to strictly encode EVERYTHING, including slashes
280- return quote (clean_str , safe = "" )
296+ # quote() ignores dots. We explicitly encode them to prevent
297+ # strict ".." traversal when injected into middle of URI templates.
298+ return quote (clean_str , safe = "" ).replace ("." , "%2E" )
0 commit comments