The following issues were found

test/mitmproxy/data/addonscripts/configure.py
8 issues
Unable to import 'mitmproxy'
Error

Line: 3 Column: 1

              import typing

from mitmproxy import exceptions


class OptionAddon:
    def load(self, loader):
        loader.add_option(
            name = "optionaddon",

            

Reported by Pylint.

Missing module docstring
Error

Line: 1 Column: 1

              import typing

from mitmproxy import exceptions


class OptionAddon:
    def load(self, loader):
        loader.add_option(
            name = "optionaddon",

            

Reported by Pylint.

Missing class docstring
Error

Line: 6 Column: 1

              from mitmproxy import exceptions


class OptionAddon:
    def load(self, loader):
        loader.add_option(
            name = "optionaddon",
            typespec = typing.Optional[int],
            default = None,

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 7 Column: 5

              

class OptionAddon:
    def load(self, loader):
        loader.add_option(
            name = "optionaddon",
            typespec = typing.Optional[int],
            default = None,
            help = "Option Addon",

            

Reported by Pylint.

Method could be a function
Error

Line: 7 Column: 5

              

class OptionAddon:
    def load(self, loader):
        loader.add_option(
            name = "optionaddon",
            typespec = typing.Optional[int],
            default = None,
            help = "Option Addon",

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 15 Column: 5

                          help = "Option Addon",
        )

    def configure(self, updates):
        raise exceptions.OptionsError("Options Error")

addons = [
    OptionAddon()
]

            

Reported by Pylint.

Method could be a function
Error

Line: 15 Column: 5

                          help = "Option Addon",
        )

    def configure(self, updates):
        raise exceptions.OptionsError("Options Error")

addons = [
    OptionAddon()
]

            

Reported by Pylint.

Trailing newlines
Error

Line: 21 Column: 1

              addons = [
    OptionAddon()
]


            

Reported by Pylint.

mitmproxy/net/http/http1/read.py
8 issues
TODO: we can probably get rid of this check?
Error

Line: 114 Column: 3

                          port = port or url.default_port(scheme)
            if not port:
                raise ValueError
            # TODO: we can probably get rid of this check?
            url.parse(target)

        raise_if_http_version_unknown(http_version)
    except ValueError as e:
        raise ValueError(f"Bad HTTP request line: {line!r}") from e

            

Reported by Pylint.

Consider explicitly re-raising using the 'from' keyword
Error

Line: 165 Column: 17

                                  raise ValueError()
                ret.append((name, value))
            except ValueError:
                raise ValueError(f"Invalid header line: {line!r}")
    return Headers(ret)


def read_request_head(lines: List[bytes]) -> Request:
    """

            

Reported by Pylint.

Missing module docstring
Error

Line: 1 Column: 1

              import re
import time
from typing import List, Tuple, Iterable, Optional

from mitmproxy.http import Request, Headers, Response
from mitmproxy.net.http import url


def get_header_tokens(headers, key):

            

Reported by Pylint.

Unnecessary "elif" after "return"
Error

Line: 30 Column: 9

                  """
    if "connection" in headers:
        tokens = get_header_tokens(headers, "connection")
        if "close" in tokens:
            return True
        elif "keep-alive" in tokens:
            return False

    return http_version not in (

            

Reported by Pylint.

Too many return statements (9/6)
Error

Line: 41 Column: 1

                  )


def expected_http_body_size(
        request: Request,
        response: Optional[Response] = None
) -> Optional[int]:
    """
        Returns:

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 88 Column: 1

                  return -1


def raise_if_http_version_unknown(http_version: bytes) -> None:
    if not re.match(br"^HTTP/\d\.\d$", http_version):
        raise ValueError(f"Unknown HTTP version: {http_version!r}")


def _read_request_line(line: bytes) -> Tuple[str, int, bytes, bytes, bytes, bytes, bytes]:

            

Reported by Pylint.

Variable name "e" doesn't conform to snake_case naming style
Error

Line: 118 Column: 5

                          url.parse(target)

        raise_if_http_version_unknown(http_version)
    except ValueError as e:
        raise ValueError(f"Bad HTTP request line: {line!r}") from e

    return host, port, method, scheme, authority, path, http_version



            

Reported by Pylint.

Variable name "e" doesn't conform to snake_case naming style
Error

Line: 133 Column: 5

                      http_version, status_code_str, reason = parts
        status_code = int(status_code_str)
        raise_if_http_version_unknown(http_version)
    except ValueError as e:
        raise ValueError(f"Bad HTTP response line: {line!r}") from e

    return http_version, status_code, reason



            

Reported by Pylint.

test/mitmproxy/coretypes/test_bidi.py
7 issues
Unable to import 'pytest'
Error

Line: 1 Column: 1

              import pytest
from mitmproxy.coretypes import bidi


def test_bidi():
    b = bidi.BiDi(a=1, b=2)
    assert b.a == 1
    assert b.get_name(1) == "a"
    assert b.get_name(5) is None

            

Reported by Pylint.

Missing module docstring
Error

Line: 1 Column: 1

              import pytest
from mitmproxy.coretypes import bidi


def test_bidi():
    b = bidi.BiDi(a=1, b=2)
    assert b.a == 1
    assert b.get_name(1) == "a"
    assert b.get_name(5) is None

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 5 Column: 1

              from mitmproxy.coretypes import bidi


def test_bidi():
    b = bidi.BiDi(a=1, b=2)
    assert b.a == 1
    assert b.get_name(1) == "a"
    assert b.get_name(5) is None
    with pytest.raises(AttributeError):

            

Reported by Pylint.

Variable name "b" doesn't conform to snake_case naming style
Error

Line: 6 Column: 5

              

def test_bidi():
    b = bidi.BiDi(a=1, b=2)
    assert b.a == 1
    assert b.get_name(1) == "a"
    assert b.get_name(5) is None
    with pytest.raises(AttributeError):
        getattr(b, "c")

            

Reported by Pylint.

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Security

Line: 7
Suggestion: https://bandit.readthedocs.io/en/latest/plugins/b101_assert_used.html

              
def test_bidi():
    b = bidi.BiDi(a=1, b=2)
    assert b.a == 1
    assert b.get_name(1) == "a"
    assert b.get_name(5) is None
    with pytest.raises(AttributeError):
        getattr(b, "c")
    with pytest.raises(ValueError):

            

Reported by Bandit.

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Security

Line: 8
Suggestion: https://bandit.readthedocs.io/en/latest/plugins/b101_assert_used.html

              def test_bidi():
    b = bidi.BiDi(a=1, b=2)
    assert b.a == 1
    assert b.get_name(1) == "a"
    assert b.get_name(5) is None
    with pytest.raises(AttributeError):
        getattr(b, "c")
    with pytest.raises(ValueError):
        bidi.BiDi(one=1, two=1)

            

Reported by Bandit.

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Security

Line: 9
Suggestion: https://bandit.readthedocs.io/en/latest/plugins/b101_assert_used.html

                  b = bidi.BiDi(a=1, b=2)
    assert b.a == 1
    assert b.get_name(1) == "a"
    assert b.get_name(5) is None
    with pytest.raises(AttributeError):
        getattr(b, "c")
    with pytest.raises(ValueError):
        bidi.BiDi(one=1, two=1)

            

Reported by Bandit.

examples/addons/http-trailers.py
7 issues
Unable to import 'mitmproxy'
Error

Line: 10 Column: 1

              body.
"""

from mitmproxy import http
from mitmproxy.http import Headers


def request(flow: http.HTTPFlow):
    if flow.request.trailers:

            

Reported by Pylint.

Unable to import 'mitmproxy.http'
Error

Line: 11 Column: 1

              """

from mitmproxy import http
from mitmproxy.http import Headers


def request(flow: http.HTTPFlow):
    if flow.request.trailers:
        print("HTTP Trailers detected! Request contains:", flow.request.trailers)

            

Reported by Pylint.

Module name "http-trailers" doesn't conform to snake_case naming style
Error

Line: 1 Column: 1

              """
This script simply prints all received HTTP Trailers.

HTTP requests and responses can container trailing headers which are sent after
the body is fully transmitted. Such trailers need to be announced in the initial
headers by name, so the receiving endpoint can wait and read them after the
body.
"""


            

Reported by Pylint.

Missing function or method docstring
Error

Line: 14 Column: 1

              from mitmproxy.http import Headers


def request(flow: http.HTTPFlow):
    if flow.request.trailers:
        print("HTTP Trailers detected! Request contains:", flow.request.trailers)

    if flow.request.path == "/inject_trailers":
        if flow.request.is_http10:

            

Reported by Pylint.

Unnecessary "elif" after "return"
Error

Line: 19 Column: 9

                      print("HTTP Trailers detected! Request contains:", flow.request.trailers)

    if flow.request.path == "/inject_trailers":
        if flow.request.is_http10:
            # HTTP/1.0 doesn't support trailers
            return
        elif flow.request.is_http11:
            if not flow.request.content:
                # Avoid sending a body on GET requests or a 0 byte chunked body with trailers.

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 38 Column: 1

                      print("Injected a new request trailer...", flow.request.headers["trailer"])


def response(flow: http.HTTPFlow):
    if flow.response.trailers:
        print("HTTP Trailers detected! Response contains:", flow.response.trailers)

    if flow.request.path == "/inject_trailers":
        if flow.request.is_http10:

            

Reported by Pylint.

Unnecessary "elif" after "return"
Error

Line: 43 Column: 9

                      print("HTTP Trailers detected! Response contains:", flow.response.trailers)

    if flow.request.path == "/inject_trailers":
        if flow.request.is_http10:
            return
        elif flow.request.is_http11:
            if not flow.response.content:
                return
            flow.response.headers["transfer-encoding"] = "chunked"

            

Reported by Pylint.

examples/contrib/custom_next_layer.py
7 issues
Unable to import 'mitmproxy'
Error

Line: 11 Column: 1

                  - mitmdump -s custom_next_layer.py
    - curl -x localhost:8080 -k https://example.com
"""
from mitmproxy import ctx
from mitmproxy.proxy import layer, layers


def running():
    # We change the connection strategy to lazy so that next_layer happens before we actually connect upstream.

            

Reported by Pylint.

Unable to import 'mitmproxy.proxy'
Error

Line: 12 Column: 1

                  - curl -x localhost:8080 -k https://example.com
"""
from mitmproxy import ctx
from mitmproxy.proxy import layer, layers


def running():
    # We change the connection strategy to lazy so that next_layer happens before we actually connect upstream.
    # Alternatively we could also change the server address in `server_connect`.

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 15 Column: 1

              from mitmproxy.proxy import layer, layers


def running():
    # We change the connection strategy to lazy so that next_layer happens before we actually connect upstream.
    # Alternatively we could also change the server address in `server_connect`.
    ctx.options.connection_strategy = "lazy"



            

Reported by Pylint.

Line too long (111/100)
Error

Line: 16 Column: 1

              

def running():
    # We change the connection strategy to lazy so that next_layer happens before we actually connect upstream.
    # Alternatively we could also change the server address in `server_connect`.
    ctx.options.connection_strategy = "lazy"


def next_layer(nextlayer: layer.NextLayer):

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 21 Column: 1

                  ctx.options.connection_strategy = "lazy"


def next_layer(nextlayer: layer.NextLayer):
    ctx.log(
        f"{nextlayer.context=}\n"
        f"{nextlayer.data_client()[:70]=}\n"
        f"{nextlayer.data_server()[:70]=}\n"
    )

            

Reported by Pylint.

Line too long (113/100)
Error

Line: 35 Column: 1

                      # which our example server here does not accept for plaintext connections.
        nextlayer.context.client.alpn = b""

        # We know all layers that come next: First negotiate TLS with the client, then do simple TCP passthrough.
        # Setting only one layer here would also work, in that case next_layer would be called again after TLS establishment.
        nextlayer.layer = layers.ClientTLSLayer(nextlayer.context)
        nextlayer.layer.child_layer = layers.TCPLayer(nextlayer.context)

            

Reported by Pylint.

Line too long (125/100)
Error

Line: 36 Column: 1

                      nextlayer.context.client.alpn = b""

        # We know all layers that come next: First negotiate TLS with the client, then do simple TCP passthrough.
        # Setting only one layer here would also work, in that case next_layer would be called again after TLS establishment.
        nextlayer.layer = layers.ClientTLSLayer(nextlayer.context)
        nextlayer.layer.child_layer = layers.TCPLayer(nextlayer.context)

            

Reported by Pylint.

examples/contrib/jsondump.py
7 issues
Unable to import 'mitmproxy'
Error

Line: 39 Column: 1

              import json
import requests

from mitmproxy import ctx

FILE_WORKERS = 1
HTTP_WORKERS = 10



            

Reported by Pylint.

Unused variable 'i'
Error

Line: 222 Column: 13

              
        self._init_transformations()

        for i in range(FILE_WORKERS if self.outfile else HTTP_WORKERS):
            t = Thread(target=self.worker)
            t.daemon = True
            t.start()

    def response(self, flow):

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 60 Column: 5

                      self.auth = None
        self.queue = Queue()

    def done(self):
        self.queue.join()
        if self.outfile:
            self.outfile.close()

    fields = {

            

Reported by Pylint.

Unnecessary "elif" after "return"
Error

Line: 155 Column: 9

                      """
        Recursively convert all list/dict elements of type `bytes` into strings.
        """
        if isinstance(obj, dict):
            return {cls.convert_to_strings(key): cls.convert_to_strings(value)
                    for key, value in obj.items()}
        elif isinstance(obj, list) or isinstance(obj, tuple):
            return [cls.convert_to_strings(element) for element in obj]
        elif isinstance(obj, bytes):

            

Reported by Pylint.

Consider merging these isinstance calls to isinstance(obj, (list, tuple))
Error

Line: 158 Column: 14

                      if isinstance(obj, dict):
            return {cls.convert_to_strings(key): cls.convert_to_strings(value)
                    for key, value in obj.items()}
        elif isinstance(obj, list) or isinstance(obj, tuple):
            return [cls.convert_to_strings(element) for element in obj]
        elif isinstance(obj, bytes):
            return str(obj)[2:-1]
        return obj


            

Reported by Pylint.

Missing function or method docstring
Error

Line: 164 Column: 5

                          return str(obj)[2:-1]
        return obj

    def worker(self):
        while True:
            frame = self.queue.get()
            self.dump(frame)
            self.queue.task_done()


            

Reported by Pylint.

Variable name "t" doesn't conform to snake_case naming style
Error

Line: 223 Column: 13

                      self._init_transformations()

        for i in range(FILE_WORKERS if self.outfile else HTTP_WORKERS):
            t = Thread(target=self.worker)
            t.daemon = True
            t.start()

    def response(self, flow):
        """

            

Reported by Pylint.

examples/contrib/remote-debug.py
7 issues
Unable to import 'pydevd_pycharm'
Error

Line: 20 Column: 5

              

def load(l):
    import pydevd_pycharm
    pydevd_pycharm.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True, suspend=False)

            

Reported by Pylint.

Unused argument 'l'
Error

Line: 19 Column: 10

              """


def load(l):
    import pydevd_pycharm
    pydevd_pycharm.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True, suspend=False)

            

Reported by Pylint.

Module name "remote-debug" doesn't conform to snake_case naming style
Error

Line: 1 Column: 1

              """
This script enables remote debugging of the mitmproxy console *UI* with PyCharm.
For general debugging purposes, it is easier to just debug mitmdump within PyCharm.

Usage:
    - pip install pydevd on the mitmproxy machine
    - Open the Run/Debug Configuration dialog box in PyCharm, and select the
      Python Remote Debug configuration type.
    - Debugging works in the way that mitmproxy connects to the debug server

            

Reported by Pylint.

Argument name "l" doesn't conform to snake_case naming style
Error

Line: 19 Column: 1

              """


def load(l):
    import pydevd_pycharm
    pydevd_pycharm.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True, suspend=False)

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 19 Column: 1

              """


def load(l):
    import pydevd_pycharm
    pydevd_pycharm.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True, suspend=False)

            

Reported by Pylint.

Import outside toplevel (pydevd_pycharm)
Error

Line: 20 Column: 5

              

def load(l):
    import pydevd_pycharm
    pydevd_pycharm.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True, suspend=False)

            

