This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch v2-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v2-1-test by this push:
     new 73ff5a8  Fix redacting secrets in context exceptions. (#17618)
73ff5a8 is described below

commit 73ff5a89742ec0ebd4097d3e0751c16b0016e14e
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sat Aug 14 22:49:03 2021 +0200

    Fix redacting secrets in context exceptions. (#17618)
    
    * Fix redacting secrets in context exceptions.
    
    Secret masking did not work in implicit and
    explicit context exceptions (see
    https://www.python.org/dev/peps/pep-3134/)
    When there was a `try/except/raise` sequence,
    or `raise ... from` exception - the original
    exceptions were not redacted.
    
    Related: #17604
    
    (cherry picked from commit 6df3ee7997e335eb7d353d4ec0f5c2dd3a0e0d26)
---
 airflow/utils/log/secrets_masker.py    | 10 ++++-
 tests/utils/log/test_secrets_masker.py | 76 ++++++++++++++++++++++++++++++++++
 2 files changed, 84 insertions(+), 2 deletions(-)

diff --git a/airflow/utils/log/secrets_masker.py 
b/airflow/utils/log/secrets_masker.py
index 0ce0424..f8fbb35 100644
--- a/airflow/utils/log/secrets_masker.py
+++ b/airflow/utils/log/secrets_masker.py
@@ -144,6 +144,13 @@ class SecretsMasker(logging.Filter):
         )
         return frozenset(record.__dict__).difference({'msg', 'args'})
 
+    def _redact_exception_with_context(self, exception):
+        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__)
+
     def filter(self, record) -> bool:
         if self.ALREADY_FILTERED_FLAG in record.__dict__:
             # Filters are attached to multiple handlers and logs, keep a
@@ -157,8 +164,7 @@ class SecretsMasker(logging.Filter):
                 record.__dict__[k] = self.redact(v)
             if record.exc_info and record.exc_info[1] is not None:
                 exc = record.exc_info[1]
-                # I'm not sure if this is a good idea!
-                exc.args = (self.redact(v) for v in exc.args)
+                self._redact_exception_with_context(exc)
         record.__dict__[self.ALREADY_FILTERED_FLAG] = True
 
         return True
diff --git a/tests/utils/log/test_secrets_masker.py 
b/tests/utils/log/test_secrets_masker.py
index 78a10b5..e60bdc6 100644
--- a/tests/utils/log/test_secrets_masker.py
+++ b/tests/utils/log/test_secrets_masker.py
@@ -25,6 +25,8 @@ import pytest
 from airflow.utils.log.secrets_masker import SecretsMasker, 
should_hide_value_for_key
 from tests.test_utils.config import conf_vars
 
+p = "password"
+
 
 @pytest.fixture
 def logger(caplog):
@@ -145,6 +147,80 @@ class TestSecretsMasker:
             """
         )
 
+    def test_masking_in_implicit_context_exceptions(self, logger, caplog):
+        """
+        Show that redacting password works in context exceptions.
+        """
+        try:
+            try:
+                try:
+                    raise RuntimeError(f"Cannot connect to user:{p}")
+                except RuntimeError as ex1:
+                    raise RuntimeError(f'Exception: {ex1}')
+            except RuntimeError as ex2:
+                raise RuntimeError(f'Exception: {ex2}')
+        except RuntimeError:
+            logger.exception("Err")
+
+        line = lineno() - 8
+
+        assert caplog.text == textwrap.dedent(
+            f"""\
+            ERROR Err
+            Traceback (most recent call last):
+              File ".../test_secrets_masker.py", line {line}, in 
test_masking_in_implicit_context_exceptions
+                raise RuntimeError(f"Cannot connect to user:{{p}}")
+            RuntimeError: Cannot connect to user:***
+
+            During handling of the above exception, another exception occurred:
+
+            Traceback (most recent call last):
+              File ".../test_secrets_masker.py", line {line+2}, in 
test_masking_in_implicit_context_exceptions
+                raise RuntimeError(f'Exception: {{ex1}}')
+            RuntimeError: Exception: Cannot connect to user:***
+
+            During handling of the above exception, another exception occurred:
+
+            Traceback (most recent call last):
+              File ".../test_secrets_masker.py", line {line+4}, in 
test_masking_in_implicit_context_exceptions
+                raise RuntimeError(f'Exception: {{ex2}}')
+            RuntimeError: Exception: Exception: Cannot connect to user:***
+            """
+        )
+
+    def test_masking_in_explicit_context_exceptions(self, logger, caplog):
+        """
+        Show that redacting password works in context exceptions.
+        """
+        exception = None
+        try:
+            raise RuntimeError(f"Cannot connect to user:{p}")
+        except RuntimeError as ex:
+            exception = ex
+        try:
+            raise RuntimeError(f'Exception: {exception}') from exception
+        except RuntimeError:
+            logger.exception("Err")
+
+        line = lineno() - 8
+
+        assert caplog.text == textwrap.dedent(
+            f"""\
+            ERROR Err
+            Traceback (most recent call last):
+              File ".../test_secrets_masker.py", line {line}, in 
test_masking_in_explicit_context_exceptions
+                raise RuntimeError(f"Cannot connect to user:{{p}}")
+            RuntimeError: Cannot connect to user:***
+
+            The above exception was the direct cause of the following 
exception:
+
+            Traceback (most recent call last):
+              File ".../test_secrets_masker.py", line {line+4}, in 
test_masking_in_explicit_context_exceptions
+                raise RuntimeError(f'Exception: {{exception}}') from exception
+            RuntimeError: Exception: Cannot connect to user:***
+            """
+        )
+
     @pytest.mark.parametrize(
         ("name", "value", "expected_mask"),
         [

Reply via email to