aboutsummaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/addons/addheader.py13
-rw-r--r--examples/addons/anatomy.py15
-rw-r--r--examples/addons/commands-flows.py21
-rw-r--r--examples/addons/commands-paths.py32
-rw-r--r--examples/addons/commands-simple.py17
-rw-r--r--examples/addons/events.py179
-rw-r--r--examples/addons/options-configure.py28
-rw-r--r--examples/addons/options-simple.py24
-rw-r--r--examples/addons/scripting.py3
-rw-r--r--examples/complex/README.md2
-rw-r--r--examples/complex/stream_modify.py2
-rwxr-xr-xexamples/complex/xss_scanner.py8
-rw-r--r--examples/simple/custom_contentview.py4
-rw-r--r--examples/simple/custom_option.py10
-rw-r--r--examples/simple/internet_in_mirror.py9
-rw-r--r--examples/simple/log_events.py4
-rw-r--r--examples/simple/upsidedownternet.py16
17 files changed, 358 insertions, 29 deletions
diff --git a/examples/addons/addheader.py b/examples/addons/addheader.py
new file mode 100644
index 00000000..f4b29268
--- /dev/null
+++ b/examples/addons/addheader.py
@@ -0,0 +1,13 @@
+
+class AddHeader:
+ def __init__(self):
+ self.num = 0
+
+ def response(self, flow):
+ self.num = self.num + 1
+ flow.response.headers["count"] = str(self.num)
+
+
+addons = [
+ AddHeader()
+]
diff --git a/examples/addons/anatomy.py b/examples/addons/anatomy.py
new file mode 100644
index 00000000..c60afeaa
--- /dev/null
+++ b/examples/addons/anatomy.py
@@ -0,0 +1,15 @@
+from mitmproxy import ctx
+
+
+class Counter:
+ def __init__(self):
+ self.num = 0
+
+ def request(self, flow):
+ self.num = self.num + 1
+ ctx.log.info("We've seen %d flows" % self.num)
+
+
+addons = [
+ Counter()
+]
diff --git a/examples/addons/commands-flows.py b/examples/addons/commands-flows.py
new file mode 100644
index 00000000..cebc8f9d
--- /dev/null
+++ b/examples/addons/commands-flows.py
@@ -0,0 +1,21 @@
+import typing
+
+from mitmproxy import command
+from mitmproxy import ctx
+from mitmproxy import flow
+
+
+class MyAddon:
+ def __init__(self):
+ self.num = 0
+
+ @command.command("myaddon.addheader")
+ def addheader(self, flows: typing.Sequence[flow.Flow]) -> None:
+ for f in flows:
+ f.request.headers["myheader"] = "value"
+ ctx.log.alert("done")
+
+
+addons = [
+ MyAddon()
+]
diff --git a/examples/addons/commands-paths.py b/examples/addons/commands-paths.py
new file mode 100644
index 00000000..f37a0fbc
--- /dev/null
+++ b/examples/addons/commands-paths.py
@@ -0,0 +1,32 @@
+import typing
+
+from mitmproxy import command
+from mitmproxy import ctx
+from mitmproxy import flow
+from mitmproxy import types
+
+
+class MyAddon:
+ def __init__(self):
+ self.num = 0
+
+ @command.command("myaddon.histogram")
+ def histogram(
+ self,
+ flows: typing.Sequence[flow.Flow],
+ path: types.Path,
+ ) -> None:
+ totals = {}
+ for f in flows:
+ totals[f.request.host] = totals.setdefault(f.request.host, 0) + 1
+
+ fp = open(path, "w+")
+ for cnt, dom in sorted([(v, k) for (k, v) in totals.items()]):
+ fp.write("%s: %s\n" % (cnt, dom))
+
+ ctx.log.alert("done")
+
+
+addons = [
+ MyAddon()
+]
diff --git a/examples/addons/commands-simple.py b/examples/addons/commands-simple.py
new file mode 100644
index 00000000..c9cd6341
--- /dev/null
+++ b/examples/addons/commands-simple.py
@@ -0,0 +1,17 @@
+from mitmproxy import command
+from mitmproxy import ctx
+
+
+class MyAddon:
+ def __init__(self):
+ self.num = 0
+
+ @command.command("myaddon.inc")
+ def inc(self) -> None:
+ self.num += 1
+ ctx.log.info("num = %s" % self.num)
+
+
+addons = [
+ MyAddon()
+]
diff --git a/examples/addons/events.py b/examples/addons/events.py
new file mode 100644
index 00000000..d3c90430
--- /dev/null
+++ b/examples/addons/events.py
@@ -0,0 +1,179 @@
+import typing
+
+import mitmproxy.addonmanager
+import mitmproxy.connections
+import mitmproxy.http
+import mitmproxy.log
+import mitmproxy.tcp
+import mitmproxy.websocket
+import mitmproxy.proxy.protocol
+
+
+class Events:
+ # HTTP lifecycle
+ def http_connect(self, flow: mitmproxy.http.HTTPFlow):
+ """
+ An HTTP CONNECT request was received. Setting a non 2xx response on
+ the flow will return the response to the client abort the
+ connection. CONNECT requests and responses do not generate the usual
+ HTTP handler events. CONNECT requests are only valid in regular and
+ upstream proxy modes.
+ """
+
+ def requestheaders(self, flow: mitmproxy.http.HTTPFlow):
+ """
+ HTTP request headers were successfully read. At this point, the body
+ is empty.
+ """
+
+ def request(self, flow: mitmproxy.http.HTTPFlow):
+ """
+ The full HTTP request has been read.
+ """
+
+ def responseheaders(self, flow: mitmproxy.http.HTTPFlow):
+ """
+ HTTP response headers were successfully read. At this point, the body
+ is empty.
+ """
+
+ def response(self, flow: mitmproxy.http.HTTPFlow):
+ """
+ The full HTTP response has been read.
+ """
+
+ def error(self, flow: mitmproxy.http.HTTPFlow):
+ """
+ An HTTP error has occurred, e.g. invalid server responses, or
+ interrupted connections. This is distinct from a valid server HTTP
+ error response, which is simply a response with an HTTP error code.
+ """
+
+ # TCP lifecycle
+ def tcp_start(self, flow: mitmproxy.tcp.TCPFlow):
+ """
+ A TCP connection has started.
+ """
+
+ def tcp_message(self, flow: mitmproxy.tcp.TCPFlow):
+ """
+ A TCP connection has received a message. The most recent message
+ will be flow.messages[-1]. The message is user-modifiable.
+ """
+
+ def tcp_error(self, flow: mitmproxy.tcp.TCPFlow):
+ """
+ A TCP error has occurred.
+ """
+
+ def tcp_end(self, flow: mitmproxy.tcp.TCPFlow):
+ """
+ A TCP connection has ended.
+ """
+
+ # Websocket lifecycle
+ def websocket_handshake(self, flow: mitmproxy.http.HTTPFlow):
+ """
+ Called when a client wants to establish a WebSocket connection. The
+ WebSocket-specific headers can be manipulated to alter the
+ handshake. The flow object is guaranteed to have a non-None request
+ attribute.
+ """
+
+ def websocket_start(self, flow: mitmproxy.websocket.WebsocketFlow):
+ """
+ A websocket connection has commenced.
+ """
+
+ def websocket_message(self, flow: mitmproxy.websocket.WebsocketFlow):
+ """
+ Called when a WebSocket message is received from the client or
+ server. The most recent message will be flow.messages[-1]. The
+ message is user-modifiable. Currently there are two types of
+ messages, corresponding to the BINARY and TEXT frame types.
+ """
+
+ def websocket_error(self, flow: mitmproxy.websocket.WebsocketFlow):
+ """
+ A websocket connection has had an error.
+ """
+
+ def websocket_end(self, flow: mitmproxy.websocket.WebsocketFlow):
+ """
+ A websocket connection has ended.
+ """
+
+ # Network lifecycle
+ def clientconnect(self, layer: mitmproxy.proxy.protocol.Layer):
+ """
+ A client has connected to mitmproxy. Note that a connection can
+ correspond to multiple HTTP requests.
+ """
+
+ def clientdisconnect(self, layer: mitmproxy.proxy.protocol.Layer):
+ """
+ A client has disconnected from mitmproxy.
+ """
+
+ def serverconnect(self, conn: mitmproxy.connections.ServerConnection):
+ """
+ Mitmproxy has connected to a server. Note that a connection can
+ correspond to multiple requests.
+ """
+
+ def serverdisconnect(self, conn: mitmproxy.connections.ServerConnection):
+ """
+ Mitmproxy has disconnected from a server.
+ """
+
+ def next_layer(self, layer: mitmproxy.proxy.protocol.Layer):
+ """
+ Network layers are being switched. You may change which layer will
+ be used by returning a new layer object from this event.
+ """
+
+ # General lifecycle
+ def configure(self, updated: typing.Set[str]):
+ """
+ Called when configuration changes. The updated argument is a
+ set-like object containing the keys of all changed options. This
+ event is called during startup with all options in the updated set.
+ """
+
+ def done(self):
+ """
+ Called when the addon shuts down, either by being removed from the
+ mitmproxy instance, or when mitmproxy itself shuts down.
+ """
+
+ def load(self, entry: mitmproxy.addonmanager.Loader):
+ """
+ Called when an addon is first loaded. This event receives a Loader
+ object, which contains methods for adding options and commands. This
+ method is where the addon configures itself.
+ """
+
+ def log(self, entry: mitmproxy.log.LogEntry):
+ """
+ Called whenever a new log entry is created through the mitmproxy
+ context. Be careful not to log from this event, which will cause an
+ infinite loop!
+ """
+
+ def running(self):
+ """
+ Called when the proxy is completely up and running. At this point,
+ you can expect the proxy to be bound to a port, and all addons to be
+ loaded.
+ """
+
+ def tick(self):
+ """
+ A regular ticker - called approximately once every 100ms.
+ """
+
+ def update(self, flows: typing.Sequence[mitmproxy.flow.Flow]):
+ """
+ Update is called when one or more flow objects have been modified,
+ usually from a different addon.
+ """
diff --git a/examples/addons/options-configure.py b/examples/addons/options-configure.py
new file mode 100644
index 00000000..c7638e87
--- /dev/null
+++ b/examples/addons/options-configure.py
@@ -0,0 +1,28 @@
+import typing
+
+from mitmproxy import ctx
+from mitmproxy import exceptions
+
+
+class AddHeader:
+ def load(self, loader):
+ loader.add_option(
+ name = "addheader",
+ typespec = typing.Optional[int],
+ default = None,
+ help = "Add a header to responses",
+ )
+
+ def configure(self, updates):
+ if "addheader" in updates:
+ if ctx.options.addheader is not None and ctx.options.addheader > 100:
+ raise exceptions.OptionsError("addheader must be <= 100")
+
+ def response(self, flow):
+ if ctx.options.addheader is not None:
+ flow.response.headers["addheader"] = str(ctx.options.addheader)
+
+
+addons = [
+ AddHeader()
+]
diff --git a/examples/addons/options-simple.py b/examples/addons/options-simple.py
new file mode 100644
index 00000000..0acefb3f
--- /dev/null
+++ b/examples/addons/options-simple.py
@@ -0,0 +1,24 @@
+from mitmproxy import ctx
+
+
+class AddHeader:
+ def __init__(self):
+ self.num = 0
+
+ def load(self, loader):
+ loader.add_option(
+ name = "addheader",
+ typespec = bool,
+ default = False,
+ help = "Add a count header to responses",
+ )
+
+ def response(self, flow):
+ if ctx.options.addheader:
+ self.num = self.num + 1
+ flow.response.headers["count"] = str(self.num)
+
+
+addons = [
+ AddHeader()
+]
diff --git a/examples/addons/scripting.py b/examples/addons/scripting.py
new file mode 100644
index 00000000..8b23680e
--- /dev/null
+++ b/examples/addons/scripting.py
@@ -0,0 +1,3 @@
+
+def request(flow):
+ flow.request.headers["myheader"] = "value"
diff --git a/examples/complex/README.md b/examples/complex/README.md
index 77dbe2f5..c53503e4 100644
--- a/examples/complex/README.md
+++ b/examples/complex/README.md
@@ -10,7 +10,7 @@
| mitmproxywrapper.py | Bracket mitmproxy run with proxy enable/disable on OS X |
| nonblocking.py | Demonstrate parallel processing with a blocking script |
| remote_debug.py | This script enables remote debugging of the mitmproxy _UI_ with PyCharm. |
-| sslstrip.py | sslstrip-like funtionality implemented with mitmproxy |
+| sslstrip.py | sslstrip-like functionality implemented with mitmproxy |
| stream.py | Enable streaming for all responses. |
| stream_modify.py | Modify a streamed response body. |
| tcp_message.py | Modify a raw TCP connection |
diff --git a/examples/complex/stream_modify.py b/examples/complex/stream_modify.py
index 5e5da95b..46bdcb78 100644
--- a/examples/complex/stream_modify.py
+++ b/examples/complex/stream_modify.py
@@ -3,7 +3,7 @@ This inline script modifies a streamed response.
If you do not need streaming, see the modify_response_body example.
Be aware that content replacement isn't trivial:
- If the transfer encoding isn't chunked, you cannot simply change the content length.
- - If you want to replace all occurences of "foobar", make sure to catch the cases
+ - If you want to replace all occurrences of "foobar", make sure to catch the cases
where one chunk ends with [...]foo" and the next starts with "bar[...].
"""
diff --git a/examples/complex/xss_scanner.py b/examples/complex/xss_scanner.py
index 0ee38cd4..0c0dd0f3 100755
--- a/examples/complex/xss_scanner.py
+++ b/examples/complex/xss_scanner.py
@@ -215,7 +215,7 @@ def get_SQLi_data(new_body: str, original_body: str, request_URL: str, injection
# A qc is either ' or "
def inside_quote(qc: str, substring_bytes: bytes, text_index: int, body_bytes: bytes) -> bool:
- """ Whether the Numberth occurence of the first string in the second
+ """ Whether the Numberth occurrence of the first string in the second
string is inside quotes as defined by the supplied QuoteChar """
substring = substring_bytes.decode('utf-8')
body = body_bytes.decode('utf-8')
@@ -246,7 +246,7 @@ def paths_to_text(html: str, string: str) -> List[str]:
- Note that it does a BFS """
def remove_last_occurence_of_sub_string(string: str, substr: str) -> str:
- """ Delete the last occurence of substr from str
+ """ Delete the last occurrence of substr from str
String String -> String
"""
index = string.rfind(substr)
@@ -274,7 +274,7 @@ def paths_to_text(html: str, string: str) -> List[str]:
def get_XSS_data(body: Union[str, bytes], request_URL: str, injection_point: str) -> Optional[XSSData]:
""" Return a XSSDict if there is a XSS otherwise return None """
def in_script(text, index, body) -> bool:
- """ Whether the Numberth occurence of the first string in the second
+ """ Whether the Numberth occurrence of the first string in the second
string is inside a script tag """
paths = paths_to_text(body.decode('utf-8'), text.decode("utf-8"))
try:
@@ -284,7 +284,7 @@ def get_XSS_data(body: Union[str, bytes], request_URL: str, injection_point: str
return False
def in_HTML(text: bytes, index: int, body: bytes) -> bool:
- """ Whether the Numberth occurence of the first string in the second
+ """ Whether the Numberth occurrence of the first string in the second
string is inside the HTML but not inside a script tag or part of
a HTML attribute"""
# if there is a < then lxml will interpret that as a tag, so only search for the stuff before it
diff --git a/examples/simple/custom_contentview.py b/examples/simple/custom_contentview.py
index b958bdce..77d32474 100644
--- a/examples/simple/custom_contentview.py
+++ b/examples/simple/custom_contentview.py
@@ -7,10 +7,6 @@ from mitmproxy import contentviews
class ViewSwapCase(contentviews.View):
name = "swapcase"
-
- # We don't have a good solution for the keyboard shortcut yet -
- # you manually need to find a free letter. Contributions welcome :)
- prompt = ("swap case text", "z")
content_types = ["text/plain"]
def __call__(self, data, **metadata) -> contentviews.TViewResult:
diff --git a/examples/simple/custom_option.py b/examples/simple/custom_option.py
index 5b6070dd..8d0cfe7f 100644
--- a/examples/simple/custom_option.py
+++ b/examples/simple/custom_option.py
@@ -1,3 +1,13 @@
+"""
+This example shows how addons can register custom options
+that can be configured at startup or during execution
+from the options dialog within mitmproxy.
+
+Example:
+
+$ mitmproxy --set custom=true
+$ mitmproxy --set custom # shorthand for boolean options
+"""
from mitmproxy import ctx
diff --git a/examples/simple/internet_in_mirror.py b/examples/simple/internet_in_mirror.py
new file mode 100644
index 00000000..5d3e555d
--- /dev/null
+++ b/examples/simple/internet_in_mirror.py
@@ -0,0 +1,9 @@
+"""
+This script reflects all content passing through the proxy.
+"""
+from mitmproxy import http
+
+
+def response(flow: http.HTTPFlow) -> None:
+ reflector = b"<style>body {transform: scaleX(-1);}</style></head>"
+ flow.response.content = flow.response.content.replace(b"</head>", reflector)
diff --git a/examples/simple/log_events.py b/examples/simple/log_events.py
index 581b99f3..b9aa2c1f 100644
--- a/examples/simple/log_events.py
+++ b/examples/simple/log_events.py
@@ -1,8 +1,6 @@
"""
It is recommended to use `ctx.log` for logging within a script.
-This goes to the event log in mitmproxy and to stdout in mitmdump.
-
-If you want to help us out: https://github.com/mitmproxy/mitmproxy/issues/1530 :-)
+print() statements are equivalent to ctx.log.warn().
"""
from mitmproxy import ctx
diff --git a/examples/simple/upsidedownternet.py b/examples/simple/upsidedownternet.py
deleted file mode 100644
index f150a5c3..00000000
--- a/examples/simple/upsidedownternet.py
+++ /dev/null
@@ -1,16 +0,0 @@
-"""
-This script rotates all images passing through the proxy by 180 degrees.
-"""
-import io
-from PIL import Image
-from mitmproxy import http
-
-
-def response(flow: http.HTTPFlow) -> None:
- if flow.response.headers.get("content-type", "").startswith("image"):
- s = io.BytesIO(flow.response.content)
- img = Image.open(s).rotate(180)
- s2 = io.BytesIO()
- img.save(s2, "png")
- flow.response.content = s2.getvalue()
- flow.response.headers["content-type"] = "image/png"