Reported by Pylint.

Line too long (108/100)
Error

Line: 21 Column: 1

              
def load(l):
    import pydevd_pycharm
    pydevd_pycharm.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True, suspend=False)

            

Reported by Pylint.

examples/contrib/sslstrip.py
7 issues
Unable to import 'mitmproxy'
Error

Line: 9 Column: 1

              import urllib.parse
import typing  # noqa

from mitmproxy import http

# set of SSL/TLS capable hosts
secure_hosts: typing.Set[str] = set()



            

Reported by Pylint.

Missing function or method docstring
Error

Line: 15 Column: 1

              secure_hosts: typing.Set[str] = set()


def request(flow: http.HTTPFlow) -> None:
    flow.request.headers.pop('If-Modified-Since', None)
    flow.request.headers.pop('Cache-Control', None)

    # do not force https redirection
    flow.request.headers.pop('Upgrade-Insecure-Requests', None)

            

Reported by Pylint.

Line too long (101/100)
Error

Line: 28 Column: 1

                      flow.request.port = 443

        # We need to update the request destination to whatever is specified in the host header:
        # Having no TLS Server Name Indication from the client and just an IP address as request.host
        # in transparent mode, TLS server name certificate validation would fail.
        flow.request.host = flow.request.pretty_host


def response(flow: http.HTTPFlow) -> None:

            

