Repository: PyCQA/bandit (python)

Issue:
general_bad_file_permissions permits most dangerous flag
The logic in `general_bad_file_permissions` seems to ignore `stat.S_IXOTH` (execute by others), which is the most dangerous of all the POSIX file flags.

It raises a high severity issue for `S_IWOTH` (write by others) and it raises a medium severity issue for `S_IXGRP` (execute by group).

The following test raises no issues:

```python
import os
import stat

os.chmod('/path/to/binary/i/just/uploaded', stat.S_IXOTH)
```

The guide referenced in the file documents why it is bad to have this flag:
https://security.openstack.org/guidelines/dg_apply-restrictive-file-permissions.html#testing-guide

Pull request description:
Add case for global exec
Updates the `general_bad_file_permissions` plugin to give:
- Medium if group writable or executable
- High if global writable or executable

Closes #569

Patch:
diff --git a/bandit/plugins/general_bad_file_permissions.py b/bandit/plugins/general_bad_file_permissions.py
index 68e091b..7d3fce4 100644
--- a/bandit/plugins/general_bad_file_permissions.py
+++ b/bandit/plugins/general_bad_file_permissions.py
@@ -14,8 +14,8 @@ flags sets. Python provides ``chmod`` to manipulate POSIX style permissions.
 
 This plugin test looks for the use of ``chmod`` and will alert when it is used
 to set particularly permissive control flags. A MEDIUM warning is generated if
-a file is set to group executable and a HIGH warning is reported if a file is
-set world writable. Warnings are given with HIGH confidence.
+a file is set to group write or executable and a HIGH warning is reported if a
+file is set world write or executable. Warnings are given with HIGH confidence.
 
 :Example:
 
@@ -49,6 +49,9 @@ set world writable. Warnings are given with HIGH confidence.
 .. versionchanged:: 1.7.3
     CWE information added
 
+.. versionchanged:: 1.7.5
+    Added checks for S_IWGRP and S_IXOTH
+
 """  # noqa: E501
 import stat
 
@@ -57,6 +60,15 @@ from bandit.core import issue
 from bandit.core import test_properties as test
 
 
+def _stat_is_dangerous(mode):
+    return (
+        mode & stat.S_IWOTH
+        or mode & stat.S_IWGRP
+        or mode & stat.S_IXGRP
+        or mode & stat.S_IXOTH
+    )
+
+
 @test.checks("Call")
 @test.test_id("B103")
 def set_bad_file_permissions(context):
@@ -67,7 +79,7 @@ def set_bad_file_permissions(context):
             if (
                 mode is not None
                 and isinstance(mode, int)
-                and (mode & stat.S_IWOTH or mode & stat.S_IXGRP)
+                and _stat_is_dangerous(mode)
             ):
                 # world writable is an HIGH, group executable is a MEDIUM
                 if mode & stat.S_IWOTH:
diff --git a/examples/os-chmod.py b/examples/os-chmod.py
index 65560f6..f7fff85 100644
--- a/examples/os-chmod.py
+++ b/examples/os-chmod.py
@@ -14,4 +14,6 @@ os.chmod('~/.bashrc', 511)
 os.chmod('/etc/hosts', 0o777)
 os.chmod('/tmp/oh_hai', 0x1ff)
 os.chmod('/etc/passwd', stat.S_IRWXU)
-os.chmod(key_file, 0o777)
+os.chmod(keyfile, 0o777)
+os.chmod('~/hidden_exec', stat.S_IXGRP)
+os.chmod('~/hidden_exec', stat.S_IXOTH)


Test patch:
diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py
index 1d1bd7e..16d07c0 100644
--- a/tests/functional/test_functional.py
+++ b/tests/functional/test_functional.py
@@ -300,8 +300,8 @@ class FunctionalTests(testtools.TestCase):
     def test_os_chmod(self):
         """Test setting file permissions."""
         expect = {
-            "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 2, "HIGH": 8},
-            "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 9},
+            "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 4, "HIGH": 8},
+            "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 11},
         }
         self.check_example("os-chmod.py", expect)