You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by ka...@apache.org on 2020/11/18 13:50:23 UTC

[airflow] branch v1-10-test updated: Mask Password in Log table when using the CLI (#11468)

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

kaxilnaik pushed a commit to branch v1-10-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v1-10-test by this push:
     new 3cd9e9f  Mask Password in Log table when using the CLI (#11468)
3cd9e9f is described below

commit 3cd9e9fb868547b6fb82fc84c78622daa22ca00f
Author: Kaxil Naik <ka...@gmail.com>
AuthorDate: Mon Oct 12 19:27:01 2020 +0100

    Mask Password in Log table when using the CLI (#11468)
    
    (cherry picked from commit 4e32546faf227a6497ce8b282fff7450cae6f665)
---
 airflow/utils/cli.py         | 13 +++++++++++-
 tests/utils/test_cli_util.py | 48 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/airflow/utils/cli.py b/airflow/utils/cli.py
index 9018407..31c8c62 100644
--- a/airflow/utils/cli.py
+++ b/airflow/utils/cli.py
@@ -99,9 +99,20 @@ def _build_metrics(func_name, namespace):
     :param namespace: Namespace instance from argparse
     :return: dict with metrics
     """
+    sensitive_fields = {'-p', '--password', '--conn-password'}
+    full_command = list(sys.argv)
+    for idx, command in enumerate(full_command):  # pylint: disable=too-many-nested-blocks
+        if command in sensitive_fields:
+            # For cases when password is passed as "--password xyz" (with space between key and value)
+            full_command[idx + 1] = "*" * 8
+        else:
+            # For cases when password is passed as "--password=xyz" (with '=' between key and value)
+            for sensitive_field in sensitive_fields:
+                if command.startswith('{}='.format(sensitive_field)):
+                    full_command[idx] = '{}={}'.format(sensitive_field, "*" * 8)
 
     metrics = {'sub_command': func_name, 'start_datetime': datetime.utcnow(),
-               'full_command': '{}'.format(list(sys.argv)), 'user': getpass.getuser()}
+               'full_command': '{}'.format(full_command), 'user': getpass.getuser()}
 
     assert isinstance(namespace, Namespace)
     tmp_dic = vars(namespace)
diff --git a/tests/utils/test_cli_util.py b/tests/utils/test_cli_util.py
index 6a411fa..fc7b771 100644
--- a/tests/utils/test_cli_util.py
+++ b/tests/utils/test_cli_util.py
@@ -17,14 +17,18 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-
+import json
 import os
+import sys
 import unittest
 from argparse import Namespace
 from contextlib import contextmanager
 from datetime import datetime
 
+from parameterized import parameterized
+
 from airflow.utils import cli, cli_action_loggers
+from tests.compat import mock
 
 
 class CliUtilTest(unittest.TestCase):
@@ -71,6 +75,48 @@ class CliUtilTest(unittest.TestCase):
         with fail_action_logger_callback():
             success_func(Namespace())
 
+    @parameterized.expand(
+        [
+            (
+                "airflow create_user -u test2 -l doe -f jon -e jdoe@apache.org -r admin --password test",
+                "airflow create_user -u test2 -l doe -f jon -e jdoe@apache.org -r admin --password ********"
+            ),
+            (
+                "airflow create_user -u test2 -l doe -f jon -e jdoe@apache.org -r admin -p test",
+                "airflow create_user -u test2 -l doe -f jon -e jdoe@apache.org -r admin -p ********"
+            ),
+            (
+                "airflow create_user -u test2 -l doe -f jon -e jdoe@apache.org -r admin --password=test",
+                "airflow create_user -u test2 -l doe -f jon -e jdoe@apache.org -r admin --password=********"
+            ),
+            (
+                "airflow create_user -u test2 -l doe -f jon -e jdoe@apache.org -r admin -p=test",
+                "airflow create_user -u test2 -l doe -f jon -e jdoe@apache.org -r admin -p=********"
+            ),
+            (
+                "airflow connections --add dsfs --conn-login asd --conn-password test --conn-type google",
+                "airflow connections --add dsfs --conn-login asd --conn-password ******** --conn-type google",
+            )
+        ]
+    )
+    def test_cli_create_user_supplied_password_is_masked(self, given_command, expected_masked_command):
+        args = given_command.split()
+
+        expected_command = expected_masked_command.split()
+
+        exec_date = datetime.utcnow()
+        namespace = Namespace(dag_id='foo', task_id='bar', subcommand='test', execution_date=exec_date)
+        with mock.patch.object(sys, "argv", args):
+            metrics = cli._build_metrics(args[1], namespace)
+
+        self.assertTrue(metrics.get('start_datetime') <= datetime.utcnow())
+
+        log = metrics.get('log')
+        command = json.loads(log.extra).get('full_command')  # type: str
+        # Replace single quotes to double quotes to avoid json decode error
+        command = json.loads(command.replace("'", '"'))
+        self.assertEqual(command, expected_command)
+
 
 @contextmanager
 def fail_action_logger_callback():