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 check for "requests" calls without timeout #743

Merged
merged 20 commits into from
Mar 28, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions bandit/plugins/request_without_timeout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# -*- coding:utf-8 -*-
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary unless there are unicode characters in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should I remove it?
most plugins seem to include the line, even if there are no unicode characters in the file

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes please. Recently Black was integrated as a format check. It'll flag this.

#
# 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: High
Location: examples/requests-missing-timeout.py:3:0
More Info: https://bandit.readthedocs.io/en/latest/plugins/b113_request_without_timeout.html
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: High
Location: examples/requests-missing-timeout.py:4:0
More Info: https://bandit.readthedocs.io/en/latest/plugins/b113_request_without_timeout.html
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

""" # noqa: E501

import bandit
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:
issue = bandit.Issue(
severity=bandit.MEDIUM,
confidence=bandit.LOW,
text="Requests call without timeout"
)
return issue
# check for timeout=None
if context.check_call_arg_value('timeout', 'None'):
issue = bandit.Issue(
severity=bandit.MEDIUM,
confidence=bandit.LOW,
text="Requests call with timeout set to None"
)
return issue
5 changes: 5 additions & 0 deletions doc/source/plugins/b113_request_without_timeout.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-------------------------------------
B113: request_without_timeout
-------------------------------------

.. automodule:: bandit.plugins.request_without_timeout
2 changes: 1 addition & 1 deletion examples/httpoxy_cgihandler.py
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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)
28 changes: 14 additions & 14 deletions examples/requests-ssl-verify-disabled.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
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)
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,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