Reported by Pylint.

Missing function or method docstring
Error

Line: 33 Column: 1

                      flow.request.host = flow.request.pretty_host


def response(flow: http.HTTPFlow) -> None:
    assert flow.response
    flow.response.headers.pop('Strict-Transport-Security', None)
    flow.response.headers.pop('Public-Key-Pins', None)

    # strip links in response body

            

Reported by Pylint.

Use of assert detected. The enclosed code will be removed when compiling to optimised byte code.
Security

Line: 34
Suggestion: https://bandit.readthedocs.io/en/latest/plugins/b101_assert_used.html

              

def response(flow: http.HTTPFlow) -> None:
    assert flow.response
    flow.response.headers.pop('Strict-Transport-Security', None)
    flow.response.headers.pop('Public-Key-Pins', None)

    # strip links in response body
    flow.response.content = flow.response.content.replace(b'https://', b'http://')

            

Reported by Bandit.

Line too long (113/100)
Error

Line: 42 Column: 1

                  flow.response.content = flow.response.content.replace(b'https://', b'http://')

    # strip meta tag upgrade-insecure-requests in response body
    csp_meta_tag_pattern = br'<meta.*http-equiv=["\']Content-Security-Policy[\'"].*upgrade-insecure-requests.*?>'
    flow.response.content = re.sub(csp_meta_tag_pattern, b'', flow.response.content, flags=re.IGNORECASE)

    # strip links in 'Location' header
    if flow.response.headers.get('Location', '').startswith('https://'):
        location = flow.response.headers['Location']

            

Reported by Pylint.

Line too long (105/100)
Error

Line: 43 Column: 1

              
    # strip meta tag upgrade-insecure-requests in response body
    csp_meta_tag_pattern = br'<meta.*http-equiv=["\']Content-Security-Policy[\'"].*upgrade-insecure-requests.*?>'
    flow.response.content = re.sub(csp_meta_tag_pattern, b'', flow.response.content, flags=re.IGNORECASE)

    # strip links in 'Location' header
    if flow.response.headers.get('Location', '').startswith('https://'):
        location = flow.response.headers['Location']
        hostname = urllib.parse.urlparse(location).hostname

            

Reported by Pylint.

examples/contrib/suppress_error_responses.py
7 issues
Unable to import 'mitmproxy'
Error

Line: 7 Column: 1

              Without this script, if the web application under test crashes, mitmproxy will send 502 Bad Gateway responses.
These responses are irritating the web application scanner since they obfuscate the actual problem.
"""
from mitmproxy import http
from mitmproxy.exceptions import HttpSyntaxException


def error(self, flow: http.HTTPFlow):
    """Kills the flow if it has an error different to HTTPSyntaxException.

            

Reported by Pylint.

Unable to import 'mitmproxy.exceptions'
Error

Line: 8 Column: 1

              These responses are irritating the web application scanner since they obfuscate the actual problem.
