Repository: PyCQA/bandit
Pull request #1177 (2024-09-27 22:37:29)

No need to check httpx client without timeout defined
Unlike python-requests, the httpx client has a default timeout of 5 seconds on its class and functions. As such, there is no need for Bandit to check for an undefined timeout. However, explicitly setting the timeout to None is still a potential problem as that would create a situtation where the client would block forever.

Fixes: #1175


Patch:
diff --git a/.github/workflows/build-publish-image.yml b/.github/workflows/build-publish-image.yml
index 2d05f83..c478777 100644
--- a/.github/workflows/build-publish-image.yml
+++ b/.github/workflows/build-publish-image.yml
@@ -31,7 +31,7 @@ jobs:
         ref: ${{ github.event_name == 'release' && github.ref || env.RELEASE_TAG }}
 
     - name: Set up Docker Buildx
-      uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3
+      uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3
 
     - name: Log in to GitHub Container Registry
       uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
@@ -41,7 +41,7 @@ jobs:
         password: ${{ secrets.GITHUB_TOKEN }}
 
     - name: Install Cosign
-      uses: sigstore/cosign-installer@4959ce089c160fddf62f7b42464195ba1a56d382 # v3.6.0
+      uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
       with:
         cosign-release: 'v2.2.2'
 
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8ecd3b9..6b0b25d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,7 +1,7 @@
 exclude: ^(examples|tools|doc)
 repos:
 - repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: v4.6.0
+  rev: v5.0.0
   hooks:
   - id: check-yaml
   - id: debug-statements
@@ -13,12 +13,12 @@ repos:
   - id: reorder-python-imports
     args: [--application-directories, '.:src', --py38-plus]
 - repo: https://github.com/psf/black-pre-commit-mirror
-  rev: 24.4.2
+  rev: 24.10.0
   hooks:
   - id: black
     args: [--line-length=79, --target-version=py38]
 - repo: https://github.com/asottile/pyupgrade
-  rev: v3.16.0
+  rev: v3.17.0
   hooks:
   - id: pyupgrade
     args: [--py38-plus]
diff --git a/bandit/blacklists/calls.py b/bandit/blacklists/calls.py
index 1719194..60656b1 100644
--- a/bandit/blacklists/calls.py
+++ b/bandit/blacklists/calls.py
@@ -96,6 +96,12 @@ as AES.
 |      |                     |   .ciphers.algorithms.Blowfish     |           |
 |      |                     | - cryptography.hazmat.primitives   |           |
 |      |                     |   .ciphers.algorithms.IDEA         |           |
+|      |                     | - cryptography.hazmat.primitives   |           |
+|      |                     |   .ciphers.algorithms.CAST5        |           |
+|      |                     | - cryptography.hazmat.primitives   |           |
+|      |                     |   .ciphers.algorithms.SEED         |           |
+|      |                     | - cryptography.hazmat.primitives   |           |
+|      |                     |   .ciphers.algorithms.TripleDES    |           |
 +------+---------------------+------------------------------------+-----------+
 | B305 | cipher_modes        | - cryptography.hazmat.primitives   | Medium    |
 |      |                     |   .ciphers.modes.ECB               |           |
@@ -410,7 +416,10 @@ def gen_blacklist():
                 "Cryptodome.Cipher.XOR.new",
                 "cryptography.hazmat.primitives.ciphers.algorithms.ARC4",
                 "cryptography.hazmat.primitives.ciphers.algorithms.Blowfish",
+                "cryptography.hazmat.primitives.ciphers.algorithms.CAST5",
                 "cryptography.hazmat.primitives.ciphers.algorithms.IDEA",
+                "cryptography.hazmat.primitives.ciphers.algorithms.SEED",
+                "cryptography.hazmat.primitives.ciphers.algorithms.TripleDES",
             ],
             "Use of insecure cipher {name}. Replace with a known secure"
             " cipher such as AES.",
diff --git a/bandit/plugins/request_without_timeout.py b/bandit/plugins/request_without_timeout.py
index d571a49..c643900 100644
--- a/bandit/plugins/request_without_timeout.py
+++ b/bandit/plugins/request_without_timeout.py
@@ -59,12 +59,7 @@ def request_without_timeout(context):
     HTTPX_ATTRS = {"request", "stream", "Client", "AsyncClient"} | HTTP_VERBS
     qualname = context.call_function_name_qual.split(".")[0]
 
-    if (
-        qualname == "requests"
-        and context.call_function_name in HTTP_VERBS
-        or qualname == "httpx"
-        and context.call_function_name in HTTPX_ATTRS
-    ):
+    if qualname == "requests" and context.call_function_name in HTTP_VERBS:
         # check for missing timeout
         if context.check_call_arg_value("timeout") is None:
             return bandit.Issue(
@@ -73,6 +68,12 @@ def request_without_timeout(context):
                 cwe=issue.Cwe.UNCONTROLLED_RESOURCE_CONSUMPTION,
                 text=f"Call to {qualname} without timeout",
             )
+    if (
+        qualname == "requests"
+        and context.call_function_name in HTTP_VERBS
+        or qualname == "httpx"
+        and context.call_function_name in HTTPX_ATTRS
+    ):
         # check for timeout=None
         if context.check_call_arg_value("timeout", "None"):
             return bandit.Issue(
diff --git a/examples/ciphers.py b/examples/ciphers.py
index 7e0762d..af30801 100644
--- a/examples/ciphers.py
+++ b/examples/ciphers.py
@@ -71,6 +71,18 @@ cipher = Cipher(algorithms.Blowfish(key), mode=None, backend=default_backend())
 encryptor = cipher.encryptor()
 ct = encryptor.update(b"a secret message")
 
+cipher = Cipher(algorithms.CAST5(key), mode=None, backend=default_backend())
+encryptor = cipher.encryptor()
+ct = encryptor.update(b"a secret message")
+
 cipher = Cipher(algorithms.IDEA(key), mode=None, backend=default_backend())
 encryptor = cipher.encryptor()
 ct = encryptor.update(b"a secret message")
+
+cipher = Cipher(algorithms.SEED(key), mode=None, backend=default_backend())
+encryptor = cipher.encryptor()
+ct = encryptor.update(b"a secret message")
+
+cipher = Cipher(algorithms.TripleDES(key), mode=None, backend=default_backend())
+encryptor = cipher.encryptor()
+ct = encryptor.update(b"a secret message")


Test patch:
diff --git a/tests/functional/test_functional.py b/tests/functional/test_functional.py
index ecc1557..a89c152 100644
--- a/tests/functional/test_functional.py
+++ b/tests/functional/test_functional.py
@@ -115,8 +115,8 @@ class FunctionalTests(testtools.TestCase):
     def test_ciphers(self):
         """Test the `Crypto.Cipher` example."""
         expect = {
-            "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 21},
-            "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, "HIGH": 22},
+            "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 1, "HIGH": 24},
+            "CONFIDENCE": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 0, "HIGH": 25},
         }
         self.check_example("ciphers.py", expect)
 
@@ -368,8 +368,8 @@ class FunctionalTests(testtools.TestCase):
     def test_requests_without_timeout(self):
         """Test for the `requests` library missing timeouts."""
         expect = {
-            "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 36, "HIGH": 0},
-            "CONFIDENCE": {"UNDEFINED": 0, "LOW": 36, "MEDIUM": 0, "HIGH": 0},
+            "SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 25, "HIGH": 0},
+            "CONFIDENCE": {"UNDEFINED": 0, "LOW": 25, "MEDIUM": 0, "HIGH": 0},
         }
         self.check_example("requests-missing-timeout.py", expect)