Edit on GitHub

#  Commands

Commands allow users to actively interact with addons - querying their state, commanding them to perform actions, and having them transform data. Like options, commands are typed, and both invocations and data returned from commands are checked at runtime. Commands are a very powerful construct - for instance, all user interaction in mitmproxy console are built by binding commands to keys.

#  Simple example

Let’s begin with a simple example.

"""Add a custom command to mitmproxy's command prompt."""

import logging

from mitmproxy import command


class MyAddon:
    def __init__(self):
        self.num = 0

    @command.command("myaddon.inc")
    def inc(self) -> None:
        self.num += 1
        logging.info(f"num = {self.num}")


addons = [MyAddon()]
examples/addons/commands-simple.py

To see this example in action, start mitmproxy console with the addon loaded:

> mitmproxy -s ./examples/addons/commands-simple.py

Now, make sure the event log is showing, and then execute the command at the prompt (started by typing “:"):

:myaddon.inc

Notice that tab completion works - our addon command has complete parity with builtin commands. There are a few things to note about this example:

#  Working with flows

Since command arguments are typed, we can provide special conveniences for working with certain important data types. The most useful of these are the Flows classes that represent mitmproxy traffic.

Consider the following addon:

"""Handle flows as command arguments."""

import logging
from collections.abc import Sequence

from mitmproxy import command
from mitmproxy import flow
from mitmproxy import http
from mitmproxy.log import ALERT


class MyAddon:
    @command.command("myaddon.addheader")
    def addheader(self, flows: Sequence[flow.Flow]) -> None:
        for f in flows:
            if isinstance(f, http.HTTPFlow):
                f.request.headers["myheader"] = "value"
        logging.log(ALERT, "done")


addons = [MyAddon()]
examples/addons/commands-flows.py

The myaddon.addheader command is quite simple: it takes a sequence of flows, and adds a header to every request. The really interesting aspect of this example is how users specify flows. Because mitmproxy can inspect the type signature, it can expand a text flow selector into a sequence of flows for us transparently. This means that the user has the full flexibility of flow filters available. Let’s try it out.

Start by loading the addon into mitmproxy and sending some traffic through so we have flows to work with:

> mitmproxy -s ./examples/addons/commands-flows.py

We can now invoke our toy command in various ways. Let’s begin by running it just on the currently focused flow:

:myaddon.addheader @focus

We can also invoke it on all flows:

:myaddon.addheader @all

Or only flows from google.com:

:myaddon.addheader ~d google.com

What’s more, we can trivially bind these commands to keyboard shortcuts within mitmproxy if we plan to use them frequently. Flow selectors combined with commands are amazingly powerful, and lets us build and expose re-usable functions for operating on flows.

#  Paths

Commands can take an arbitrary number of arguments. Let’s build on the previous example to illustrate this, and also demonstrate another special type: paths.

"""Handle file paths as command arguments."""

import logging
from collections.abc import Sequence

from mitmproxy import command
from mitmproxy import flow
from mitmproxy import http
from mitmproxy import types
from mitmproxy.log import ALERT


class MyAddon:
    @command.command("myaddon.histogram")
    def histogram(
        self,
        flows: Sequence[flow.Flow],
        path: types.Path,
    ) -> None:
        totals: dict[str, int] = {}
        for f in flows:
            if isinstance(f, http.HTTPFlow):
                totals[f.request.host] = totals.setdefault(f.request.host, 0) + 1

        with open(path, "w+") as fp:
            for cnt, dom in sorted((v, k) for (k, v) in totals.items()):
                fp.write(f"{cnt}: {dom}\n")

        logging.log(ALERT, "done")


addons = [MyAddon()]
examples/addons/commands-paths.py

Our command calculates a histogram of the domains in the specified set of flows, and writes it to a path which is specified as the second argument to the command. Try invoking it like this:

:myaddon.histogram @all /tmp/xxx

Notice that mitmproxy provides tab completion both for the flow specification and the path.

#  Supported Types

The following types are supported for options. If you need to use a type not listed here, please send us a pull request.