diff --git a/src/ansiblelint/rules/MissingFilePermissionsRule.py b/src/ansiblelint/rules/MissingFilePermissionsRule.py index b0674b638c..001b553ef4 100644 --- a/src/ansiblelint/rules/MissingFilePermissionsRule.py +++ b/src/ansiblelint/rules/MissingFilePermissionsRule.py @@ -17,6 +17,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +"""MissingFilePermissionsRule used with ansible-lint.""" +import sys from typing import TYPE_CHECKING, Any, Dict, Union from ansiblelint.rules import AnsibleLintRule @@ -86,7 +88,7 @@ def matchtask( if task['action'].get('state', None) == "absent": return False - # A symlink always has mode 0o777 + # A symlink always has mode 0777 if task['action'].get('state', None) == "link": return False @@ -105,3 +107,267 @@ def matchtask( return False return mode is None + + +if "pytest" in sys.modules: # noqa: C901 + import pytest + + SUCCESS_PERMISSIONS_PRESENT = ''' +- hosts: all + tasks: + - name: permissions not missing and numeric + file: + path: foo + mode: 0600 +''' + + SUCCESS_ABSENT_STATE = ''' +- hosts: all + tasks: + - name: permissions missing while state is absent is fine + file: + path: foo + state: absent +''' + + SUCCESS_DEFAULT_STATE = ''' +- hosts: all + tasks: + - name: permissions missing while state is file (default) is fine + file: + path: foo +''' + + SUCCESS_LINK_STATE = ''' +- hosts: all + tasks: + - name: permissions missing while state is link is fine + file: + path: foo2 + src: foo + state: link +''' + + SUCCESS_CREATE_FALSE = ''' +- hosts: all + tasks: + - name: file edit when create is false + lineinfile: + path: foo + create: false + line: some content here +''' + + SUCCESS_REPLACE = ''' +- hosts: all + tasks: + - name: replace should not require mode + replace: + path: foo +''' + + SUCCESS_RECURSE = ''' +- hosts: all + tasks: + - name: file with recursive does not require mode + file: + state: directory + recurse: yes +''' + + FAIL_PRESERVE_MODE = ''' +- hosts: all + tasks: + - name: file does not allow preserve value for mode + file: + path: foo + mode: preserve +''' + + FAIL_MISSING_PERMISSIONS_TOUCH = ''' +- hosts: all + tasks: + - name: permissions missing and might create file + file: + path: foo + state: touch +''' + + FAIL_MISSING_PERMISSIONS_DIRECTORY = ''' +- hosts: all + tasks: + - name: permissions missing and might create directory + file: + path: foo + state: directory +''' + + FAIL_LINEINFILE_CREATE = ''' +- hosts: all + tasks: + - name: lineinfile when create is true + lineinfile: + path: foo + create: true + line: some content here +''' + + FAIL_REPLACE_PRESERVE = ''' +- hosts: all + tasks: + - name: replace does not allow preserve mode + replace: + path: foo + mode: preserve +''' + + FAIL_PERMISSION_COMMENT = ''' +- hosts: all + tasks: + - name: permissions is only a comment + file: + path: foo + owner: root + group: root + state: directory + # mode: 0755 +''' + + FAIL_INI_PERMISSION = ''' +- hosts: all + tasks: + - name: permissions needed if create is used + ini_file: + path: foo + create: true +''' + + FAIL_INI_PRESERVE = ''' +- hosts: all + tasks: + - name: ini_file does not accept preserve mode + ini_file: + path: foo + create: true + mode: preserve +''' + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_success_permissions_present(rule_runner: Any) -> None: + """Permissions present and numeric.""" + results = rule_runner.run_playbook(SUCCESS_PERMISSIONS_PRESENT) + assert len(results) == 0 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_success_absent_state(rule_runner: Any) -> None: + """No permissions required if file is absent.""" + results = rule_runner.run_playbook(SUCCESS_ABSENT_STATE) + assert len(results) == 0 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_success_default_state(rule_runner: Any) -> None: + """No permissions required if default state.""" + results = rule_runner.run_playbook(SUCCESS_DEFAULT_STATE) + assert len(results) == 0 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_success_link_state(rule_runner: Any) -> None: + """No permissions required if it is a link.""" + results = rule_runner.run_playbook(SUCCESS_LINK_STATE) + assert len(results) == 0 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_success_create_false(rule_runner: Any) -> None: + """No permissions required if file is not created.""" + results = rule_runner.run_playbook(SUCCESS_CREATE_FALSE) + assert len(results) == 0 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_success_replace(rule_runner: Any) -> None: + """Replacing a file do not require mode.""" + results = rule_runner.run_playbook(SUCCESS_REPLACE) + assert len(results) == 0 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_success_recurse(rule_runner: Any) -> None: + """Do not require mode when recursing.""" + results = rule_runner.run_playbook(SUCCESS_RECURSE) + assert len(results) == 0 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_fail_preserve_mode(rule_runner: Any) -> None: + """File does not allow preserve value for mode.""" + results = rule_runner.run_playbook(FAIL_PRESERVE_MODE) + assert len(results) == 1 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_fail_missing_permissions_touch(rule_runner: Any) -> None: + """Missing permissions when possibly creating file.""" + results = rule_runner.run_playbook(FAIL_MISSING_PERMISSIONS_TOUCH) + assert len(results) == 1 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_fail_missing_permissions_directory(rule_runner: Any) -> None: + """Missing permissions when possibly creating a directory.""" + results = rule_runner.run_playbook(FAIL_MISSING_PERMISSIONS_DIRECTORY) + assert len(results) == 1 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_fail_lineinfile_create(rule_runner: Any) -> None: + """Lineinfile might create a file.""" + results = rule_runner.run_playbook(FAIL_LINEINFILE_CREATE) + assert len(results) == 1 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_fail_replace_preserve(rule_runner: Any) -> None: + """Replace does not allow preserve mode.""" + results = rule_runner.run_playbook(FAIL_REPLACE_PRESERVE) + assert len(results) == 1 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_fail_permission_comment(rule_runner: Any) -> None: + """Permissions is only a comment.""" + results = rule_runner.run_playbook(FAIL_PERMISSION_COMMENT) + assert len(results) == 1 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_fail_ini_permission(rule_runner: Any) -> None: + """Permissions needed if create is used.""" + results = rule_runner.run_playbook(FAIL_INI_PERMISSION) + assert len(results) == 1 + + @pytest.mark.parametrize( + 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] + ) + def test_fail_ini_preserve(rule_runner: Any) -> None: + """The ini_file module does not accept preserve mode.""" + results = rule_runner.run_playbook(FAIL_INI_PRESERVE) + assert len(results) == 1 diff --git a/src/ansiblelint/rules/NoLogPasswordsRule.py b/src/ansiblelint/rules/NoLogPasswordsRule.py index e8ef586a4f..bdc06d2444 100644 --- a/src/ansiblelint/rules/NoLogPasswordsRule.py +++ b/src/ansiblelint/rules/NoLogPasswordsRule.py @@ -138,6 +138,7 @@ def matchtask( user: name: lint password_lock: yes + # user_password: "this is a comment, not a password" ''' PASSWORD_LOCK_FALSE = ''' diff --git a/test/TestMissingFilePermissionsRule.py b/test/TestMissingFilePermissionsRule.py deleted file mode 100644 index ea544e03a2..0000000000 --- a/test/TestMissingFilePermissionsRule.py +++ /dev/null @@ -1,118 +0,0 @@ -# Copyright (c) 2020 Sorin Sbarnea -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -"""MissingFilePermissionsRule tests.""" -import pytest - -from ansiblelint.rules.MissingFilePermissionsRule import MissingFilePermissionsRule - -SUCCESS_TASKS = '''\ ---- -- hosts: hosts - tasks: - - name: permissions not missing and numeric - file: - path: foo - mode: 0600 - - name: permissions missing while state is absent is fine - file: - path: foo - state: absent - - name: permissions missing while state is file (default) is fine - file: - path: foo - - name: permissions missing while state is link is fine - file: - path: foo2 - src: foo - state: link - - name: file edit when create is false - lineinfile: - path: foo - create: false - line: some content here - - name: replace should not require mode - replace: - path: foo - - name: file with recursive does not require mode - file: - state: directory - recurse: yes -''' - -FAIL_TASKS = '''\ ---- -- hosts: hosts - tasks: - - name: file does not allow preserve value for mode - file: - path: foo - mode: preserve - - name: permissions missing and might create file - file: - path: foo - state: touch - - name: permissions missing and might create directory - file: - path: foo - state: directory - # - name: permissions needed if create is used - # ini_file: - # path: foo - # create: true - - name: lineinfile when create is true - lineinfile: - path: foo - create: true - line: some content here - - name: replace does not allow preserve mode - replace: - path: foo - mode: preserve - # - name: ini_file does not accept preserve mode - # ini_file: - # path: foo - # create: true - # mode: preserve -''' - - -@pytest.mark.parametrize( - 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] -) -def test_success(rule_runner) -> None: - """Validate that mode presence avoids hitting the rule.""" - results = rule_runner.run_playbook(SUCCESS_TASKS) - assert len(results) == 0 - - -@pytest.mark.parametrize( - 'rule_runner', (MissingFilePermissionsRule,), indirect=['rule_runner'] -) -def test_fail(rule_runner) -> None: - """Validate that missing mode triggers the rule.""" - results = rule_runner.run_playbook(FAIL_TASKS) - assert len(results) == 5 - assert results[0].linenumber == 4 - assert results[1].linenumber == 8 - assert results[2].linenumber == 12 - # assert results[3].linenumber == 16 - assert results[3].linenumber == 20 - assert results[4].linenumber == 25 - # assert results[6].linenumber == 29