"""
from mitmproxy import http
from mitmproxy.exceptions import HttpSyntaxException


def error(self, flow: http.HTTPFlow):
    """Kills the flow if it has an error different to HTTPSyntaxException.
            Sometimes, web scanners generate malformed HTTP syntax on purpose and we do not want to kill these requests.

            

Reported by Pylint.

Unused argument 'self'
Error

Line: 11 Column: 11

              from mitmproxy.exceptions import HttpSyntaxException


def error(self, flow: http.HTTPFlow):
    """Kills the flow if it has an error different to HTTPSyntaxException.
            Sometimes, web scanners generate malformed HTTP syntax on purpose and we do not want to kill these requests.
    """
    if flow.error is not None and not isinstance(flow.error, HttpSyntaxException):
        flow.kill()

            

Reported by Pylint.

Line too long (111/100)
Error

Line: 2 Column: 1

              """
This script suppresses the 502 Bad Gateway messages, mitmproxy sends if the server is not responsing correctly.
For example, this functionality can be helpful if mitmproxy is used in between a web scanner and a web application.
Without this script, if the web application under test crashes, mitmproxy will send 502 Bad Gateway responses.
These responses are irritating the web application scanner since they obfuscate the actual problem.
"""
from mitmproxy import http
from mitmproxy.exceptions import HttpSyntaxException


            

Reported by Pylint.

Line too long (115/100)
Error

Line: 3 Column: 1

              """
This script suppresses the 502 Bad Gateway messages, mitmproxy sends if the server is not responsing correctly.
For example, this functionality can be helpful if mitmproxy is used in between a web scanner and a web application.
Without this script, if the web application under test crashes, mitmproxy will send 502 Bad Gateway responses.
These responses are irritating the web application scanner since they obfuscate the actual problem.
"""
from mitmproxy import http
from mitmproxy.exceptions import HttpSyntaxException


            

Reported by Pylint.

Line too long (110/100)
Error

Line: 4 Column: 1

              """
This script suppresses the 502 Bad Gateway messages, mitmproxy sends if the server is not responsing correctly.
For example, this functionality can be helpful if mitmproxy is used in between a web scanner and a web application.
Without this script, if the web application under test crashes, mitmproxy will send 502 Bad Gateway responses.
These responses are irritating the web application scanner since they obfuscate the actual problem.
"""
from mitmproxy import http
from mitmproxy.exceptions import HttpSyntaxException


            

Reported by Pylint.

Line too long (120/100)
Error

Line: 13 Column: 1

              
def error(self, flow: http.HTTPFlow):
    """Kills the flow if it has an error different to HTTPSyntaxException.
            Sometimes, web scanners generate malformed HTTP syntax on purpose and we do not want to kill these requests.
    """
    if flow.error is not None and not isinstance(flow.error, HttpSyntaxException):
        flow.kill()

            

Reported by Pylint.

mitmproxy/addons/browser.py
7 issues
Missing module docstring
Error

Line: 1 Column: 1

              import shutil
import subprocess
import tempfile
import typing

from mitmproxy import command
from mitmproxy import ctx



            

Reported by Pylint.

Consider possible security implications associated with subprocess module.
Security blacklist

Line: 2
Suggestion: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html#b404-import-subprocess

              import shutil
import subprocess
import tempfile
import typing

from mitmproxy import command
from mitmproxy import ctx



            

Reported by Bandit.

Missing function or method docstring
Error

Line: 10 Column: 1

              from mitmproxy import ctx


def get_chrome_executable() -> typing.Optional[str]:
    for browser in (
            "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
            # https://stackoverflow.com/questions/40674914/google-chrome-path-in-windows-10
            r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
            r"C:\Program Files (x86)\Google\Application\chrome.exe",

            

Reported by Pylint.

Missing class docstring
Error

Line: 29 Column: 1

                  return None


class Browser:
    browser: typing.List[subprocess.Popen] = []
    tdir: typing.List[tempfile.TemporaryDirectory] = []

    @command.command("browser.start")
    def start(self) -> None:

            

Reported by Pylint.

subprocess call - check for execution of untrusted input.
Security injection

Line: 49
Suggestion: https://bandit.readthedocs.io/en/latest/plugins/b603_subprocess_without_shell_equals_true.html

              
        tdir = tempfile.TemporaryDirectory()
        self.tdir.append(tdir)
        self.browser.append(subprocess.Popen(
            [
                cmd,
                "--user-data-dir=%s" % str(tdir.name),
                "--proxy-server={}:{}".format(
                    ctx.options.listen_host or "127.0.0.1",

            

Reported by Bandit.

Missing function or method docstring
Error

Line: 68 Column: 5

                          stderr = subprocess.DEVNULL,
        ))

    def done(self):
        for browser in self.browser:
            browser.kill()
        for tdir in self.tdir:
            tdir.cleanup()
        self.browser = []

            

Reported by Pylint.

Final newline missing
Error

Line: 74 Column: 1

                      for tdir in self.tdir:
            tdir.cleanup()
        self.browser = []
        self.tdir = []
            

Reported by Pylint.