Skip to content

Commit 10895e5

Browse files
potiukAnton NitochkinCopilot
committed
[v3-1-test] Fix recursion depth error in _redact_exception_with_context (#61776)
* Fix recursion depth error in _redact_exception_with_context Fix recursion depth check in _redact_exception_with_context_or_cause. There are some obscure cases where exception might point to itself in cause/context - this PR protects against it. Changed name to include cause as well. Initially implemented as a fix to v2-11-test in #61254 - enhanced with the case of removal of too-deep exceptions rather than not-redacting it (and replacing it with sentinel exception explaining that reminder of the stack trace has been removed. Co-authored-by: Anton Nitochkin <nitochkin@google.com> * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- (cherry picked from commit 1533ecf) Co-authored-by: Jarek Potiuk <jarek@potiuk.com> Co-authored-by: Anton Nitochkin <nitochkin@google.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 6290a17 commit 10895e5

File tree

2 files changed

+378
-16
lines changed

2 files changed

+378
-16
lines changed

shared/secrets_masker/src/airflow_shared/secrets_masker/secrets_masker.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,15 +263,38 @@ def _record_attrs_to_ignore(self) -> Iterable[str]:
263263
)
264264
return frozenset(record.__dict__).difference({"msg", "args"})
265265

266-
def _redact_exception_with_context(self, exception):
266+
def _redact_exception_with_context_or_cause(self, exception, visited=None):
267267
# Exception class may not be modifiable (e.g. declared by an
268268
# extension module such as JDBC).
269269
with contextlib.suppress(AttributeError):
270-
exception.args = (self.redact(v) for v in exception.args)
271-
if exception.__context__:
272-
self._redact_exception_with_context(exception.__context__)
273-
if exception.__cause__ and exception.__cause__ is not exception.__context__:
274-
self._redact_exception_with_context(exception.__cause__)
270+
if visited is None:
271+
visited = set()
272+
273+
if id(exception) in visited:
274+
# already visited - it was redacted earlier
275+
return exception
276+
277+
# Check depth before adding to visited to ensure we skip exceptions beyond the limit
278+
if len(visited) >= self.MAX_RECURSION_DEPTH:
279+
return RuntimeError(
280+
f"Stack trace redaction hit recursion limit of {self.MAX_RECURSION_DEPTH} "
281+
f"when processing exception of type {type(exception).__name__}. "
282+
f"The remaining exceptions will be skipped to avoid "
283+
f"infinite recursion and protect against revealing sensitive information."
284+
)
285+
286+
visited.add(id(exception))
287+
288+
exception.args = tuple(self.redact(v) for v in exception.args)
289+
if exception.__context__:
290+
exception.__context__ = self._redact_exception_with_context_or_cause(
291+
exception.__context__, visited
292+
)
293+
if exception.__cause__ and exception.__cause__ is not exception.__context__:
294+
exception.__cause__ = self._redact_exception_with_context_or_cause(
295+
exception.__cause__, visited
296+
)
297+
return exception
275298

276299
def filter(self, record) -> bool:
277300
if not self.is_log_masking_enabled():
@@ -288,7 +311,7 @@ def filter(self, record) -> bool:
288311
record.__dict__[k] = self.redact(v)
289312
if record.exc_info and record.exc_info[1] is not None:
290313
exc = record.exc_info[1]
291-
self._redact_exception_with_context(exc)
314+
self._redact_exception_with_context_or_cause(exc)
292315
record.__dict__[self.ALREADY_FILTERED_FLAG] = True
293316

294317
return True

0 commit comments

Comments
 (0)