Skip to content

Commit a9aad84

Browse files
committed
Add file permission check for pathlib chmod
This extends the existing implementation for detecting bad file permissions to account for calls to pathlib module functions in addition to those from the os module. The pathlib chmod and lchmod functions are really just wrappers around the os module equivalents. However, since they are class methods, the pre-existing logic in the code did not consider the corresponding pathlib function calls. Note that the filename is not easily parsable in the case of pathlib. Closes PyCQA#1042
1 parent c420d1d commit a9aad84

File tree

5 files changed

+68
-31
lines changed

5 files changed

+68
-31
lines changed

bandit/core/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020

2121
def _get_attr_qual_name(node, aliases):
22-
"""Get a the full name for the attribute node.
22+
"""Get the full name for the attribute node.
2323
2424
This will resolve a pseudo-qualified name for the attribute
2525
rooted at node as long as all the deeper nodes are Names or

bandit/plugins/general_bad_file_permissions.py

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,28 @@
2121
2222
.. code-block:: none
2323
24-
>> Issue: Probable insecure usage of temp file/directory.
25-
Severity: Medium Confidence: Medium
24+
>> Issue: Chmod setting a permissive mask 0o664 on file (/etc/passwd).
25+
Severity: Medium Confidence: High
2626
CWE: CWE-732 (https://cwe.mitre.org/data/definitions/732.html)
27-
Location: ./examples/os-chmod.py:15
28-
14 os.chmod('/etc/hosts', 0o777)
29-
15 os.chmod('/tmp/oh_hai', 0x1ff)
30-
16 os.chmod('/etc/passwd', stat.S_IRWXU)
27+
Location: ./examples/os-chmod.py:8
28+
7 os.chmod('/etc/passwd', 0o7)
29+
8 os.chmod('/etc/passwd', 0o664)
30+
9 os.chmod('/etc/passwd', 0o777)
3131
32-
>> Issue: Chmod setting a permissive mask 0777 on file (key_file).
32+
>> Issue: Chmod setting a permissive mask 0777 on file (keyfile).
3333
Severity: High Confidence: High
3434
CWE: CWE-732 (https://cwe.mitre.org/data/definitions/732.html)
3535
Location: ./examples/os-chmod.py:17
3636
16 os.chmod('/etc/passwd', stat.S_IRWXU)
37-
17 os.chmod(key_file, 0o777)
38-
18
37+
17 os.chmod(keyfile, 0o777)
38+
18 os.chmod('~/hidden_exec', stat.S_IXGRP)
39+
40+
>> Issue: Chmod setting a permissive mask 0o666 on file (NOT PARSED).
41+
Severity: High Confidence: High
42+
CWE: CWE-732 (https://cwe.mitre.org/data/definitions/732.html)
43+
Location: ./examples/pathlib-chmod.py:5
44+
4 p1 = pathlib.Path(filename)
45+
5 p1.chmod(0o666)
3946
4047
.. seealso::
4148
@@ -52,6 +59,9 @@
5259
.. versionchanged:: 1.7.5
5360
Added checks for S_IWGRP and S_IXOTH
5461
62+
.. versionchanged:: 1.7.6
63+
Added check for pathlib chmod
64+
5565
""" # noqa: E501
5666
import stat
5767

@@ -73,27 +83,35 @@ def _stat_is_dangerous(mode):
7383
@test.test_id("B103")
7484
def set_bad_file_permissions(context):
7585
if "chmod" in context.call_function_name:
76-
if context.call_args_count == 2:
86+
if (
87+
context.call_function_name_qual.startswith("os.")
88+
and context.call_args_count == 2
89+
): # os chmod
90+
filename = context.get_call_arg_at_position(0)
7791
mode = context.get_call_arg_at_position(1)
92+
elif context.call_args_count == 1: # pathlib chmod
93+
filename = None
94+
mode = context.get_call_arg_at_position(0)
95+
else:
96+
return
7897

79-
if (
98+
if (
8099
mode is not None
81100
and isinstance(mode, int)
82101
and _stat_is_dangerous(mode)
83-
):
84-
# world writable is an HIGH, group executable is a MEDIUM
85-
if mode & stat.S_IWOTH:
86-
sev_level = bandit.HIGH
87-
else:
88-
sev_level = bandit.MEDIUM
89-
90-
filename = context.get_call_arg_at_position(0)
91-
if filename is None:
92-
filename = "NOT PARSED"
93-
return bandit.Issue(
94-
severity=sev_level,
95-
confidence=bandit.HIGH,
96-
cwe=issue.Cwe.INCORRECT_PERMISSION_ASSIGNMENT,
97-
text="Chmod setting a permissive mask %s on file (%s)."
98-
% (oct(mode), filename),
99-
)
102+
):
103+
# world writable is an HIGH, group executable is a MEDIUM
104+
if mode & stat.S_IWOTH:
105+
sev_level = bandit.HIGH
106+
else:
107+
sev_level = bandit.MEDIUM
108+
109+
if filename is None:
110+
filename = "NOT PARSED"
111+
return bandit.Issue(
112+
severity=sev_level,
113+
confidence=bandit.HIGH,
114+
cwe=issue.Cwe.INCORRECT_PERMISSION_ASSIGNMENT,
115+
text="Chmod setting a permissive mask %s on file (%s)."
116+
% (oct(mode), filename),
117+
)

examples/os-chmod.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@
1717
os.chmod(keyfile, 0o777)
1818
os.chmod('~/hidden_exec', stat.S_IXGRP)
1919
os.chmod('~/hidden_exec', stat.S_IXOTH)
20+
os.fchmod(keyfile, 0o777)
21+
os.lchmod('symlink', 0o777)

examples/pathlib-chmod.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import pathlib
2+
3+
filename = 'foobar'
4+
p1 = pathlib.Path(filename)
5+
p1.chmod(0o666)
6+
7+
symlink = 'link'
8+
p2 = pathlib.Path(symlink)
9+
p2.lchmod(0o777)

tests/functional/test_functional.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,19 @@ def test_subdirectory_okay(self):
297297
}
298298
self.check_example("init-py-test/subdirectory-okay.py", expect)
299299

300+
def test_pathlib_chmod(self):
301+
"""Test setting file permissions."""
302+
expect = {
303+
"SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, "HIGH": 2},
304+
"CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, "HIGH": 2},
305+
}
306+
self.check_example("pathlib-chmod.py", expect)
307+
300308
def test_os_chmod(self):
301309
"""Test setting file permissions."""
302310
expect = {
303-
"SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 4, "HIGH": 8},
304-
"CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 11},
311+
"SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 4, "HIGH": 10},
312+
"CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 13},
305313
}
306314
self.check_example("os-chmod.py", expect)
307315

0 commit comments

Comments
 (0)