Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file permission check for pathlib chmod #1043

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion bandit/core/utils.py
Expand Up @@ -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
Expand Down
85 changes: 53 additions & 32 deletions bandit/plugins/general_bad_file_permissions.py
Expand Up @@ -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::

Expand All @@ -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

Expand All @@ -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),
)
2 changes: 2 additions & 0 deletions examples/os-chmod.py
Expand Up @@ -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)
9 changes: 9 additions & 0 deletions 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)
12 changes: 10 additions & 2 deletions tests/functional/test_functional.py
Expand Up @@ -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)

Expand Down