Skip to content

Conversation

@potiuk
Copy link
Member

@potiuk potiuk commented Feb 11, 2026

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.


Was generative AI tooling used to co-author this PR?
  • Yes (please specify the tool below)

Generated-by: Copilot + Claude Sonnet 4.5 following the guidelines


  • Read the Pull Request Guidelines for more information. Note: commit author/co-author name and email in commits become permanently public when merged.
  • For fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
  • When adding dependency, check compliance with the ASF 3rd Party License Policy.
  • For significant user-facing changes create newsfragment: {pr_number}.significant.rst or {issue_number}.significant.rst, in airflow-core/newsfragments.

@potiuk potiuk added backport-to-v3-1-test Mark PR with this label to backport to v3-1-test branch backport-to-v2-11-test Mark PR with this label to backport to v2-11-test branch labels Feb 11, 2026
@potiuk potiuk force-pushed the avoid-recursion-limit-for-exceptions-in-secrets-masker branch 2 times, most recently from 42caffe to 5326a05 Compare February 11, 2026 14:16
@potiuk potiuk changed the title Add comprehensive tests for _redact_exception_with_context_or_cause Fix recursion depth error in _redact_exception_with_context Feb 11, 2026
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 apache#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 <[email protected]>
@potiuk potiuk force-pushed the avoid-recursion-limit-for-exceptions-in-secrets-masker branch from 5326a05 to cc6b24c Compare February 11, 2026 20:28
@jscheffl jscheffl requested a review from Copilot February 11, 2026 21:17
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the SecretsMasker exception redaction logic to safely handle circular __context__/__cause__ chains and to cap traversal using a recursion/visit limit, preventing RecursionError during log masking.

Changes:

  • Replace _redact_exception_with_context with _redact_exception_with_context_or_cause, adding cycle detection and a max-visit limit with a sentinel exception when truncating.
  • Update the log filter to call the new redaction helper.
  • Add extensive unit tests covering context/cause chaining, cycles, branching, and depth-limit behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
shared/secrets_masker/src/airflow_shared/secrets_masker/secrets_masker.py Introduces cycle/limit-aware exception redaction and switches the filter to use it.
shared/secrets_masker/tests/secrets_masker/test_secrets_masker.py Adds new tests for the updated exception redaction behavior across multiple chaining scenarios.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +263 to +268
def _redact_exception_with_context_or_cause(self, exception, visited=None):
# Exception class may not be modifiable (e.g. declared by an
# extension module such as JDBC).
with contextlib.suppress(AttributeError):
exception.args = (self.redact(v) for v in exception.args)
if exception.__context__:
self._redact_exception_with_context(exception.__context__)
if exception.__cause__ and exception.__cause__ is not exception.__context__:
self._redact_exception_with_context(exception.__cause__)
if visited is None:
visited = set()
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contextlib.suppress(AttributeError) currently wraps the entire traversal, not just the exception.args assignment. If an exception type has non-settable args (the comment mentions extension-module exceptions), the AttributeError will cause the function to skip redacting/truncating __context__/__cause__, potentially leaving sensitive values unredacted. Narrow the suppress(AttributeError) scope to only the exception.args = ... line (and optionally to assignments to __context__/__cause__ if needed), while keeping visited/recursion-limit logic outside the suppress block.

Copilot uses AI. Check for mistakes.

def test_redact_exception_with_context_simple(self):
"""
Test _redact_exception_with_context with a simple exception without context.
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This docstring/test text still refers to _redact_exception_with_context, but the method under test is now _redact_exception_with_context_or_cause. Please rename the wording here to match the new method name (and mention both context/cause if that’s what’s being exercised).

Suggested change
Test _redact_exception_with_context with a simple exception without context.
Test _redact_exception_with_context_or_cause with a simple exception without context or cause.

Copilot uses AI. Check for mistakes.
def test_redact_exception_with_max_context_recursion_depth(self):
"""
Test _redact_exception_with_context respects MAX_RECURSION_DEPTH.
Exceptions beyond the depth limit should be skipped (not redacted).
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring here says exceptions beyond the depth limit are "skipped (not redacted)", but the implementation replaces the truncated tail with a sentinel exception (and drops further chaining). Update the docstring to reflect the sentinel/truncation behavior so the test intent is accurate.

Suggested change
Exceptions beyond the depth limit should be skipped (not redacted).
Once the depth limit is reached, the remaining exception chain is not traversed;
instead, it is truncated and replaced with a sentinel exception indicating the
recursion limit was hit, and further chaining is dropped.

Copilot uses AI. Check for mistakes.
Comment on lines +539 to +543
def test_redact_exception_with_immutable_args(self):
"""
Test _redact_exception_with_context handles exceptions with immutable args gracefully.
"""
masker = SecretsMasker()
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_redact_exception_with_immutable_args only asserts the method doesn’t crash. Since the production logic should still traverse/redact __context__/__cause__ even when args can’t be set, this test won’t catch regressions where an AttributeError stops redaction of chained exceptions. Consider adding a __context__/__cause__ containing a secret and asserting it still gets redacted/truncated.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@jscheffl jscheffl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wanted to have AI used again. Not too bad as review. But approved anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-to-v2-11-test Mark PR with this label to backport to v2-11-test branch backport-to-v3-1-test Mark PR with this label to backport to v3-1-test branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants