[flake8-bandit] fix S113 false positive for httpx without timeout argument (#12213)

## Summary

S113 exists because `requests` doesn't have a default timeout, so
request without timeout may hang indefinitely

> B113: Test for missing requests timeout
This plugin test checks for requests or httpx 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.**


But httpx has default timeout 5s, so S113 for httpx request without
`timeout` argument is a false positive, only valid case would be
`timeout=None`.

https://www.python-httpx.org/advanced/timeouts/

> HTTPX is careful to enforce timeouts everywhere by default.
>
> The default behavior is to raise a TimeoutException after 5 seconds of
network inactivity.


## Test Plan

snap updated
This commit is contained in:
Trim21 2024-07-07 03:08:40 +08:00 committed by GitHub
parent 9d61727289
commit 757c75752e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 159 additions and 278 deletions

View file

@ -25,6 +25,23 @@ async def foo():
async with httpx.AsyncClient(timeout=5) as client:
await client.get('https://gmail.com')
httpx.get('https://gmail.com')
httpx.post('https://gmail.com')
httpx.put('https://gmail.com')
httpx.delete('https://gmail.com')
httpx.patch('https://gmail.com')
httpx.options('https://gmail.com')
httpx.head('https://gmail.com')
httpx.Client()
httpx.AsyncClient()
async def bar():
async with httpx.AsyncClient() as client:
await client.get('https://gmail.com')
with httpx.Client() as client:
client.get('https://gmail.com')
# Errors
requests.get('https://gmail.com')
requests.get('https://gmail.com', timeout=None)
@ -41,31 +58,15 @@ requests.options('https://gmail.com', timeout=None)
requests.head('https://gmail.com')
requests.head('https://gmail.com', timeout=None)
httpx.get('https://gmail.com')
httpx.get('https://gmail.com', timeout=None)
httpx.post('https://gmail.com')
httpx.post('https://gmail.com', timeout=None)
httpx.put('https://gmail.com')
httpx.put('https://gmail.com', timeout=None)
httpx.delete('https://gmail.com')
httpx.delete('https://gmail.com', timeout=None)
httpx.patch('https://gmail.com')
httpx.patch('https://gmail.com', timeout=None)
httpx.options('https://gmail.com')
httpx.options('https://gmail.com', timeout=None)
httpx.head('https://gmail.com')
httpx.head('https://gmail.com', timeout=None)
httpx.Client()
httpx.Client(timeout=None)
httpx.AsyncClient()
httpx.AsyncClient(timeout=None)
with httpx.Client() as client:
client.get('https://gmail.com')
with httpx.Client(timeout=None) as client:
client.get('https://gmail.com')
async def bar():
async with httpx.AsyncClient() as client:
await client.get('https://gmail.com')
async def baz():
async with httpx.AsyncClient(timeout=None) as client:
await client.get('https://gmail.com')

View file

@ -72,7 +72,7 @@ pub(crate) fn request_without_timeout(checker: &mut Checker, call: &ast::ExprCal
keyword.range(),
));
}
} else {
} else if module == "requests" {
checker.diagnostics.push(Diagnostic::new(
RequestWithoutTimeout { implicit: true, module: module.to_string() },
call.func.range(),

View file

@ -1,358 +1,238 @@
---
source: crates/ruff_linter/src/rules/flake8_bandit/mod.rs
assertion_line: 81
---
S113.py:29:1: S113 Probable use of `requests` call without timeout
S113.py:46:1: S113 Probable use of `requests` call without timeout
|
28 | # Errors
29 | requests.get('https://gmail.com')
45 | # Errors
46 | requests.get('https://gmail.com')
| ^^^^^^^^^^^^ S113
30 | requests.get('https://gmail.com', timeout=None)
31 | requests.post('https://gmail.com')
47 | requests.get('https://gmail.com', timeout=None)
48 | requests.post('https://gmail.com')
|
S113.py:30:35: S113 Probable use of `requests` call with timeout set to `None`
S113.py:47:35: S113 Probable use of `requests` call with timeout set to `None`
|
28 | # Errors
29 | requests.get('https://gmail.com')
30 | requests.get('https://gmail.com', timeout=None)
45 | # Errors
46 | requests.get('https://gmail.com')
47 | requests.get('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
31 | requests.post('https://gmail.com')
32 | requests.post('https://gmail.com', timeout=None)
48 | requests.post('https://gmail.com')
49 | requests.post('https://gmail.com', timeout=None)
|
S113.py:31:1: S113 Probable use of `requests` call without timeout
S113.py:48:1: S113 Probable use of `requests` call without timeout
|
29 | requests.get('https://gmail.com')
30 | requests.get('https://gmail.com', timeout=None)
31 | requests.post('https://gmail.com')
46 | requests.get('https://gmail.com')
47 | requests.get('https://gmail.com', timeout=None)
48 | requests.post('https://gmail.com')
| ^^^^^^^^^^^^^ S113
32 | requests.post('https://gmail.com', timeout=None)
33 | requests.put('https://gmail.com')
49 | requests.post('https://gmail.com', timeout=None)
50 | requests.put('https://gmail.com')
|
S113.py:32:36: S113 Probable use of `requests` call with timeout set to `None`
S113.py:49:36: S113 Probable use of `requests` call with timeout set to `None`
|
30 | requests.get('https://gmail.com', timeout=None)
31 | requests.post('https://gmail.com')
32 | requests.post('https://gmail.com', timeout=None)
47 | requests.get('https://gmail.com', timeout=None)
48 | requests.post('https://gmail.com')
49 | requests.post('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
33 | requests.put('https://gmail.com')
34 | requests.put('https://gmail.com', timeout=None)
50 | requests.put('https://gmail.com')
51 | requests.put('https://gmail.com', timeout=None)
|
S113.py:33:1: S113 Probable use of `requests` call without timeout
S113.py:50:1: S113 Probable use of `requests` call without timeout
|
31 | requests.post('https://gmail.com')
32 | requests.post('https://gmail.com', timeout=None)
33 | requests.put('https://gmail.com')
48 | requests.post('https://gmail.com')
49 | requests.post('https://gmail.com', timeout=None)
50 | requests.put('https://gmail.com')
| ^^^^^^^^^^^^ S113
34 | requests.put('https://gmail.com', timeout=None)
35 | requests.delete('https://gmail.com')
51 | requests.put('https://gmail.com', timeout=None)
52 | requests.delete('https://gmail.com')
|
S113.py:34:35: S113 Probable use of `requests` call with timeout set to `None`
S113.py:51:35: S113 Probable use of `requests` call with timeout set to `None`
|
32 | requests.post('https://gmail.com', timeout=None)
33 | requests.put('https://gmail.com')
34 | requests.put('https://gmail.com', timeout=None)
49 | requests.post('https://gmail.com', timeout=None)
50 | requests.put('https://gmail.com')
51 | requests.put('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
35 | requests.delete('https://gmail.com')
36 | requests.delete('https://gmail.com', timeout=None)
52 | requests.delete('https://gmail.com')
53 | requests.delete('https://gmail.com', timeout=None)
|
S113.py:35:1: S113 Probable use of `requests` call without timeout
S113.py:52:1: S113 Probable use of `requests` call without timeout
|
33 | requests.put('https://gmail.com')
34 | requests.put('https://gmail.com', timeout=None)
35 | requests.delete('https://gmail.com')
50 | requests.put('https://gmail.com')
51 | requests.put('https://gmail.com', timeout=None)
52 | requests.delete('https://gmail.com')
| ^^^^^^^^^^^^^^^ S113
36 | requests.delete('https://gmail.com', timeout=None)
37 | requests.patch('https://gmail.com')
53 | requests.delete('https://gmail.com', timeout=None)
54 | requests.patch('https://gmail.com')
|
S113.py:36:38: S113 Probable use of `requests` call with timeout set to `None`
S113.py:53:38: S113 Probable use of `requests` call with timeout set to `None`
|
34 | requests.put('https://gmail.com', timeout=None)
35 | requests.delete('https://gmail.com')
36 | requests.delete('https://gmail.com', timeout=None)
51 | requests.put('https://gmail.com', timeout=None)
52 | requests.delete('https://gmail.com')
53 | requests.delete('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
37 | requests.patch('https://gmail.com')
38 | requests.patch('https://gmail.com', timeout=None)
54 | requests.patch('https://gmail.com')
55 | requests.patch('https://gmail.com', timeout=None)
|
S113.py:37:1: S113 Probable use of `requests` call without timeout
S113.py:54:1: S113 Probable use of `requests` call without timeout
|
35 | requests.delete('https://gmail.com')
36 | requests.delete('https://gmail.com', timeout=None)
37 | requests.patch('https://gmail.com')
52 | requests.delete('https://gmail.com')
53 | requests.delete('https://gmail.com', timeout=None)
54 | requests.patch('https://gmail.com')
| ^^^^^^^^^^^^^^ S113
38 | requests.patch('https://gmail.com', timeout=None)
39 | requests.options('https://gmail.com')
55 | requests.patch('https://gmail.com', timeout=None)
56 | requests.options('https://gmail.com')
|
S113.py:38:37: S113 Probable use of `requests` call with timeout set to `None`
S113.py:55:37: S113 Probable use of `requests` call with timeout set to `None`
|
36 | requests.delete('https://gmail.com', timeout=None)
37 | requests.patch('https://gmail.com')
38 | requests.patch('https://gmail.com', timeout=None)
53 | requests.delete('https://gmail.com', timeout=None)
54 | requests.patch('https://gmail.com')
55 | requests.patch('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
39 | requests.options('https://gmail.com')
40 | requests.options('https://gmail.com', timeout=None)
56 | requests.options('https://gmail.com')
57 | requests.options('https://gmail.com', timeout=None)
|
S113.py:39:1: S113 Probable use of `requests` call without timeout
S113.py:56:1: S113 Probable use of `requests` call without timeout
|
37 | requests.patch('https://gmail.com')
38 | requests.patch('https://gmail.com', timeout=None)
39 | requests.options('https://gmail.com')
54 | requests.patch('https://gmail.com')
55 | requests.patch('https://gmail.com', timeout=None)
56 | requests.options('https://gmail.com')
| ^^^^^^^^^^^^^^^^ S113
40 | requests.options('https://gmail.com', timeout=None)
41 | requests.head('https://gmail.com')
57 | requests.options('https://gmail.com', timeout=None)
58 | requests.head('https://gmail.com')
|
S113.py:40:39: S113 Probable use of `requests` call with timeout set to `None`
S113.py:57:39: S113 Probable use of `requests` call with timeout set to `None`
|
38 | requests.patch('https://gmail.com', timeout=None)
39 | requests.options('https://gmail.com')
40 | requests.options('https://gmail.com', timeout=None)
55 | requests.patch('https://gmail.com', timeout=None)
56 | requests.options('https://gmail.com')
57 | requests.options('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
41 | requests.head('https://gmail.com')
42 | requests.head('https://gmail.com', timeout=None)
58 | requests.head('https://gmail.com')
59 | requests.head('https://gmail.com', timeout=None)
|
S113.py:41:1: S113 Probable use of `requests` call without timeout
S113.py:58:1: S113 Probable use of `requests` call without timeout
|
39 | requests.options('https://gmail.com')
40 | requests.options('https://gmail.com', timeout=None)
41 | requests.head('https://gmail.com')
56 | requests.options('https://gmail.com')
57 | requests.options('https://gmail.com', timeout=None)
58 | requests.head('https://gmail.com')
| ^^^^^^^^^^^^^ S113
42 | requests.head('https://gmail.com', timeout=None)
59 | requests.head('https://gmail.com', timeout=None)
|
S113.py:42:36: S113 Probable use of `requests` call with timeout set to `None`
S113.py:59:36: S113 Probable use of `requests` call with timeout set to `None`
|
40 | requests.options('https://gmail.com', timeout=None)
41 | requests.head('https://gmail.com')
42 | requests.head('https://gmail.com', timeout=None)
57 | requests.options('https://gmail.com', timeout=None)
58 | requests.head('https://gmail.com')
59 | requests.head('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
43 |
44 | httpx.get('https://gmail.com')
60 |
61 | httpx.get('https://gmail.com', timeout=None)
|
S113.py:44:1: S113 Probable use of `httpx` call without timeout
S113.py:61:32: S113 Probable use of `httpx` call with timeout set to `None`
|
42 | requests.head('https://gmail.com', timeout=None)
43 |
44 | httpx.get('https://gmail.com')
| ^^^^^^^^^ S113
45 | httpx.get('https://gmail.com', timeout=None)
46 | httpx.post('https://gmail.com')
|
S113.py:45:32: S113 Probable use of `httpx` call with timeout set to `None`
|
44 | httpx.get('https://gmail.com')
45 | httpx.get('https://gmail.com', timeout=None)
59 | requests.head('https://gmail.com', timeout=None)
60 |
61 | httpx.get('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
46 | httpx.post('https://gmail.com')
47 | httpx.post('https://gmail.com', timeout=None)
62 | httpx.post('https://gmail.com', timeout=None)
63 | httpx.put('https://gmail.com', timeout=None)
|
S113.py:46:1: S113 Probable use of `httpx` call without timeout
S113.py:62:33: S113 Probable use of `httpx` call with timeout set to `None`
|
44 | httpx.get('https://gmail.com')
45 | httpx.get('https://gmail.com', timeout=None)
46 | httpx.post('https://gmail.com')
| ^^^^^^^^^^ S113
47 | httpx.post('https://gmail.com', timeout=None)
48 | httpx.put('https://gmail.com')
|
S113.py:47:33: S113 Probable use of `httpx` call with timeout set to `None`
|
45 | httpx.get('https://gmail.com', timeout=None)
46 | httpx.post('https://gmail.com')
47 | httpx.post('https://gmail.com', timeout=None)
61 | httpx.get('https://gmail.com', timeout=None)
62 | httpx.post('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
48 | httpx.put('https://gmail.com')
49 | httpx.put('https://gmail.com', timeout=None)
63 | httpx.put('https://gmail.com', timeout=None)
64 | httpx.delete('https://gmail.com', timeout=None)
|
S113.py:48:1: S113 Probable use of `httpx` call without timeout
S113.py:63:32: S113 Probable use of `httpx` call with timeout set to `None`
|
46 | httpx.post('https://gmail.com')
47 | httpx.post('https://gmail.com', timeout=None)
48 | httpx.put('https://gmail.com')
| ^^^^^^^^^ S113
49 | httpx.put('https://gmail.com', timeout=None)
50 | httpx.delete('https://gmail.com')
|
S113.py:49:32: S113 Probable use of `httpx` call with timeout set to `None`
|
47 | httpx.post('https://gmail.com', timeout=None)
48 | httpx.put('https://gmail.com')
49 | httpx.put('https://gmail.com', timeout=None)
61 | httpx.get('https://gmail.com', timeout=None)
62 | httpx.post('https://gmail.com', timeout=None)
63 | httpx.put('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
50 | httpx.delete('https://gmail.com')
51 | httpx.delete('https://gmail.com', timeout=None)
64 | httpx.delete('https://gmail.com', timeout=None)
65 | httpx.patch('https://gmail.com', timeout=None)
|
S113.py:50:1: S113 Probable use of `httpx` call without timeout
S113.py:64:35: S113 Probable use of `httpx` call with timeout set to `None`
|
48 | httpx.put('https://gmail.com')
49 | httpx.put('https://gmail.com', timeout=None)
50 | httpx.delete('https://gmail.com')
| ^^^^^^^^^^^^ S113
51 | httpx.delete('https://gmail.com', timeout=None)
52 | httpx.patch('https://gmail.com')
|
S113.py:51:35: S113 Probable use of `httpx` call with timeout set to `None`
|
49 | httpx.put('https://gmail.com', timeout=None)
50 | httpx.delete('https://gmail.com')
51 | httpx.delete('https://gmail.com', timeout=None)
62 | httpx.post('https://gmail.com', timeout=None)
63 | httpx.put('https://gmail.com', timeout=None)
64 | httpx.delete('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
52 | httpx.patch('https://gmail.com')
53 | httpx.patch('https://gmail.com', timeout=None)
65 | httpx.patch('https://gmail.com', timeout=None)
66 | httpx.options('https://gmail.com', timeout=None)
|
S113.py:52:1: S113 Probable use of `httpx` call without timeout
S113.py:65:34: S113 Probable use of `httpx` call with timeout set to `None`
|
50 | httpx.delete('https://gmail.com')
51 | httpx.delete('https://gmail.com', timeout=None)
52 | httpx.patch('https://gmail.com')
| ^^^^^^^^^^^ S113
53 | httpx.patch('https://gmail.com', timeout=None)
54 | httpx.options('https://gmail.com')
|
S113.py:53:34: S113 Probable use of `httpx` call with timeout set to `None`
|
51 | httpx.delete('https://gmail.com', timeout=None)
52 | httpx.patch('https://gmail.com')
53 | httpx.patch('https://gmail.com', timeout=None)
63 | httpx.put('https://gmail.com', timeout=None)
64 | httpx.delete('https://gmail.com', timeout=None)
65 | httpx.patch('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
54 | httpx.options('https://gmail.com')
55 | httpx.options('https://gmail.com', timeout=None)
66 | httpx.options('https://gmail.com', timeout=None)
67 | httpx.head('https://gmail.com', timeout=None)
|
S113.py:54:1: S113 Probable use of `httpx` call without timeout
S113.py:66:36: S113 Probable use of `httpx` call with timeout set to `None`
|
52 | httpx.patch('https://gmail.com')
53 | httpx.patch('https://gmail.com', timeout=None)
54 | httpx.options('https://gmail.com')
| ^^^^^^^^^^^^^ S113
55 | httpx.options('https://gmail.com', timeout=None)
56 | httpx.head('https://gmail.com')
|
S113.py:55:36: S113 Probable use of `httpx` call with timeout set to `None`
|
53 | httpx.patch('https://gmail.com', timeout=None)
54 | httpx.options('https://gmail.com')
55 | httpx.options('https://gmail.com', timeout=None)
64 | httpx.delete('https://gmail.com', timeout=None)
65 | httpx.patch('https://gmail.com', timeout=None)
66 | httpx.options('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
56 | httpx.head('https://gmail.com')
57 | httpx.head('https://gmail.com', timeout=None)
67 | httpx.head('https://gmail.com', timeout=None)
68 | httpx.Client(timeout=None)
|
S113.py:56:1: S113 Probable use of `httpx` call without timeout
S113.py:67:33: S113 Probable use of `httpx` call with timeout set to `None`
|
54 | httpx.options('https://gmail.com')
55 | httpx.options('https://gmail.com', timeout=None)
56 | httpx.head('https://gmail.com')
| ^^^^^^^^^^ S113
57 | httpx.head('https://gmail.com', timeout=None)
58 | httpx.Client()
|
S113.py:57:33: S113 Probable use of `httpx` call with timeout set to `None`
|
55 | httpx.options('https://gmail.com', timeout=None)
56 | httpx.head('https://gmail.com')
57 | httpx.head('https://gmail.com', timeout=None)
65 | httpx.patch('https://gmail.com', timeout=None)
66 | httpx.options('https://gmail.com', timeout=None)
67 | httpx.head('https://gmail.com', timeout=None)
| ^^^^^^^^^^^^ S113
58 | httpx.Client()
59 | httpx.Client(timeout=None)
68 | httpx.Client(timeout=None)
69 | httpx.AsyncClient(timeout=None)
|
S113.py:58:1: S113 Probable use of `httpx` call without timeout
S113.py:68:14: S113 Probable use of `httpx` call with timeout set to `None`
|
56 | httpx.head('https://gmail.com')
57 | httpx.head('https://gmail.com', timeout=None)
58 | httpx.Client()
| ^^^^^^^^^^^^ S113
59 | httpx.Client(timeout=None)
60 | httpx.AsyncClient()
|
S113.py:59:14: S113 Probable use of `httpx` call with timeout set to `None`
|
57 | httpx.head('https://gmail.com', timeout=None)
58 | httpx.Client()
59 | httpx.Client(timeout=None)
66 | httpx.options('https://gmail.com', timeout=None)
67 | httpx.head('https://gmail.com', timeout=None)
68 | httpx.Client(timeout=None)
| ^^^^^^^^^^^^ S113
60 | httpx.AsyncClient()
61 | httpx.AsyncClient(timeout=None)
69 | httpx.AsyncClient(timeout=None)
|
S113.py:60:1: S113 Probable use of `httpx` call without timeout
S113.py:69:19: S113 Probable use of `httpx` call with timeout set to `None`
|
58 | httpx.Client()
59 | httpx.Client(timeout=None)
60 | httpx.AsyncClient()
| ^^^^^^^^^^^^^^^^^ S113
61 | httpx.AsyncClient(timeout=None)
62 | with httpx.Client() as client:
|
S113.py:61:19: S113 Probable use of `httpx` call with timeout set to `None`
|
59 | httpx.Client(timeout=None)
60 | httpx.AsyncClient()
61 | httpx.AsyncClient(timeout=None)
67 | httpx.head('https://gmail.com', timeout=None)
68 | httpx.Client(timeout=None)
69 | httpx.AsyncClient(timeout=None)
| ^^^^^^^^^^^^ S113
62 | with httpx.Client() as client:
63 | client.get('https://gmail.com')
70 |
71 | with httpx.Client(timeout=None) as client:
|
S113.py:62:6: S113 Probable use of `httpx` call without timeout
S113.py:71:19: S113 Probable use of `httpx` call with timeout set to `None`
|
60 | httpx.AsyncClient()
61 | httpx.AsyncClient(timeout=None)
62 | with httpx.Client() as client:
| ^^^^^^^^^^^^ S113
63 | client.get('https://gmail.com')
64 | with httpx.Client(timeout=None) as client:
|
S113.py:64:19: S113 Probable use of `httpx` call with timeout set to `None`
|
62 | with httpx.Client() as client:
63 | client.get('https://gmail.com')
64 | with httpx.Client(timeout=None) as client:
69 | httpx.AsyncClient(timeout=None)
70 |
71 | with httpx.Client(timeout=None) as client:
| ^^^^^^^^^^^^ S113
65 | client.get('https://gmail.com')
66 | async def bar():
|
S113.py:67:16: S113 Probable use of `httpx` call without timeout
|
65 | client.get('https://gmail.com')
66 | async def bar():
67 | async with httpx.AsyncClient() as client:
| ^^^^^^^^^^^^^^^^^ S113
68 | await client.get('https://gmail.com')
69 | async def baz():
|
S113.py:70:34: S113 Probable use of `httpx` call with timeout set to `None`
|
68 | await client.get('https://gmail.com')
69 | async def baz():
70 | async with httpx.AsyncClient(timeout=None) as client:
| ^^^^^^^^^^^^ S113
71 | await client.get('https://gmail.com')
72 | client.get('https://gmail.com')
|