diff --git a/bandit/core/utils.py b/bandit/core/utils.py index 32d9d4965..ffeeb019c 100644 --- a/bandit/core/utils.py +++ b/bandit/core/utils.py @@ -19,7 +19,7 @@ def _get_attr_qual_name(node, aliases): - """Get a the full name for the attribute node. + """Get the full name for the attribute node. This will resolve a pseudo-qualified name for the attribute rooted at node as long as all the deeper nodes are Names or diff --git a/bandit/plugins/general_bad_file_permissions.py b/bandit/plugins/general_bad_file_permissions.py index 7d3fce4df..2b3700849 100644 --- a/bandit/plugins/general_bad_file_permissions.py +++ b/bandit/plugins/general_bad_file_permissions.py @@ -21,21 +21,28 @@ .. code-block:: none - >> Issue: Probable insecure usage of temp file/directory. - Severity: Medium Confidence: Medium + >> Issue: Chmod setting a permissive mask 0o664 on file (/etc/passwd). + Severity: Medium Confidence: High CWE: CWE-732 (https://cwe.mitre.org/data/definitions/732.html) - Location: ./examples/os-chmod.py:15 - 14 os.chmod('/etc/hosts', 0o777) - 15 os.chmod('/tmp/oh_hai', 0x1ff) - 16 os.chmod('/etc/passwd', stat.S_IRWXU) + Location: ./examples/os-chmod.py:8 + 7 os.chmod('/etc/passwd', 0o7) + 8 os.chmod('/etc/passwd', 0o664) + 9 os.chmod('/etc/passwd', 0o777) - >> Issue: Chmod setting a permissive mask 0777 on file (key_file). + >> Issue: Chmod setting a permissive mask 0777 on file (keyfile). Severity: High Confidence: High CWE: CWE-732 (https://cwe.mitre.org/data/definitions/732.html) Location: ./examples/os-chmod.py:17 16 os.chmod('/etc/passwd', stat.S_IRWXU) - 17 os.chmod(key_file, 0o777) - 18 + 17 os.chmod(keyfile, 0o777) + 18 os.chmod('~/hidden_exec', stat.S_IXGRP) + + >> Issue: Chmod setting a permissive mask 0o666 on file (NOT PARSED). + Severity: High Confidence: High + CWE: CWE-732 (https://cwe.mitre.org/data/definitions/732.html) + Location: ./examples/pathlib-chmod.py:5 + 4 p1 = pathlib.Path(filename) + 5 p1.chmod(0o666) .. seealso:: @@ -52,6 +59,9 @@ .. versionchanged:: 1.7.5 Added checks for S_IWGRP and S_IXOTH +.. versionchanged:: 1.7.6 + Added check for pathlib chmod + """ # noqa: E501 import stat @@ -73,27 +83,38 @@ def _stat_is_dangerous(mode): @test.test_id("B103") def set_bad_file_permissions(context): if "chmod" in context.call_function_name: - if context.call_args_count == 2: + if ( + context.call_function_name_qual.startswith("os.") + and context.call_args_count == 2 + ): # os chmod + filename = context.get_call_arg_at_position(0) mode = context.get_call_arg_at_position(1) - - if ( - mode is not None - and isinstance(mode, int) - and _stat_is_dangerous(mode) - ): - # world writable is an HIGH, group executable is a MEDIUM - if mode & stat.S_IWOTH: - sev_level = bandit.HIGH - else: - sev_level = bandit.MEDIUM - - filename = context.get_call_arg_at_position(0) - if filename is None: - filename = "NOT PARSED" - return bandit.Issue( - severity=sev_level, - confidence=bandit.HIGH, - cwe=issue.Cwe.INCORRECT_PERMISSION_ASSIGNMENT, - text="Chmod setting a permissive mask %s on file (%s)." - % (oct(mode), filename), - ) + elif ( + context.is_module_imported_like("pathlib") + and context.call_args_count == 1 # pathlib chmod + ): + filename = None + mode = context.get_call_arg_at_position(0) + else: + return + + if ( + mode is not None + and isinstance(mode, int) + and _stat_is_dangerous(mode) + ): + # world writable is an HIGH, group executable is a MEDIUM + if mode & stat.S_IWOTH: + sev_level = bandit.HIGH + else: + sev_level = bandit.MEDIUM + + if filename is None: + filename = "NOT PARSED" + return bandit.Issue( + severity=sev_level, + confidence=bandit.HIGH, + cwe=issue.Cwe.INCORRECT_PERMISSION_ASSIGNMENT, + text="Chmod setting a permissive mask %s on file (%s)." + % (oct(mode), filename), + ) diff --git a/examples/os-chmod.py b/examples/os-chmod.py index f7fff8517..24e7e9050 100644 --- a/examples/os-chmod.py +++ b/examples/os-chmod.py @@ -17,3 +17,5 @@ os.chmod(keyfile, 0o777) os.chmod('~/hidden_exec', stat.S_IXGRP) os.chmod('~/hidden_exec', stat.S_IXOTH) +os.fchmod(keyfile, 0o777) +os.lchmod('symlink', 0o777) diff --git a/examples/pathlib-chmod.py b/examples/pathlib-chmod.py new file mode 100644 index 000000000..f7c014e2c --- /dev/null +++ b/examples/pathlib-chmod.py @@ -0,0 +1,9 @@ +import pathlib + +filename = 'foobar' +p1 = pathlib.Path(filename) +p1.chmod(0o666) + +symlink = 'link' +p2 = pathlib.Path(symlink) +p2.lchmod(0o777) diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py index 7835e7488..dc5bf6bcb 100644 --- a/tests/functional/test_functional.py +++ b/tests/functional/test_functional.py @@ -297,11 +297,19 @@ def test_subdirectory_okay(self): } self.check_example("init-py-test/subdirectory-okay.py", expect) + def test_pathlib_chmod(self): + """Test setting file permissions.""" + expect = { + "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, "HIGH": 2}, + "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, "HIGH": 2}, + } + self.check_example("pathlib-chmod.py", expect) + def test_os_chmod(self): """Test setting file permissions.""" expect = { - "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 4, "HIGH": 8}, - "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 11}, + "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 4, "HIGH": 10}, + "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 13}, } self.check_example("os-chmod.py", expect)