From 110aa83ee8fb06f63ce1dbfd73c4734bbe26c419 Mon Sep 17 00:00:00 2001 From: Dmitriy Rabotyagov Date: Wed, 19 May 2021 13:58:01 +0300 Subject: [PATCH] Add rule for checking no_log is set It's pretty important to ensure, that password won't be logged and thus exposed in the output. So we add linter rule that will check that all tasks, that have "password" in argument are not logged. Signed-Off-By: Dmitriy Rabotyagov --- .../playbooks/no-log-passwords-failure.yml | 18 +++++++ .../playbooks/no-log-passwords-success.yml | 13 +++++ src/ansiblelint/rules/NoLogPasswordsRule.py | 51 +++++++++++++++++++ test/TestNoLogPasswordsRule.py | 23 +++++++++ 4 files changed, 105 insertions(+) create mode 100644 examples/playbooks/no-log-passwords-failure.yml create mode 100644 examples/playbooks/no-log-passwords-success.yml create mode 100644 src/ansiblelint/rules/NoLogPasswordsRule.py create mode 100644 test/TestNoLogPasswordsRule.py diff --git a/examples/playbooks/no-log-passwords-failure.yml b/examples/playbooks/no-log-passwords-failure.yml new file mode 100644 index 00000000000..4e4b822497d --- /dev/null +++ b/examples/playbooks/no-log-passwords-failure.yml @@ -0,0 +1,18 @@ +- tasks: + - name: Fail no_log isn't used + user: + name: bidule + password: "wow" + state: absent + - name: Fail when no_log is set to False + user: + name: bidule + password: "wow" + state: absent + no_log: False + - name: Fail when no_log is set to no + user: + name: bidule + password: "wow" + state: absent + no_log: no diff --git a/examples/playbooks/no-log-passwords-success.yml b/examples/playbooks/no-log-passwords-success.yml new file mode 100644 index 00000000000..c0dcdd24911 --- /dev/null +++ b/examples/playbooks/no-log-passwords-success.yml @@ -0,0 +1,13 @@ +- tasks: + - name: Succeed when no_log is set to yes + user: + name: bidule + password: "wow" + state: absent + no_log: yes + - name: Succeed when no_log is set to True + user: + name: bidule + password: "wow" + state: absent + no_log: True diff --git a/src/ansiblelint/rules/NoLogPasswordsRule.py b/src/ansiblelint/rules/NoLogPasswordsRule.py new file mode 100644 index 00000000000..8f64f5516dc --- /dev/null +++ b/src/ansiblelint/rules/NoLogPasswordsRule.py @@ -0,0 +1,51 @@ +# Copyright 2018, Rackspace US, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import TYPE_CHECKING, Any, Dict, Union + +from ansiblelint.rules import AnsibleLintRule +from ansiblelint.utils import convert_to_boolean + +if TYPE_CHECKING: + from typing import Optional + + from ansiblelint.file_utils import Lintable + + +class NoLogPasswordsRule(AnsibleLintRule): + id = "no-log-password" + shortdesc = "password should not be logged." + description = ( + "All the modules that take a password argument should fail " + "if no_log is not set or set to False in the task." + ) + severity = 'LOW' + tags = ["passwords", "experimental"] + version_added = "v5.0.9" + + def matchtask( + self, task: Dict[str, Any], file: 'Optional[Lintable]' = None + ) -> Union[bool, str]: + + for param in task["action"].keys(): + if 'password' in param: + has_password = True + break + else: + has_password = False + + # No no_log and no_log: False behave the same way + # and should return a failure (return True), so we + # need to invert the boolean + return bool(has_password and not convert_to_boolean(task['action'].get('no_log', False))) diff --git a/test/TestNoLogPasswordsRule.py b/test/TestNoLogPasswordsRule.py new file mode 100644 index 00000000000..9d45336702c --- /dev/null +++ b/test/TestNoLogPasswordsRule.py @@ -0,0 +1,23 @@ +import unittest + +from ansiblelint.rules import RulesCollection +from ansiblelint.runner import Runner +from ansiblelint.rules.NoLogPasswordsRule import NoLogPasswordsRule + + +class TestNoLogPasswordsRule(unittest.TestCase): + collection = RulesCollection() + + def setUp(self): + self.collection.register(NoLogPasswordsRule()) + + def test_file_positive(self): + success = 'examples/playbooks/no-log-passwords-success.yml' + good_runner = Runner(success, rules=self.collection) + self.assertEqual([], good_runner.run()) + + def test_file_negative(self): + failure = 'examples/playbooks/no-log-passwords-failure.yml' + bad_runner = Runner(failure, rules=self.collection) + errs = bad_runner.run() + self.assertEqual(3, len(errs))