Repository: apache/airflow
File: shared/secrets_masker/src/airflow_shared/secrets_masker/secrets_masker.py (Python)

Code before:
            logging.INFO,
            __file__,
            1,
            "",
            (),
            exc_info=None,
            func="funcname",
        )
        return frozenset(record.__dict__).difference({"msg", "args"})

    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):
            if visited is None:
                visited = set()

            if id(exception) in visited:
                # already visited - it was redacted earlier
                return exception

            # Check depth before adding to visited to ensure we skip exceptions beyond the limit
            if len(visited) >= self.MAX_RECURSION_DEPTH:
                return RuntimeError(
                    f"Stack trace redaction hit recursion limit of {self.MAX_RECURSION_DEPTH} "
                    f"when processing exception of type {type(exception).__name__}. "
                    f"Redaction of this exception will be skipped to avoid "
                    f"infinite recursion."
                )

            visited.add(id(exception))

            exception.args = (self.redact(v) for v in exception.args)
            if exception.__context__:
                exception.__context__ = self._redact_exception_with_context_or_cause(
                    exception.__context__, visited
                )
            if exception.__cause__ and exception.__cause__ is not exception.__context__:
                exception.__cause__ = self._redact_exception_with_context_or_cause(
                    exception.__cause__, visited
                )
        return exception

    def filter(self, record) -> bool:
        if not self.is_log_masking_enabled():
            return True

        if self.ALREADY_FILTERED_FLAG in record.__dict__:
            # Filters are attached to multiple handlers and logs, keep a
            # "private" flag that stops us needing to process it more than once
            return True

Review comment (suggestion):
Small thing on the message: it says "redaction of this exception will be skipped" but IIUC the original exception is actually being replaced by this sentinel RuntimeError, not skipped. Something like "remainder of exception chain has been truncated" might be more accurate? Not sure if I'm reading this correctly though.

Code after:
            logging.INFO,
            __file__,
            1,
            "",
            (),
            exc_info=None,
            func="funcname",
        )
        return frozenset(record.__dict__).difference({"msg", "args"})

    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):
            if visited is None:
                visited = set()

            if id(exception) in visited:
                # already visited - it was redacted earlier
                return exception

            # Check depth before adding to visited to ensure we skip exceptions beyond the limit
            if len(visited) >= self.MAX_RECURSION_DEPTH:
                return RuntimeError(
                    f"Stack trace redaction hit recursion limit of {self.MAX_RECURSION_DEPTH} "
                    f"when processing exception of type {type(exception).__name__}. "
                    f"The remaining exceptions will be skipped to avoid "
                    f"infinite recursion and protect against revealing sensitive information."
                )

            visited.add(id(exception))

            exception.args = tuple(self.redact(v) for v in exception.args)
            if exception.__context__:
                exception.__context__ = self._redact_exception_with_context_or_cause(
                    exception.__context__, visited
                )
            if exception.__cause__ and exception.__cause__ is not exception.__context__:
                exception.__cause__ = self._redact_exception_with_context_or_cause(
                    exception.__cause__, visited
                )
        return exception

    def filter(self, record) -> bool:
        if not self.is_log_masking_enabled():
            return True

        if self.ALREADY_FILTERED_FLAG in record.__dict__:
            # Filters are attached to multiple handlers and logs, keep a
            # "private" flag that stops us needing to process it more than once
            return True