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

Bandit 1.7.5 false positive for request_without_timeout (B113) #996

Open
volans- opened this issue Mar 10, 2023 · 8 comments
Open

Bandit 1.7.5 false positive for request_without_timeout (B113) #996

volans- opened this issue Mar 10, 2023 · 8 comments
Labels
bug Something isn't working

Comments

@volans-
Copy link

volans- commented Mar 10, 2023

Describe the bug

Bandit is incorrectly marking calls to requests library without a timeout while the code it's actually not calling directly the requests library and the timeout is already set elsewhere.

Reproduction steps

  1. Define this code:
from mylibrary import my_session

class Repro:

    def __init__(self):
        self.requests = my_session(timeout=5)

    def get(self, uri):
        return self.requests.get(uri)
  1. Run bandit: bandit -l -i -r --skip B404,B603 somepath/

  2. Get an error:

>> 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/1.7.5/plugins/b113_request_without_timeout.html
   Location: repro.py:10:15
9	    def get(self, uri):
10	        return self.requests.get(uri)
11

Expected behavior

Bandit should not report any error because the session has a default timeout set via an HTTPAdapter in another library.

The code calls self.requests that could be any kind of object, does bandit do code inspection of the object to detect that is actually a requests session?
I don't think so as it triggered the issue also with my pseudo code that doesn't import the real library.
If it was indeed doing introspection, it should probably also check if there is a default timeout set in the session.

Bandit version

1.7.5 (Default)

Python version

3.9,3.10

Additional context

[note] The dropdown menu of the issues template here on Github has a Python 3.1 version and is missing 3.10, possibly a typo in the template.

@volans- volans- added the bug Something isn't working label Mar 10, 2023
wmfgerrit pushed a commit to wikimedia/operations-cookbooks that referenced this issue Mar 10, 2023
* Temporary suppress bandit request_without_timeout (B113) error because
  of false positives.
* See also: PyCQA/bandit#996

Change-Id: I7a1c276c2a51ac5278d8a20bd7e267c6a9340e96
@ericwb
Copy link
Member

ericwb commented Mar 10, 2023

Yes, if you look back at PR #743 that created this new check, there was concern over introducing such a low confidence plugin. The best course of action for now would be to skip the test if you're getting too many false positives.

@mathieu-lemay
Copy link

I've found a few more examples of false positives.

  1. Getting merge requests with the GitLab client: merge_request = project.mergerequests.get(pull_request_id, lazy=True)
  2. Setting up mocks with requests_mock: requests_mock.post("https://example.org", json={})

@tucked
Copy link

tucked commented Mar 31, 2023

This repros...

import requests
requests.post(
  "https://httpbin.org/post", timeout=60 * 3
)

edit: It's because of the 60 * 3 instead of 180 😩

@ericwb
Copy link
Member

ericwb commented Apr 3, 2023

Should be fixed with #1011

@tucked
Copy link

tucked commented Apr 3, 2023

@ericwb, this still hits on 3260f13:

>> 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/1.7.6/plugins/b113_request_without_timeout.html
   Location: sscce.py:2:0
1       import requests
2       requests.post(
3         "https://httpbin.org/post", timeout=60 * 3
4       )

Should I open a new issue?

@ericwb ericwb reopened this Apr 6, 2023
@ericwb
Copy link
Member

ericwb commented Apr 6, 2023

@tucked Good catch. Seems timeout arg gets missed as the node is an ast.BinOp. The context._get_literal_value() doesn't handle BinOp and defaults to None.

@ericwb
Copy link
Member

ericwb commented Apr 6, 2023

There are some limitations in what Bandit can identify. So in the case of BinOp node, Bandit would be required to evaluate the value (possibly via eval()). But BinOp nodes can also include variables and Bandit doesn't keep track of things like a stack and heap. It moves Bandit into the territory of a compiler or runtime analysis.

This example is simple as it is only "60 * 3", but what about "60 * x" or "x ** 2" or "(5 - 1) ** 2". It can get complicated fast as you possibly have to handle many types of operations and precedence.

One possible way to fix in scenarios with all constants is to:

    code = ast.unparse(literal)
    literal_value = eval(code)

However, even Bandit itself warns about using eval(). And ast.literal_eval() doesn't work on BinOp nodes.

duncanmmacleod added a commit to duncanmmacleod/bandit that referenced this issue Aug 8, 2023
@chasepo
Copy link

chasepo commented Oct 18, 2024

I've been meaning to comment on this as well. If the above isn't possible to evaluate, I dont know if that means this is even harder (or easier) to catch. But this is how we use it when it triggers false positives (deployer configured values).

current_user_req = requests.get(
    f"{USER_LOOKUP_API}/{user_key}",
    cert=(cert_file.name, key_file.name),
    verify=ca_file.name,
    timeout=float(REQUEST_TIMEOUT),
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants