Skip to content

Commit

Permalink
add check for "requests" calls without timeout (#743)
Browse files Browse the repository at this point in the history
* add check for "requests" calls without timeout

* change request_without_timeout confidence to low

* Update bandit/plugins/request_without_timeout.py

Co-authored-by: Eric Brown <ericwb@users.noreply.github.com>

* Update bandit/plugins/request_without_timeout.py

Co-authored-by: Eric Brown <ericwb@users.noreply.github.com>

* Update doc/source/plugins/b113_request_without_timeout.rst

Co-authored-by: Eric Brown <ericwb@users.noreply.github.com>

* Update doc/source/plugins/b113_request_without_timeout.rst

Co-authored-by: Eric Brown <ericwb@users.noreply.github.com>

* Update bandit/plugins/request_without_timeout.py

Co-authored-by: Eric Brown <ericwb@users.noreply.github.com>

* remove utf-8

* fix confidence in comment

* Apply suggestions from code review

* Update issue.py

* Apply suggestions from code review

* Apply suggestions from code review

* Apply suggestions from code review

* Apply suggestions from code review

Co-authored-by: Eric Brown <ericwb@users.noreply.github.com>
  • Loading branch information
mschfh and ericwb committed Mar 28, 2022
1 parent def9928 commit 5ff73ff
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 16 deletions.
1 change: 1 addition & 0 deletions bandit/core/issue.py
Expand Up @@ -25,6 +25,7 @@ class Cwe:
BROKEN_CRYPTO = 327
INSUFFICIENT_RANDOM_VALUES = 330
INSECURE_TEMP_FILE = 377
UNCONTROLLED_RESOURCE_CONSUMPTION = 400
DESERIALIZATION_OF_UNTRUSTED_DATA = 502
MULTIPLE_BINDS = 605
IMPROPER_CHECK_OF_EXCEPT_COND = 703
Expand Down
74 changes: 74 additions & 0 deletions bandit/plugins/request_without_timeout.py
@@ -0,0 +1,74 @@
# SPDX-License-Identifier: Apache-2.0
r"""
=======================================
B113: Test for missing requests timeout
=======================================
This plugin test checks for ``requests`` calls without a timeout specified.
Nearly all production code should use this parameter in nearly all requests,
Failure to do so can cause your program to hang indefinitely.
When request methods are used without the timeout parameter set,
Bandit will return a MEDIUM severity error.
:Example:
.. code-block:: none
>> Issue: [B113:request_without_timeout] Requests call without timeout
Severity: Medium Confidence: Low
CWE: CWE-400 (https://cwe.mitre.org/data/definitions/400.html)
More Info: https://bandit.readthedocs.io/en/latest/plugins/b113_request_without_timeout.html
Location: examples/requests-missing-timeout.py:3:0
2
3 requests.get('https://gmail.com')
4 requests.get('https://gmail.com', timeout=None)
--------------------------------------------------
>> Issue: [B113:request_without_timeout] Requests call with timeout set to None
Severity: Medium Confidence: Low
CWE: CWE-400 (https://cwe.mitre.org/data/definitions/400.html)
More Info: https://bandit.readthedocs.io/en/latest/plugins/b113_request_without_timeout.html
Location: examples/requests-missing-timeout.py:4:0
3 requests.get('https://gmail.com')
4 requests.get('https://gmail.com', timeout=None)
5 requests.get('https://gmail.com', timeout=5)
.. seealso::
- https://2.python-requests.org/en/master/user/quickstart/#timeouts
.. versionadded:: 1.7.5
""" # noqa: E501
import bandit
from bandit.core import issue
from bandit.core import test_properties as test


@test.checks("Call")
@test.test_id("B113")
def request_without_timeout(context):
http_verbs = ("get", "options", "head", "post", "put", "patch", "delete")
if (
"requests" in context.call_function_name_qual
and context.call_function_name in http_verbs
):
# check for missing timeout
if context.check_call_arg_value("timeout") is None:
return bandit.Issue(
severity=bandit.MEDIUM,
confidence=bandit.LOW,
cwe=issue.Cwe.UNCONTROLLED_RESOURCE_CONSUMPTION,
text="Requests call without timeout",
)
# check for timeout=None
if context.check_call_arg_value("timeout", "None"):
return bandit.Issue(
severity=bandit.MEDIUM,
confidence=bandit.LOW,
cwe=issue.Cwe.UNCONTROLLED_RESOURCE_CONSUMPTION,
text="Requests call with timeout set to None",
)
5 changes: 5 additions & 0 deletions doc/source/plugins/b113_request_without_timeout.rst
@@ -0,0 +1,5 @@
-----------------------------
B113: request_without_timeout
-----------------------------

.. automodule:: bandit.plugins.request_without_timeout
2 changes: 1 addition & 1 deletion examples/httpoxy_cgihandler.py
Expand Up @@ -2,7 +2,7 @@
import wsgiref.handlers

def application(environ, start_response):
r = requests.get('https://192.168.0.42/private/api/foobar')
r = requests.get('https://192.168.0.42/private/api/foobar', timeout=30)
start_response('200 OK', [('Content-Type', 'text/plain')])
return [r.content]

Expand Down
23 changes: 23 additions & 0 deletions examples/requests-missing-timeout.py
@@ -0,0 +1,23 @@
import requests

requests.get('https://gmail.com')
requests.get('https://gmail.com', timeout=None)
requests.get('https://gmail.com', timeout=5)
requests.post('https://gmail.com')
requests.post('https://gmail.com', timeout=None)
requests.post('https://gmail.com', timeout=5)
requests.put('https://gmail.com')
requests.put('https://gmail.com', timeout=None)
requests.put('https://gmail.com', timeout=5)
requests.delete('https://gmail.com')
requests.delete('https://gmail.com', timeout=None)
requests.delete('https://gmail.com', timeout=5)
requests.patch('https://gmail.com')
requests.patch('https://gmail.com', timeout=None)
requests.patch('https://gmail.com', timeout=5)
requests.options('https://gmail.com')
requests.options('https://gmail.com', timeout=None)
requests.options('https://gmail.com', timeout=5)
requests.head('https://gmail.com')
requests.head('https://gmail.com', timeout=None)
requests.head('https://gmail.com', timeout=5)
29 changes: 14 additions & 15 deletions examples/requests-ssl-verify-disabled.py
@@ -1,21 +1,20 @@
import httpx
import requests


requests.get('https://gmail.com', verify=True)
requests.get('https://gmail.com', verify=False)
requests.post('https://gmail.com', verify=True)
requests.post('https://gmail.com', verify=False)
requests.put('https://gmail.com', verify=True)
requests.put('https://gmail.com', verify=False)
requests.delete('https://gmail.com', verify=True)
requests.delete('https://gmail.com', verify=False)
requests.patch('https://gmail.com', verify=True)
requests.patch('https://gmail.com', verify=False)
requests.options('https://gmail.com', verify=True)
requests.options('https://gmail.com', verify=False)
requests.head('https://gmail.com', verify=True)
requests.head('https://gmail.com', verify=False)
requests.get('https://gmail.com', timeout=30, verify=True)
requests.get('https://gmail.com', timeout=30, verify=False)
requests.post('https://gmail.com', timeout=30, verify=True)
requests.post('https://gmail.com', timeout=30, verify=False)
requests.put('https://gmail.com', timeout=30, verify=True)
requests.put('https://gmail.com', timeout=30, verify=False)
requests.delete('https://gmail.com', timeout=30, verify=True)
requests.delete('https://gmail.com', timeout=30, verify=False)
requests.patch('https://gmail.com', timeout=30, verify=True)
requests.patch('https://gmail.com', timeout=30, verify=False)
requests.options('https://gmail.com', timeout=30, verify=True)
requests.options('https://gmail.com', timeout=30, verify=False)
requests.head('https://gmail.com', timeout=30, verify=True)
requests.head('https://gmail.com', timeout=30, verify=False)

httpx.request('GET', 'https://gmail.com', verify=True)
httpx.request('GET', 'https://gmail.com', verify=False)
Expand Down
3 changes: 3 additions & 0 deletions setup.cfg
Expand Up @@ -60,6 +60,9 @@ bandit.plugins =
# bandit/plugins/crypto_request_no_cert_validation.py
request_with_no_cert_validation = bandit.plugins.crypto_request_no_cert_validation:request_with_no_cert_validation

# bandit/plugins/request_without_timeout.py
request_without_timeout = bandit.plugins.request_without_timeout:request_without_timeout

# bandit/plugins/exec.py
exec_used = bandit.plugins.exec:exec_used

Expand Down
8 changes: 8 additions & 0 deletions tests/functional/test_functional.py
Expand Up @@ -393,6 +393,14 @@ def test_requests_ssl_verify_disabled(self):
}
self.check_example("requests-ssl-verify-disabled.py", expect)

def test_requests_without_timeout(self):
"""Test for the `requests` library missing timeouts."""
expect = {
"SEVERITY": {"UNDEFINED": 0, "LOW": 0, "MEDIUM": 14, "HIGH": 0},
"CONFIDENCE": {"UNDEFINED": 0, "LOW": 14, "MEDIUM": 0, "HIGH": 0},
}
self.check_example("requests-missing-timeout.py", expect)

def test_skip(self):
"""Test `#nosec` and `#noqa` comments."""
expect = {
Expand Down

0 comments on commit 5ff73ff

Please sign in to comment.