mirror of
https://github.com/Grasscutters/mitmproxy.git
synced 2024-11-26 18:18:25 +00:00
Merge pull request #2264 from cortesi/options
Change the way addons handle options
This commit is contained in:
commit
1daf0b3f0a
@ -6,6 +6,6 @@ def load(l):
|
|||||||
l.add_option("custom", bool, False, "A custom option")
|
l.add_option("custom", bool, False, "A custom option")
|
||||||
|
|
||||||
|
|
||||||
def configure(options, updated):
|
def configure(updated):
|
||||||
if "custom" in updated:
|
if "custom" in updated:
|
||||||
ctx.log.info("custom option value: %s" % options.custom)
|
ctx.log.info("custom option value: %s" % ctx.options.custom)
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
# (this script works best with --anticache)
|
# (this script works best with --anticache)
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
class Injector:
|
class Injector:
|
||||||
def __init__(self):
|
|
||||||
self.iframe_url = None
|
|
||||||
|
|
||||||
def load(self, loader):
|
def load(self, loader):
|
||||||
loader.add_option(
|
loader.add_option(
|
||||||
"iframe", str, "", "IFrame to inject"
|
"iframe", str, "", "IFrame to inject"
|
||||||
)
|
)
|
||||||
|
|
||||||
def configure(self, options, updated):
|
|
||||||
self.iframe_url = options.iframe
|
|
||||||
|
|
||||||
def response(self, flow):
|
def response(self, flow):
|
||||||
if self.iframe_url:
|
if ctx.options.iframe:
|
||||||
html = BeautifulSoup(flow.response.content, "html.parser")
|
html = BeautifulSoup(flow.response.content, "html.parser")
|
||||||
if html.body:
|
if html.body:
|
||||||
iframe = html.new_tag(
|
iframe = html.new_tag(
|
||||||
"iframe",
|
"iframe",
|
||||||
src=self.iframe_url,
|
src=ctx.options.iframe,
|
||||||
frameborder=0,
|
frameborder=0,
|
||||||
height=0,
|
height=0,
|
||||||
width=0)
|
width=0)
|
||||||
|
@ -110,7 +110,7 @@ class AddonManager:
|
|||||||
master.options.changed.connect(self._configure_all)
|
master.options.changed.connect(self._configure_all)
|
||||||
|
|
||||||
def _configure_all(self, options, updated):
|
def _configure_all(self, options, updated):
|
||||||
self.trigger("configure", options, updated)
|
self.trigger("configure", updated)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""
|
"""
|
||||||
@ -129,9 +129,13 @@ class AddonManager:
|
|||||||
|
|
||||||
def register(self, addon):
|
def register(self, addon):
|
||||||
"""
|
"""
|
||||||
Register an addon and all its sub-addons with the manager without
|
Register an addon, call its load event, and then register all its
|
||||||
adding it to the chain. This should be used by addons that
|
sub-addons. This should be used by addons that dynamically manage
|
||||||
dynamically manage addons. Must be called within a current context.
|
addons.
|
||||||
|
|
||||||
|
If the calling addon is already running, it should follow with
|
||||||
|
running and configure events. Must be called within a current
|
||||||
|
context.
|
||||||
"""
|
"""
|
||||||
for a in traverse([addon]):
|
for a in traverse([addon]):
|
||||||
name = _get_name(a)
|
name = _get_name(a)
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
class AntiCache:
|
class AntiCache:
|
||||||
def __init__(self):
|
|
||||||
self.enabled = False
|
|
||||||
|
|
||||||
def configure(self, options, updated):
|
|
||||||
self.enabled = options.anticache
|
|
||||||
|
|
||||||
def request(self, flow):
|
def request(self, flow):
|
||||||
if self.enabled:
|
if ctx.options.anticache:
|
||||||
flow.request.anticache()
|
flow.request.anticache()
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
class AntiComp:
|
class AntiComp:
|
||||||
def __init__(self):
|
|
||||||
self.enabled = False
|
|
||||||
|
|
||||||
def configure(self, options, updated):
|
|
||||||
self.enabled = options.anticomp
|
|
||||||
|
|
||||||
def request(self, flow):
|
def request(self, flow):
|
||||||
if self.enabled:
|
if ctx.options.anticomp:
|
||||||
flow.request.anticomp()
|
flow.request.anticomp()
|
||||||
|
@ -7,7 +7,7 @@ class CheckALPN:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.failed = False
|
self.failed = False
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
self.failed = mitmproxy.ctx.master.options.http2 and not tcp.HAS_ALPN
|
self.failed = mitmproxy.ctx.master.options.http2 and not tcp.HAS_ALPN
|
||||||
if self.failed:
|
if self.failed:
|
||||||
ctx.log.warn(
|
ctx.log.warn(
|
||||||
|
@ -5,7 +5,7 @@ class CheckCA:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.failed = False
|
self.failed = False
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
has_ca = (
|
has_ca = (
|
||||||
mitmproxy.ctx.master.server and
|
mitmproxy.ctx.master.server and
|
||||||
mitmproxy.ctx.master.server.config and
|
mitmproxy.ctx.master.server.config and
|
||||||
|
@ -20,12 +20,12 @@ class ClientPlayback:
|
|||||||
def load(self, flows: typing.Sequence[flow.Flow]):
|
def load(self, flows: typing.Sequence[flow.Flow]):
|
||||||
self.flows = flows
|
self.flows = flows
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
if "client_replay" in updated:
|
if "client_replay" in updated:
|
||||||
if options.client_replay:
|
if ctx.options.client_replay:
|
||||||
ctx.log.info("Client Replay: {}".format(options.client_replay))
|
ctx.log.info("Client Replay: {}".format(ctx.options.client_replay))
|
||||||
try:
|
try:
|
||||||
flows = io.read_flows_from_paths(options.client_replay)
|
flows = io.read_flows_from_paths(ctx.options.client_replay)
|
||||||
except exceptions.FlowReadException as e:
|
except exceptions.FlowReadException as e:
|
||||||
raise exceptions.OptionsError(str(e))
|
raise exceptions.OptionsError(str(e))
|
||||||
self.load(flows)
|
self.load(flows)
|
||||||
|
@ -4,12 +4,14 @@
|
|||||||
"""
|
"""
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import platform
|
from mitmproxy import platform
|
||||||
|
from mitmproxy import ctx
|
||||||
from mitmproxy.net import server_spec
|
from mitmproxy.net import server_spec
|
||||||
from mitmproxy.utils import human
|
from mitmproxy.utils import human
|
||||||
|
|
||||||
|
|
||||||
class CoreOptionValidation:
|
class CoreOptionValidation:
|
||||||
def configure(self, opts, updated):
|
def configure(self, updated):
|
||||||
|
opts = ctx.options
|
||||||
if opts.add_upstream_certs_to_client_chain and not opts.upstream_cert:
|
if opts.add_upstream_certs_to_client_chain and not opts.upstream_cert:
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"The no-upstream-cert and add-upstream-certs-to-client-chain "
|
"The no-upstream-cert and add-upstream-certs-to-client-chain "
|
||||||
|
@ -14,9 +14,6 @@ class DisableH2C:
|
|||||||
by sending the connection preface. We just kill those flows.
|
by sending the connection preface. We just kill those flows.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def configure(self, options, updated):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def process_flow(self, f):
|
def process_flow(self, f):
|
||||||
if f.request.headers.get('upgrade', '') == 'h2c':
|
if f.request.headers.get('upgrade', '') == 'h2c':
|
||||||
mitmproxy.ctx.log.warn("HTTP/2 cleartext connections (h2c upgrade requests) are currently not supported.")
|
mitmproxy.ctx.log.warn("HTTP/2 cleartext connections (h2c upgrade requests) are currently not supported.")
|
||||||
|
@ -29,24 +29,18 @@ def colorful(line, styles):
|
|||||||
class Dumper:
|
class Dumper:
|
||||||
def __init__(self, outfile=sys.stdout):
|
def __init__(self, outfile=sys.stdout):
|
||||||
self.filter = None # type: flowfilter.TFilter
|
self.filter = None # type: flowfilter.TFilter
|
||||||
self.flow_detail = None # type: int
|
|
||||||
self.outfp = outfile # type: typing.io.TextIO
|
self.outfp = outfile # type: typing.io.TextIO
|
||||||
self.showhost = None # type: bool
|
|
||||||
self.default_contentview = "auto" # type: str
|
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
if "view_filter" in updated:
|
if "view_filter" in updated:
|
||||||
if options.view_filter:
|
if ctx.options.view_filter:
|
||||||
self.filter = flowfilter.parse(options.view_filter)
|
self.filter = flowfilter.parse(ctx.options.view_filter)
|
||||||
if not self.filter:
|
if not self.filter:
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"Invalid filter expression: %s" % options.view_filter
|
"Invalid filter expression: %s" % ctx.options.view_filter
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.filter = None
|
self.filter = None
|
||||||
self.flow_detail = options.flow_detail
|
|
||||||
self.showhost = options.showhost
|
|
||||||
self.default_contentview = options.default_contentview
|
|
||||||
|
|
||||||
def echo(self, text, ident=None, **style):
|
def echo(self, text, ident=None, **style):
|
||||||
if ident:
|
if ident:
|
||||||
@ -67,13 +61,13 @@ class Dumper:
|
|||||||
|
|
||||||
def _echo_message(self, message):
|
def _echo_message(self, message):
|
||||||
_, lines, error = contentviews.get_message_content_view(
|
_, lines, error = contentviews.get_message_content_view(
|
||||||
self.default_contentview,
|
ctx.options.default_contentview,
|
||||||
message
|
message
|
||||||
)
|
)
|
||||||
if error:
|
if error:
|
||||||
ctx.log.debug(error)
|
ctx.log.debug(error)
|
||||||
|
|
||||||
if self.flow_detail == 3:
|
if ctx.options.flow_detail == 3:
|
||||||
lines_to_echo = itertools.islice(lines, 70)
|
lines_to_echo = itertools.islice(lines, 70)
|
||||||
else:
|
else:
|
||||||
lines_to_echo = lines
|
lines_to_echo = lines
|
||||||
@ -95,7 +89,7 @@ class Dumper:
|
|||||||
if next(lines, None):
|
if next(lines, None):
|
||||||
self.echo("(cut off)", ident=4, dim=True)
|
self.echo("(cut off)", ident=4, dim=True)
|
||||||
|
|
||||||
if self.flow_detail >= 2:
|
if ctx.options.flow_detail >= 2:
|
||||||
self.echo("")
|
self.echo("")
|
||||||
|
|
||||||
def _echo_request_line(self, flow):
|
def _echo_request_line(self, flow):
|
||||||
@ -121,12 +115,12 @@ class Dumper:
|
|||||||
fg=method_color,
|
fg=method_color,
|
||||||
bold=True
|
bold=True
|
||||||
)
|
)
|
||||||
if self.showhost:
|
if ctx.options.showhost:
|
||||||
url = flow.request.pretty_url
|
url = flow.request.pretty_url
|
||||||
else:
|
else:
|
||||||
url = flow.request.url
|
url = flow.request.url
|
||||||
terminalWidthLimit = max(shutil.get_terminal_size()[0] - 25, 50)
|
terminalWidthLimit = max(shutil.get_terminal_size()[0] - 25, 50)
|
||||||
if self.flow_detail < 1 and len(url) > terminalWidthLimit:
|
if ctx.options.flow_detail < 1 and len(url) > terminalWidthLimit:
|
||||||
url = url[:terminalWidthLimit] + "…"
|
url = url[:terminalWidthLimit] + "…"
|
||||||
url = click.style(strutils.escape_control_characters(url), bold=True)
|
url = click.style(strutils.escape_control_characters(url), bold=True)
|
||||||
|
|
||||||
@ -176,7 +170,7 @@ class Dumper:
|
|||||||
size = click.style(size, bold=True)
|
size = click.style(size, bold=True)
|
||||||
|
|
||||||
arrows = click.style(" <<", bold=True)
|
arrows = click.style(" <<", bold=True)
|
||||||
if self.flow_detail == 1:
|
if ctx.options.flow_detail == 1:
|
||||||
# This aligns the HTTP response code with the HTTP request method:
|
# This aligns the HTTP response code with the HTTP request method:
|
||||||
# 127.0.0.1:59519: GET http://example.com/
|
# 127.0.0.1:59519: GET http://example.com/
|
||||||
# << 304 Not Modified 0b
|
# << 304 Not Modified 0b
|
||||||
@ -194,16 +188,16 @@ class Dumper:
|
|||||||
def echo_flow(self, f):
|
def echo_flow(self, f):
|
||||||
if f.request:
|
if f.request:
|
||||||
self._echo_request_line(f)
|
self._echo_request_line(f)
|
||||||
if self.flow_detail >= 2:
|
if ctx.options.flow_detail >= 2:
|
||||||
self._echo_headers(f.request.headers)
|
self._echo_headers(f.request.headers)
|
||||||
if self.flow_detail >= 3:
|
if ctx.options.flow_detail >= 3:
|
||||||
self._echo_message(f.request)
|
self._echo_message(f.request)
|
||||||
|
|
||||||
if f.response:
|
if f.response:
|
||||||
self._echo_response_line(f)
|
self._echo_response_line(f)
|
||||||
if self.flow_detail >= 2:
|
if ctx.options.flow_detail >= 2:
|
||||||
self._echo_headers(f.response.headers)
|
self._echo_headers(f.response.headers)
|
||||||
if self.flow_detail >= 3:
|
if ctx.options.flow_detail >= 3:
|
||||||
self._echo_message(f.response)
|
self._echo_message(f.response)
|
||||||
|
|
||||||
if f.error:
|
if f.error:
|
||||||
@ -211,7 +205,7 @@ class Dumper:
|
|||||||
self.echo(" << {}".format(msg), bold=True, fg="red")
|
self.echo(" << {}".format(msg), bold=True, fg="red")
|
||||||
|
|
||||||
def match(self, f):
|
def match(self, f):
|
||||||
if self.flow_detail == 0:
|
if ctx.options.flow_detail == 0:
|
||||||
return False
|
return False
|
||||||
if not self.filter:
|
if not self.filter:
|
||||||
return True
|
return True
|
||||||
@ -239,7 +233,7 @@ class Dumper:
|
|||||||
if self.match(f):
|
if self.match(f):
|
||||||
message = f.messages[-1]
|
message = f.messages[-1]
|
||||||
self.echo(f.message_info(message))
|
self.echo(f.message_info(message))
|
||||||
if self.flow_detail >= 3:
|
if ctx.options.flow_detail >= 3:
|
||||||
self._echo_message(message)
|
self._echo_message(message)
|
||||||
|
|
||||||
def websocket_end(self, f):
|
def websocket_end(self, f):
|
||||||
@ -267,5 +261,5 @@ class Dumper:
|
|||||||
server=repr(f.server_conn.address),
|
server=repr(f.server_conn.address),
|
||||||
direction=direction,
|
direction=direction,
|
||||||
))
|
))
|
||||||
if self.flow_detail >= 3:
|
if ctx.options.flow_detail >= 3:
|
||||||
self._echo_message(message)
|
self._echo_message(message)
|
||||||
|
@ -1,20 +1,21 @@
|
|||||||
from mitmproxy import flowfilter
|
from mitmproxy import flowfilter
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
class Intercept:
|
class Intercept:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.filt = None
|
self.filt = None
|
||||||
|
|
||||||
def configure(self, opts, updated):
|
def configure(self, updated):
|
||||||
if "intercept" in updated:
|
if "intercept" in updated:
|
||||||
if not opts.intercept:
|
if not ctx.options.intercept:
|
||||||
self.filt = None
|
self.filt = None
|
||||||
return
|
return
|
||||||
self.filt = flowfilter.parse(opts.intercept)
|
self.filt = flowfilter.parse(ctx.options.intercept)
|
||||||
if not self.filt:
|
if not self.filt:
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"Invalid interception filter: %s" % opts.intercept
|
"Invalid interception filter: %s" % ctx.options.intercept
|
||||||
)
|
)
|
||||||
|
|
||||||
def process_flow(self, f):
|
def process_flow(self, f):
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from mitmproxy.addons import wsgiapp
|
from mitmproxy.addons import wsgiapp
|
||||||
from mitmproxy.addons.onboardingapp import app
|
from mitmproxy.addons.onboardingapp import app
|
||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
class Onboarding(wsgiapp.WSGIApp):
|
class Onboarding(wsgiapp.WSGIApp):
|
||||||
@ -7,13 +8,11 @@ class Onboarding(wsgiapp.WSGIApp):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(app.Adapter(app.application), None, None)
|
super().__init__(app.Adapter(app.application), None, None)
|
||||||
self.enabled = False
|
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
self.host = options.onboarding_host
|
self.host = ctx.options.onboarding_host
|
||||||
self.port = options.onboarding_port
|
self.port = ctx.options.onboarding_port
|
||||||
self.enabled = options.onboarding
|
|
||||||
|
|
||||||
def request(self, f):
|
def request(self, f):
|
||||||
if self.enabled:
|
if ctx.options.onboarding:
|
||||||
super().request(f)
|
super().request(f)
|
||||||
|
@ -10,6 +10,7 @@ import mitmproxy.net.http
|
|||||||
from mitmproxy import connections # noqa
|
from mitmproxy import connections # noqa
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import http
|
from mitmproxy import http
|
||||||
|
from mitmproxy import ctx
|
||||||
from mitmproxy.net.http import status_codes
|
from mitmproxy.net.http import status_codes
|
||||||
|
|
||||||
REALM = "mitmproxy"
|
REALM = "mitmproxy"
|
||||||
@ -45,7 +46,6 @@ class ProxyAuth:
|
|||||||
self.nonanonymous = False
|
self.nonanonymous = False
|
||||||
self.htpasswd = None
|
self.htpasswd = None
|
||||||
self.singleuser = None
|
self.singleuser = None
|
||||||
self.mode = None
|
|
||||||
self.authenticated = weakref.WeakKeyDictionary() # type: MutableMapping[connections.ClientConnection, Tuple[str, str]]
|
self.authenticated = weakref.WeakKeyDictionary() # type: MutableMapping[connections.ClientConnection, Tuple[str, str]]
|
||||||
"""Contains all connections that are permanently authenticated after an HTTP CONNECT"""
|
"""Contains all connections that are permanently authenticated after an HTTP CONNECT"""
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class ProxyAuth:
|
|||||||
- True, if authentication is done as if mitmproxy is a proxy
|
- True, if authentication is done as if mitmproxy is a proxy
|
||||||
- False, if authentication is done as if mitmproxy is a HTTP server
|
- False, if authentication is done as if mitmproxy is a HTTP server
|
||||||
"""
|
"""
|
||||||
return self.mode in ("regular", "upstream")
|
return ctx.options.mode in ("regular", "upstream")
|
||||||
|
|
||||||
def which_auth_header(self) -> str:
|
def which_auth_header(self) -> str:
|
||||||
if self.is_proxy_auth():
|
if self.is_proxy_auth():
|
||||||
@ -113,16 +113,16 @@ class ProxyAuth:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
# Handlers
|
# Handlers
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
if "proxyauth" in updated:
|
if "proxyauth" in updated:
|
||||||
self.nonanonymous = False
|
self.nonanonymous = False
|
||||||
self.singleuser = None
|
self.singleuser = None
|
||||||
self.htpasswd = None
|
self.htpasswd = None
|
||||||
if options.proxyauth:
|
if ctx.options.proxyauth:
|
||||||
if options.proxyauth == "any":
|
if ctx.options.proxyauth == "any":
|
||||||
self.nonanonymous = True
|
self.nonanonymous = True
|
||||||
elif options.proxyauth.startswith("@"):
|
elif ctx.options.proxyauth.startswith("@"):
|
||||||
p = options.proxyauth[1:]
|
p = ctx.options.proxyauth[1:]
|
||||||
try:
|
try:
|
||||||
self.htpasswd = passlib.apache.HtpasswdFile(p)
|
self.htpasswd = passlib.apache.HtpasswdFile(p)
|
||||||
except (ValueError, OSError) as v:
|
except (ValueError, OSError) as v:
|
||||||
@ -130,20 +130,18 @@ class ProxyAuth:
|
|||||||
"Could not open htpasswd file: %s" % p
|
"Could not open htpasswd file: %s" % p
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
parts = options.proxyauth.split(':')
|
parts = ctx.options.proxyauth.split(':')
|
||||||
if len(parts) != 2:
|
if len(parts) != 2:
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"Invalid single-user auth specification."
|
"Invalid single-user auth specification."
|
||||||
)
|
)
|
||||||
self.singleuser = parts
|
self.singleuser = parts
|
||||||
if "mode" in updated:
|
|
||||||
self.mode = options.mode
|
|
||||||
if self.enabled():
|
if self.enabled():
|
||||||
if options.mode == "transparent":
|
if ctx.options.mode == "transparent":
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"Proxy Authentication not supported in transparent mode."
|
"Proxy Authentication not supported in transparent mode."
|
||||||
)
|
)
|
||||||
if options.mode == "socks5":
|
if ctx.options.mode == "socks5":
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"Proxy Authentication not supported in SOCKS mode. "
|
"Proxy Authentication not supported in SOCKS mode. "
|
||||||
"https://github.com/mitmproxy/mitmproxy/issues/738"
|
"https://github.com/mitmproxy/mitmproxy/issues/738"
|
||||||
|
@ -9,9 +9,6 @@ class ReadFile:
|
|||||||
"""
|
"""
|
||||||
An addon that handles reading from file on startup.
|
An addon that handles reading from file on startup.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
|
||||||
self.path = None
|
|
||||||
|
|
||||||
def load_flows_file(self, path: str) -> int:
|
def load_flows_file(self, path: str) -> int:
|
||||||
path = os.path.expanduser(path)
|
path = os.path.expanduser(path)
|
||||||
cnt = 0
|
cnt = 0
|
||||||
@ -31,16 +28,11 @@ class ReadFile:
|
|||||||
ctx.log.error("Flow file corrupted.")
|
ctx.log.error("Flow file corrupted.")
|
||||||
raise exceptions.FlowReadException(v)
|
raise exceptions.FlowReadException(v)
|
||||||
|
|
||||||
def configure(self, options, updated):
|
|
||||||
if "rfile" in updated and options.rfile:
|
|
||||||
self.path = options.rfile
|
|
||||||
|
|
||||||
def running(self):
|
def running(self):
|
||||||
if self.path:
|
if ctx.options.rfile:
|
||||||
try:
|
try:
|
||||||
self.load_flows_file(self.path)
|
self.load_flows_file(ctx.options.rfile)
|
||||||
except exceptions.FlowReadException as v:
|
except exceptions.FlowReadException as v:
|
||||||
raise exceptions.OptionsError(v)
|
raise exceptions.OptionsError(v)
|
||||||
finally:
|
finally:
|
||||||
self.path = None
|
|
||||||
ctx.master.addons.trigger("processing_complete")
|
ctx.master.addons.trigger("processing_complete")
|
||||||
|
@ -47,7 +47,7 @@ class Replace:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.lst = []
|
self.lst = []
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
"""
|
"""
|
||||||
.replacements is a list of tuples (fpat, rex, s):
|
.replacements is a list of tuples (fpat, rex, s):
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class Replace:
|
|||||||
"""
|
"""
|
||||||
if "replacements" in updated:
|
if "replacements" in updated:
|
||||||
lst = []
|
lst = []
|
||||||
for rep in options.replacements:
|
for rep in ctx.options.replacements:
|
||||||
fpatt, rex, s = parse_hook(rep)
|
fpatt, rex, s = parse_hook(rep)
|
||||||
|
|
||||||
flt = flowfilter.parse(fpatt)
|
flt = flowfilter.parse(fpatt)
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
import os
|
import os
|
||||||
import importlib
|
import importlib
|
||||||
import threading
|
import time
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from mitmproxy import addonmanager
|
from mitmproxy import addonmanager
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import ctx
|
from mitmproxy import ctx
|
||||||
|
|
||||||
import watchdog.events
|
|
||||||
from watchdog.observers import polling
|
|
||||||
|
|
||||||
|
|
||||||
def load_script(actx, path):
|
def load_script(actx, path):
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
@ -28,60 +25,29 @@ def load_script(actx, path):
|
|||||||
sys.path[:] = oldpath
|
sys.path[:] = oldpath
|
||||||
|
|
||||||
|
|
||||||
class ReloadHandler(watchdog.events.FileSystemEventHandler):
|
|
||||||
def __init__(self, callback):
|
|
||||||
self.callback = callback
|
|
||||||
|
|
||||||
def filter(self, event):
|
|
||||||
"""
|
|
||||||
Returns True only when .py file is changed
|
|
||||||
"""
|
|
||||||
if event.is_directory:
|
|
||||||
return False
|
|
||||||
if os.path.basename(event.src_path).startswith("."):
|
|
||||||
return False
|
|
||||||
if event.src_path.endswith(".py"):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def on_modified(self, event):
|
|
||||||
if self.filter(event):
|
|
||||||
self.callback()
|
|
||||||
|
|
||||||
def on_created(self, event):
|
|
||||||
if self.filter(event):
|
|
||||||
self.callback()
|
|
||||||
|
|
||||||
|
|
||||||
class Script:
|
class Script:
|
||||||
"""
|
"""
|
||||||
An addon that manages a single script.
|
An addon that manages a single script.
|
||||||
"""
|
"""
|
||||||
|
ReloadInterval = 2
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self.name = "scriptmanager:" + path
|
self.name = "scriptmanager:" + path
|
||||||
self.path = path
|
self.path = path
|
||||||
self.ns = None
|
self.ns = None
|
||||||
self.observer = None
|
|
||||||
|
|
||||||
self.last_options = None
|
self.last_load = 0
|
||||||
self.should_reload = threading.Event()
|
self.last_mtime = 0
|
||||||
|
|
||||||
def load(self, l):
|
|
||||||
self.ns = load_script(ctx, self.path)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def addons(self):
|
def addons(self):
|
||||||
if self.ns is not None:
|
return [self.ns] if self.ns else []
|
||||||
return [self.ns]
|
|
||||||
return []
|
|
||||||
|
|
||||||
def reload(self):
|
|
||||||
self.should_reload.set()
|
|
||||||
|
|
||||||
def tick(self):
|
def tick(self):
|
||||||
if self.should_reload.is_set():
|
if time.time() - self.last_load > self.ReloadInterval:
|
||||||
self.should_reload.clear()
|
mtime = os.stat(self.path).st_mtime
|
||||||
ctx.log.info("Reloading script: %s" % self.name)
|
if mtime > self.last_mtime:
|
||||||
|
ctx.log.info("Loading script: %s" % self.name)
|
||||||
if self.ns:
|
if self.ns:
|
||||||
ctx.master.addons.remove(self.ns)
|
ctx.master.addons.remove(self.ns)
|
||||||
self.ns = load_script(ctx, self.path)
|
self.ns = load_script(ctx, self.path)
|
||||||
@ -89,18 +55,14 @@ class Script:
|
|||||||
# We're already running, so we have to explicitly register and
|
# We're already running, so we have to explicitly register and
|
||||||
# configure the addon
|
# configure the addon
|
||||||
ctx.master.addons.register(self.ns)
|
ctx.master.addons.register(self.ns)
|
||||||
self.configure(self.last_options, self.last_options.keys())
|
ctx.master.addons.invoke_addon(self.ns, "running")
|
||||||
|
ctx.master.addons.invoke_addon(
|
||||||
def configure(self, options, updated):
|
self.ns,
|
||||||
self.last_options = options
|
"configure",
|
||||||
if not self.observer:
|
ctx.options.keys()
|
||||||
self.observer = polling.PollingObserver()
|
|
||||||
# Bind the handler to the real underlying master object
|
|
||||||
self.observer.schedule(
|
|
||||||
ReloadHandler(self.reload),
|
|
||||||
os.path.dirname(self.path) or "."
|
|
||||||
)
|
)
|
||||||
self.observer.start()
|
self.last_load = time.time()
|
||||||
|
self.last_mtime = mtime
|
||||||
|
|
||||||
|
|
||||||
class ScriptLoader:
|
class ScriptLoader:
|
||||||
@ -118,14 +80,14 @@ class ScriptLoader:
|
|||||||
# Returning once we have proper commands
|
# Returning once we have proper commands
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
if "scripts" in updated:
|
if "scripts" in updated:
|
||||||
for s in options.scripts:
|
for s in ctx.options.scripts:
|
||||||
if options.scripts.count(s) > 1:
|
if ctx.options.scripts.count(s) > 1:
|
||||||
raise exceptions.OptionsError("Duplicate script: %s" % s)
|
raise exceptions.OptionsError("Duplicate script: %s" % s)
|
||||||
|
|
||||||
for a in self.addons[:]:
|
for a in self.addons[:]:
|
||||||
if a.path not in options.scripts:
|
if a.path not in ctx.options.scripts:
|
||||||
ctx.log.info("Un-loading script: %s" % a.name)
|
ctx.log.info("Un-loading script: %s" % a.name)
|
||||||
ctx.master.addons.remove(a)
|
ctx.master.addons.remove(a)
|
||||||
self.addons.remove(a)
|
self.addons.remove(a)
|
||||||
@ -142,7 +104,7 @@ class ScriptLoader:
|
|||||||
|
|
||||||
ordered = []
|
ordered = []
|
||||||
newscripts = []
|
newscripts = []
|
||||||
for s in options.scripts:
|
for s in ctx.options.scripts:
|
||||||
if s in current:
|
if s in current:
|
||||||
ordered.append(current[s])
|
ordered.append(current[s])
|
||||||
else:
|
else:
|
||||||
|
@ -10,8 +10,6 @@ from mitmproxy import io
|
|||||||
|
|
||||||
class ServerPlayback:
|
class ServerPlayback:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.options = None
|
|
||||||
|
|
||||||
self.flowmap = {}
|
self.flowmap = {}
|
||||||
self.stop = False
|
self.stop = False
|
||||||
self.final_flow = None
|
self.final_flow = None
|
||||||
@ -38,27 +36,27 @@ class ServerPlayback:
|
|||||||
queriesArray = urllib.parse.parse_qsl(query, keep_blank_values=True)
|
queriesArray = urllib.parse.parse_qsl(query, keep_blank_values=True)
|
||||||
|
|
||||||
key = [str(r.port), str(r.scheme), str(r.method), str(path)] # type: List[Any]
|
key = [str(r.port), str(r.scheme), str(r.method), str(path)] # type: List[Any]
|
||||||
if not self.options.server_replay_ignore_content:
|
if not ctx.options.server_replay_ignore_content:
|
||||||
if self.options.server_replay_ignore_payload_params and r.multipart_form:
|
if ctx.options.server_replay_ignore_payload_params and r.multipart_form:
|
||||||
key.extend(
|
key.extend(
|
||||||
(k, v)
|
(k, v)
|
||||||
for k, v in r.multipart_form.items(multi=True)
|
for k, v in r.multipart_form.items(multi=True)
|
||||||
if k.decode(errors="replace") not in self.options.server_replay_ignore_payload_params
|
if k.decode(errors="replace") not in ctx.options.server_replay_ignore_payload_params
|
||||||
)
|
)
|
||||||
elif self.options.server_replay_ignore_payload_params and r.urlencoded_form:
|
elif ctx.options.server_replay_ignore_payload_params and r.urlencoded_form:
|
||||||
key.extend(
|
key.extend(
|
||||||
(k, v)
|
(k, v)
|
||||||
for k, v in r.urlencoded_form.items(multi=True)
|
for k, v in r.urlencoded_form.items(multi=True)
|
||||||
if k not in self.options.server_replay_ignore_payload_params
|
if k not in ctx.options.server_replay_ignore_payload_params
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
key.append(str(r.raw_content))
|
key.append(str(r.raw_content))
|
||||||
|
|
||||||
if not self.options.server_replay_ignore_host:
|
if not ctx.options.server_replay_ignore_host:
|
||||||
key.append(r.host)
|
key.append(r.host)
|
||||||
|
|
||||||
filtered = []
|
filtered = []
|
||||||
ignore_params = self.options.server_replay_ignore_params or []
|
ignore_params = ctx.options.server_replay_ignore_params or []
|
||||||
for p in queriesArray:
|
for p in queriesArray:
|
||||||
if p[0] not in ignore_params:
|
if p[0] not in ignore_params:
|
||||||
filtered.append(p)
|
filtered.append(p)
|
||||||
@ -66,9 +64,9 @@ class ServerPlayback:
|
|||||||
key.append(p[0])
|
key.append(p[0])
|
||||||
key.append(p[1])
|
key.append(p[1])
|
||||||
|
|
||||||
if self.options.server_replay_use_headers:
|
if ctx.options.server_replay_use_headers:
|
||||||
headers = []
|
headers = []
|
||||||
for i in self.options.server_replay_use_headers:
|
for i in ctx.options.server_replay_use_headers:
|
||||||
v = r.headers.get(i)
|
v = r.headers.get(i)
|
||||||
headers.append((i, v))
|
headers.append((i, v))
|
||||||
key.append(headers)
|
key.append(headers)
|
||||||
@ -83,7 +81,7 @@ class ServerPlayback:
|
|||||||
"""
|
"""
|
||||||
hsh = self._hash(request)
|
hsh = self._hash(request)
|
||||||
if hsh in self.flowmap:
|
if hsh in self.flowmap:
|
||||||
if self.options.server_replay_nopop:
|
if ctx.options.server_replay_nopop:
|
||||||
return self.flowmap[hsh][0]
|
return self.flowmap[hsh][0]
|
||||||
else:
|
else:
|
||||||
ret = self.flowmap[hsh].pop(0)
|
ret = self.flowmap[hsh].pop(0)
|
||||||
@ -91,13 +89,12 @@ class ServerPlayback:
|
|||||||
del self.flowmap[hsh]
|
del self.flowmap[hsh]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
self.options = options
|
|
||||||
if "server_replay" in updated:
|
if "server_replay" in updated:
|
||||||
self.clear()
|
self.clear()
|
||||||
if options.server_replay:
|
if ctx.options.server_replay:
|
||||||
try:
|
try:
|
||||||
flows = io.read_flows_from_paths(options.server_replay)
|
flows = io.read_flows_from_paths(ctx.options.server_replay)
|
||||||
except exceptions.FlowReadException as e:
|
except exceptions.FlowReadException as e:
|
||||||
raise exceptions.OptionsError(str(e))
|
raise exceptions.OptionsError(str(e))
|
||||||
self.load_flows(flows)
|
self.load_flows(flows)
|
||||||
@ -112,13 +109,13 @@ class ServerPlayback:
|
|||||||
if rflow:
|
if rflow:
|
||||||
response = rflow.response.copy()
|
response = rflow.response.copy()
|
||||||
response.is_replay = True
|
response.is_replay = True
|
||||||
if self.options.refresh_server_playback:
|
if ctx.options.refresh_server_playback:
|
||||||
response.refresh()
|
response.refresh()
|
||||||
f.response = response
|
f.response = response
|
||||||
if not self.flowmap:
|
if not self.flowmap:
|
||||||
self.final_flow = f
|
self.final_flow = f
|
||||||
self.stop = True
|
self.stop = True
|
||||||
elif self.options.replay_kill_extra:
|
elif ctx.options.replay_kill_extra:
|
||||||
ctx.log.warn(
|
ctx.log.warn(
|
||||||
"server_playback: killed non-replay request {}".format(
|
"server_playback: killed non-replay request {}".format(
|
||||||
f.request.url
|
f.request.url
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import flowfilter
|
from mitmproxy import flowfilter
|
||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
def parse_setheader(s):
|
def parse_setheader(s):
|
||||||
@ -43,17 +44,10 @@ class SetHeaders:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.lst = []
|
self.lst = []
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
"""
|
|
||||||
options.setheaders is a tuple of (fpatt, header, value)
|
|
||||||
|
|
||||||
fpatt: String specifying a filter pattern.
|
|
||||||
header: Header name.
|
|
||||||
value: Header value string
|
|
||||||
"""
|
|
||||||
if "setheaders" in updated:
|
if "setheaders" in updated:
|
||||||
self.lst = []
|
self.lst = []
|
||||||
for shead in options.setheaders:
|
for shead in ctx.options.setheaders:
|
||||||
fpatt, header, value = parse_setheader(shead)
|
fpatt, header, value = parse_setheader(shead)
|
||||||
|
|
||||||
flt = flowfilter.parse(fpatt)
|
flt = flowfilter.parse(fpatt)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import flowfilter
|
from mitmproxy import flowfilter
|
||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
class StickyAuth:
|
class StickyAuth:
|
||||||
@ -7,13 +8,13 @@ class StickyAuth:
|
|||||||
self.flt = None
|
self.flt = None
|
||||||
self.hosts = {}
|
self.hosts = {}
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
if "stickyauth" in updated:
|
if "stickyauth" in updated:
|
||||||
if options.stickyauth:
|
if ctx.options.stickyauth:
|
||||||
flt = flowfilter.parse(options.stickyauth)
|
flt = flowfilter.parse(ctx.options.stickyauth)
|
||||||
if not flt:
|
if not flt:
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"stickyauth: invalid filter expression: %s" % options.stickyauth
|
"stickyauth: invalid filter expression: %s" % ctx.options.stickyauth
|
||||||
)
|
)
|
||||||
self.flt = flt
|
self.flt = flt
|
||||||
else:
|
else:
|
||||||
|
@ -5,6 +5,7 @@ from mitmproxy.net.http import cookies
|
|||||||
|
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import flowfilter
|
from mitmproxy import flowfilter
|
||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
def ckey(attrs, f):
|
def ckey(attrs, f):
|
||||||
@ -33,13 +34,13 @@ class StickyCookie:
|
|||||||
self.jar = collections.defaultdict(dict)
|
self.jar = collections.defaultdict(dict)
|
||||||
self.flt = None
|
self.flt = None
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
if "stickycookie" in updated:
|
if "stickycookie" in updated:
|
||||||
if options.stickycookie:
|
if ctx.options.stickycookie:
|
||||||
flt = flowfilter.parse(options.stickycookie)
|
flt = flowfilter.parse(ctx.options.stickycookie)
|
||||||
if not flt:
|
if not flt:
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"stickycookie: invalid filter expression: %s" % options.stickycookie
|
"stickycookie: invalid filter expression: %s" % ctx.options.stickycookie
|
||||||
)
|
)
|
||||||
self.flt = flt
|
self.flt = flt
|
||||||
else:
|
else:
|
||||||
|
@ -8,10 +8,10 @@ class StreamBodies:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.max_size = None
|
self.max_size = None
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
if "stream_large_bodies" in updated and options.stream_large_bodies:
|
if "stream_large_bodies" in updated and ctx.options.stream_large_bodies:
|
||||||
try:
|
try:
|
||||||
self.max_size = human.parse_size(options.stream_large_bodies)
|
self.max_size = human.parse_size(ctx.options.stream_large_bodies)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise exceptions.OptionsError(e)
|
raise exceptions.OptionsError(e)
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import os.path
|
|||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import flowfilter
|
from mitmproxy import flowfilter
|
||||||
from mitmproxy import io
|
from mitmproxy import io
|
||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
|
|
||||||
class StreamFile:
|
class StreamFile:
|
||||||
@ -20,26 +21,26 @@ class StreamFile:
|
|||||||
self.stream = io.FilteredFlowWriter(f, flt)
|
self.stream = io.FilteredFlowWriter(f, flt)
|
||||||
self.active_flows = set()
|
self.active_flows = set()
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
# We're already streaming - stop the previous stream and restart
|
# We're already streaming - stop the previous stream and restart
|
||||||
if "streamfile_filter" in updated:
|
if "streamfile_filter" in updated:
|
||||||
if options.streamfile_filter:
|
if ctx.options.streamfile_filter:
|
||||||
self.filt = flowfilter.parse(options.streamfile_filter)
|
self.filt = flowfilter.parse(ctx.options.streamfile_filter)
|
||||||
if not self.filt:
|
if not self.filt:
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"Invalid filter specification: %s" % options.streamfile_filter
|
"Invalid filter specification: %s" % ctx.options.streamfile_filter
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.filt = None
|
self.filt = None
|
||||||
if "streamfile" in updated:
|
if "streamfile" in updated:
|
||||||
if self.stream:
|
if self.stream:
|
||||||
self.done()
|
self.done()
|
||||||
if options.streamfile:
|
if ctx.options.streamfile:
|
||||||
if options.streamfile.startswith("+"):
|
if ctx.options.streamfile.startswith("+"):
|
||||||
path = options.streamfile[1:]
|
path = ctx.options.streamfile[1:]
|
||||||
mode = "ab"
|
mode = "ab"
|
||||||
else:
|
else:
|
||||||
path = options.streamfile
|
path = ctx.options.streamfile
|
||||||
mode = "wb"
|
mode = "wb"
|
||||||
self.start_stream_to_path(path, mode, self.filt)
|
self.start_stream_to_path(path, mode, self.filt)
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import sys
|
|||||||
import click
|
import click
|
||||||
|
|
||||||
from mitmproxy import log
|
from mitmproxy import log
|
||||||
|
from mitmproxy import ctx
|
||||||
|
|
||||||
# These get over-ridden by the save execution context. Keep them around so we
|
# These get over-ridden by the save execution context. Keep them around so we
|
||||||
# can log directly.
|
# can log directly.
|
||||||
@ -11,19 +12,15 @@ realstderr = sys.stderr
|
|||||||
|
|
||||||
class TermLog:
|
class TermLog:
|
||||||
def __init__(self, outfile=None):
|
def __init__(self, outfile=None):
|
||||||
self.options = None
|
|
||||||
self.outfile = outfile
|
self.outfile = outfile
|
||||||
|
|
||||||
def configure(self, options, updated):
|
|
||||||
self.options = options
|
|
||||||
|
|
||||||
def log(self, e):
|
def log(self, e):
|
||||||
if log.log_tier(e.level) == log.log_tier("error"):
|
if log.log_tier(e.level) == log.log_tier("error"):
|
||||||
outfile = self.outfile or realstderr
|
outfile = self.outfile or realstderr
|
||||||
else:
|
else:
|
||||||
outfile = self.outfile or realstdout
|
outfile = self.outfile or realstdout
|
||||||
|
|
||||||
if self.options.verbosity >= log.log_tier(e.level):
|
if ctx.options.verbosity >= log.log_tier(e.level):
|
||||||
click.secho(
|
click.secho(
|
||||||
e.msg,
|
e.msg,
|
||||||
file=outfile,
|
file=outfile,
|
||||||
|
@ -8,15 +8,8 @@ from mitmproxy.utils import human
|
|||||||
|
|
||||||
|
|
||||||
class TermStatus:
|
class TermStatus:
|
||||||
def __init__(self):
|
|
||||||
self.server = False
|
|
||||||
|
|
||||||
def configure(self, options, updated):
|
|
||||||
if "server" in updated:
|
|
||||||
self.server = options.server
|
|
||||||
|
|
||||||
def running(self):
|
def running(self):
|
||||||
if self.server:
|
if ctx.options.server:
|
||||||
ctx.log.info(
|
ctx.log.info(
|
||||||
"Proxy server listening at http://{}".format(
|
"Proxy server listening at http://{}".format(
|
||||||
human.format_address(ctx.master.server.address)
|
human.format_address(ctx.master.server.address)
|
||||||
|
@ -2,6 +2,7 @@ import re
|
|||||||
import base64
|
import base64
|
||||||
|
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
|
from mitmproxy import ctx
|
||||||
from mitmproxy.utils import strutils
|
from mitmproxy.utils import strutils
|
||||||
|
|
||||||
|
|
||||||
@ -26,20 +27,17 @@ class UpstreamAuth():
|
|||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.auth = None
|
self.auth = None
|
||||||
self.root_mode = None
|
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
# FIXME: We're doing this because our proxy core is terminally confused
|
# FIXME: We're doing this because our proxy core is terminally confused
|
||||||
# at the moment. Ideally, we should be able to check if we're in
|
# at the moment. Ideally, we should be able to check if we're in
|
||||||
# reverse proxy mode at the HTTP layer, so that scripts can put the
|
# reverse proxy mode at the HTTP layer, so that scripts can put the
|
||||||
# proxy in reverse proxy mode for specific reuests.
|
# proxy in reverse proxy mode for specific reuests.
|
||||||
if "mode" in updated:
|
|
||||||
self.root_mode = options.mode
|
|
||||||
if "upstream_auth" in updated:
|
if "upstream_auth" in updated:
|
||||||
if options.upstream_auth is None:
|
if ctx.options.upstream_auth is None:
|
||||||
self.auth = None
|
self.auth = None
|
||||||
else:
|
else:
|
||||||
self.auth = parse_upstream_auth(options.upstream_auth)
|
self.auth = parse_upstream_auth(ctx.options.upstream_auth)
|
||||||
|
|
||||||
def http_connect(self, f):
|
def http_connect(self, f):
|
||||||
if self.auth and f.mode == "upstream":
|
if self.auth and f.mode == "upstream":
|
||||||
@ -49,5 +47,5 @@ class UpstreamAuth():
|
|||||||
if self.auth:
|
if self.auth:
|
||||||
if f.mode == "upstream" and not f.server_conn.via:
|
if f.mode == "upstream" and not f.server_conn.via:
|
||||||
f.request.headers["Proxy-Authorization"] = self.auth
|
f.request.headers["Proxy-Authorization"] = self.auth
|
||||||
elif self.root_mode == "reverse":
|
elif ctx.options.mode == "reverse":
|
||||||
f.request.headers["Proxy-Authorization"] = self.auth
|
f.request.headers["Proxy-Authorization"] = self.auth
|
||||||
|
@ -18,6 +18,7 @@ import sortedcontainers
|
|||||||
import mitmproxy.flow
|
import mitmproxy.flow
|
||||||
from mitmproxy import flowfilter
|
from mitmproxy import flowfilter
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
|
from mitmproxy import ctx
|
||||||
from mitmproxy import http # noqa
|
from mitmproxy import http # noqa
|
||||||
|
|
||||||
# The underlying sorted list implementation expects the sort key to be stable
|
# The underlying sorted list implementation expects the sort key to be stable
|
||||||
@ -302,26 +303,26 @@ class View(collections.Sequence):
|
|||||||
return self._store.get(flow_id)
|
return self._store.get(flow_id)
|
||||||
|
|
||||||
# Event handlers
|
# Event handlers
|
||||||
def configure(self, opts, updated):
|
def configure(self, updated):
|
||||||
if "view_filter" in updated:
|
if "view_filter" in updated:
|
||||||
filt = None
|
filt = None
|
||||||
if opts.view_filter:
|
if ctx.options.view_filter:
|
||||||
filt = flowfilter.parse(opts.view_filter)
|
filt = flowfilter.parse(ctx.options.view_filter)
|
||||||
if not filt:
|
if not filt:
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"Invalid interception filter: %s" % opts.view_filter
|
"Invalid interception filter: %s" % ctx.options.view_filter
|
||||||
)
|
)
|
||||||
self.set_filter(filt)
|
self.set_filter(filt)
|
||||||
if "console_order" in updated:
|
if "console_order" in updated:
|
||||||
if opts.console_order not in self.orders:
|
if ctx.options.console_order not in self.orders:
|
||||||
raise exceptions.OptionsError(
|
raise exceptions.OptionsError(
|
||||||
"Unknown flow order: %s" % opts.console_order
|
"Unknown flow order: %s" % ctx.options.console_order
|
||||||
)
|
)
|
||||||
self.set_order(self.orders[opts.console_order])
|
self.set_order(self.orders[ctx.options.console_order])
|
||||||
if "console_order_reversed" in updated:
|
if "console_order_reversed" in updated:
|
||||||
self.set_reversed(opts.console_order_reversed)
|
self.set_reversed(ctx.options.console_order_reversed)
|
||||||
if "console_focus_follow" in updated:
|
if "console_focus_follow" in updated:
|
||||||
self.focus_follow = opts.console_focus_follow
|
self.focus_follow = ctx.options.console_focus_follow
|
||||||
|
|
||||||
def request(self, f):
|
def request(self, f):
|
||||||
self.add(f)
|
self.add(f)
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import mitmproxy.master # noqa
|
import mitmproxy.master # noqa
|
||||||
import mitmproxy.log # noqa
|
import mitmproxy.log # noqa
|
||||||
|
import mitmproxy.options # noqa
|
||||||
|
|
||||||
master = None # type: "mitmproxy.master.Master"
|
master = None # type: "mitmproxy.master.Master"
|
||||||
log = None # type: "mitmproxy.log.Log"
|
log = None # type: "mitmproxy.log.Log"
|
||||||
|
options = None # type: "mitmproxy.options.Options"
|
||||||
|
@ -50,11 +50,13 @@ class Master:
|
|||||||
return
|
return
|
||||||
mitmproxy_ctx.master = self
|
mitmproxy_ctx.master = self
|
||||||
mitmproxy_ctx.log = log.Log(self)
|
mitmproxy_ctx.log = log.Log(self)
|
||||||
|
mitmproxy_ctx.options = self.options
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
mitmproxy_ctx.master = None
|
mitmproxy_ctx.master = None
|
||||||
mitmproxy_ctx.log = None
|
mitmproxy_ctx.log = None
|
||||||
|
mitmproxy_ctx.options = None
|
||||||
|
|
||||||
def tell(self, mtype, m):
|
def tell(self, mtype, m):
|
||||||
m.reply = controller.DummyReply()
|
m.reply = controller.DummyReply()
|
||||||
|
@ -107,14 +107,16 @@ class context:
|
|||||||
self.master.addons.invoke_addon(
|
self.master.addons.invoke_addon(
|
||||||
addon,
|
addon,
|
||||||
"configure",
|
"configure",
|
||||||
self.options,
|
|
||||||
kwargs.keys()
|
kwargs.keys()
|
||||||
)
|
)
|
||||||
|
|
||||||
def script(self, path):
|
def script(self, path):
|
||||||
|
"""
|
||||||
|
Loads a script from path, and returns the enclosed addon.
|
||||||
|
"""
|
||||||
sc = script.Script(path)
|
sc = script.Script(path)
|
||||||
loader = addonmanager.Loader(self.master)
|
loader = addonmanager.Loader(self.master)
|
||||||
sc.load(loader)
|
self.master.addons.invoke_addon(sc, "load", loader)
|
||||||
for a in addonmanager.traverse(sc.addons):
|
self.configure(sc)
|
||||||
getattr(a, "load", lambda x: None)(loader)
|
self.master.addons.invoke_addon(sc, "tick")
|
||||||
return sc
|
return sc.addons[0] if sc.addons else None
|
||||||
|
@ -76,7 +76,7 @@ def run(MasterKlass, args, extra=None): # pragma: no cover
|
|||||||
unknown = optmanager.load_paths(opts, args.conf)
|
unknown = optmanager.load_paths(opts, args.conf)
|
||||||
server = process_options(parser, opts, args)
|
server = process_options(parser, opts, args)
|
||||||
master = MasterKlass(opts, server)
|
master = MasterKlass(opts, server)
|
||||||
master.addons.trigger("configure", opts, opts.keys())
|
master.addons.trigger("configure", opts.keys())
|
||||||
remaining = opts.update_known(**unknown)
|
remaining = opts.update_known(**unknown)
|
||||||
if remaining and opts.verbosity > 1:
|
if remaining and opts.verbosity > 1:
|
||||||
print("Ignored options: %s" % remaining)
|
print("Ignored options: %s" % remaining)
|
||||||
|
1
setup.py
1
setup.py
@ -80,7 +80,6 @@ setup(
|
|||||||
"ruamel.yaml>=0.13.2, <0.15",
|
"ruamel.yaml>=0.13.2, <0.15",
|
||||||
"tornado>=4.3, <4.6",
|
"tornado>=4.3, <4.6",
|
||||||
"urwid>=1.3.1, <1.4",
|
"urwid>=1.3.1, <1.4",
|
||||||
"watchdog>=0.8.3, <0.9",
|
|
||||||
"brotlipy>=0.5.1, <0.7",
|
"brotlipy>=0.5.1, <0.7",
|
||||||
"sortedcontainers>=1.5.4, <1.6",
|
"sortedcontainers>=1.5.4, <1.6",
|
||||||
# transitive from cryptography, we just blacklist here.
|
# transitive from cryptography, we just blacklist here.
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
from mitmproxy import options
|
|
||||||
from mitmproxy import contentviews
|
from mitmproxy import contentviews
|
||||||
from mitmproxy import proxy
|
|
||||||
from mitmproxy import master
|
|
||||||
from mitmproxy.addons import script
|
|
||||||
|
|
||||||
from mitmproxy.test import tflow
|
from mitmproxy.test import tflow
|
||||||
from mitmproxy.test import tutils
|
from mitmproxy.test import tutils
|
||||||
from mitmproxy.test import taddons
|
from mitmproxy.test import taddons
|
||||||
@ -14,34 +9,17 @@ from ..mitmproxy import tservers
|
|||||||
example_dir = tutils.test_data.push("../examples")
|
example_dir = tutils.test_data.push("../examples")
|
||||||
|
|
||||||
|
|
||||||
class ScriptError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class RaiseMaster(master.Master):
|
|
||||||
def add_log(self, e, level):
|
|
||||||
if level in ("warn", "error"):
|
|
||||||
raise ScriptError(e)
|
|
||||||
|
|
||||||
|
|
||||||
def tscript(cmd, args=""):
|
|
||||||
o = options.Options()
|
|
||||||
cmd = example_dir.path(cmd)
|
|
||||||
m = RaiseMaster(o, proxy.DummyServer())
|
|
||||||
sc = script.Script(cmd)
|
|
||||||
m.addons.add(sc)
|
|
||||||
return m, sc
|
|
||||||
|
|
||||||
|
|
||||||
class TestScripts(tservers.MasterTest):
|
class TestScripts(tservers.MasterTest):
|
||||||
def test_add_header(self):
|
def test_add_header(self):
|
||||||
m, _ = tscript("simple/add_header.py")
|
with taddons.context() as tctx:
|
||||||
|
a = tctx.script(example_dir.path("simple/add_header.py"))
|
||||||
f = tflow.tflow(resp=tutils.tresp())
|
f = tflow.tflow(resp=tutils.tresp())
|
||||||
m.addons.handle_lifecycle("response", f)
|
a.response(f)
|
||||||
assert f.response.headers["newheader"] == "foo"
|
assert f.response.headers["newheader"] == "foo"
|
||||||
|
|
||||||
def test_custom_contentviews(self):
|
def test_custom_contentviews(self):
|
||||||
m, sc = tscript("simple/custom_contentview.py")
|
with taddons.context() as tctx:
|
||||||
|
tctx.script(example_dir.path("simple/custom_contentview.py"))
|
||||||
swapcase = contentviews.get("swapcase")
|
swapcase = contentviews.get("swapcase")
|
||||||
_, fmt = swapcase(b"<html>Test!</html>")
|
_, fmt = swapcase(b"<html>Test!</html>")
|
||||||
assert any(b'tEST!' in val[0][1] for val in fmt)
|
assert any(b'tEST!' in val[0][1] for val in fmt)
|
||||||
@ -61,55 +39,61 @@ class TestScripts(tservers.MasterTest):
|
|||||||
assert b'iframe' in content and b'evil_iframe' in content
|
assert b'iframe' in content and b'evil_iframe' in content
|
||||||
|
|
||||||
def test_modify_form(self):
|
def test_modify_form(self):
|
||||||
m, sc = tscript("simple/modify_form.py")
|
with taddons.context() as tctx:
|
||||||
|
sc = tctx.script(example_dir.path("simple/modify_form.py"))
|
||||||
|
|
||||||
form_header = Headers(content_type="application/x-www-form-urlencoded")
|
form_header = Headers(content_type="application/x-www-form-urlencoded")
|
||||||
f = tflow.tflow(req=tutils.treq(headers=form_header))
|
f = tflow.tflow(req=tutils.treq(headers=form_header))
|
||||||
m.addons.handle_lifecycle("request", f)
|
sc.request(f)
|
||||||
|
|
||||||
assert f.request.urlencoded_form["mitmproxy"] == "rocks"
|
assert f.request.urlencoded_form["mitmproxy"] == "rocks"
|
||||||
|
|
||||||
f.request.headers["content-type"] = ""
|
f.request.headers["content-type"] = ""
|
||||||
m.addons.handle_lifecycle("request", f)
|
sc.request(f)
|
||||||
assert list(f.request.urlencoded_form.items()) == [("foo", "bar")]
|
assert list(f.request.urlencoded_form.items()) == [("foo", "bar")]
|
||||||
|
|
||||||
def test_modify_querystring(self):
|
def test_modify_querystring(self):
|
||||||
m, sc = tscript("simple/modify_querystring.py")
|
with taddons.context() as tctx:
|
||||||
|
sc = tctx.script(example_dir.path("simple/modify_querystring.py"))
|
||||||
f = tflow.tflow(req=tutils.treq(path="/search?q=term"))
|
f = tflow.tflow(req=tutils.treq(path="/search?q=term"))
|
||||||
|
|
||||||
m.addons.handle_lifecycle("request", f)
|
sc.request(f)
|
||||||
assert f.request.query["mitmproxy"] == "rocks"
|
assert f.request.query["mitmproxy"] == "rocks"
|
||||||
|
|
||||||
f.request.path = "/"
|
f.request.path = "/"
|
||||||
m.addons.handle_lifecycle("request", f)
|
sc.request(f)
|
||||||
assert f.request.query["mitmproxy"] == "rocks"
|
assert f.request.query["mitmproxy"] == "rocks"
|
||||||
|
|
||||||
def test_redirect_requests(self):
|
def test_redirect_requests(self):
|
||||||
m, sc = tscript("simple/redirect_requests.py")
|
with taddons.context() as tctx:
|
||||||
|
sc = tctx.script(example_dir.path("simple/redirect_requests.py"))
|
||||||
f = tflow.tflow(req=tutils.treq(host="example.org"))
|
f = tflow.tflow(req=tutils.treq(host="example.org"))
|
||||||
m.addons.handle_lifecycle("request", f)
|
sc.request(f)
|
||||||
assert f.request.host == "mitmproxy.org"
|
assert f.request.host == "mitmproxy.org"
|
||||||
|
|
||||||
def test_send_reply_from_proxy(self):
|
def test_send_reply_from_proxy(self):
|
||||||
m, sc = tscript("simple/send_reply_from_proxy.py")
|
with taddons.context() as tctx:
|
||||||
|
sc = tctx.script(example_dir.path("simple/send_reply_from_proxy.py"))
|
||||||
f = tflow.tflow(req=tutils.treq(host="example.com", port=80))
|
f = tflow.tflow(req=tutils.treq(host="example.com", port=80))
|
||||||
m.addons.handle_lifecycle("request", f)
|
sc.request(f)
|
||||||
assert f.response.content == b"Hello World"
|
assert f.response.content == b"Hello World"
|
||||||
|
|
||||||
def test_dns_spoofing(self):
|
def test_dns_spoofing(self):
|
||||||
m, sc = tscript("complex/dns_spoofing.py")
|
with taddons.context() as tctx:
|
||||||
|
sc = tctx.script(example_dir.path("complex/dns_spoofing.py"))
|
||||||
|
|
||||||
original_host = "example.com"
|
original_host = "example.com"
|
||||||
|
|
||||||
host_header = Headers(host=original_host)
|
host_header = Headers(host=original_host)
|
||||||
f = tflow.tflow(req=tutils.treq(headers=host_header, port=80))
|
f = tflow.tflow(req=tutils.treq(headers=host_header, port=80))
|
||||||
|
|
||||||
m.addons.handle_lifecycle("requestheaders", f)
|
tctx.master.addons.invoke_addon(sc, "requestheaders", f)
|
||||||
|
|
||||||
# Rewrite by reverse proxy mode
|
# Rewrite by reverse proxy mode
|
||||||
f.request.scheme = "https"
|
f.request.scheme = "https"
|
||||||
f.request.port = 443
|
f.request.port = 443
|
||||||
|
|
||||||
m.addons.handle_lifecycle("request", f)
|
tctx.master.addons.invoke_addon(sc, "request", f)
|
||||||
|
|
||||||
assert f.request.scheme == "http"
|
assert f.request.scheme == "http"
|
||||||
assert f.request.port == 80
|
assert f.request.port == 80
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from mitmproxy.addons import onboarding
|
from mitmproxy.addons import onboarding
|
||||||
|
from mitmproxy.test import taddons
|
||||||
from .. import tservers
|
from .. import tservers
|
||||||
|
|
||||||
|
|
||||||
@ -7,9 +8,13 @@ class TestApp(tservers.HTTPProxyTest):
|
|||||||
return [onboarding.Onboarding()]
|
return [onboarding.Onboarding()]
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
|
with taddons.context() as tctx:
|
||||||
|
tctx.configure(self.addons()[0])
|
||||||
assert self.app("/").status_code == 200
|
assert self.app("/").status_code == 200
|
||||||
|
|
||||||
def test_cert(self):
|
def test_cert(self):
|
||||||
|
with taddons.context() as tctx:
|
||||||
|
tctx.configure(self.addons()[0])
|
||||||
for ext in ["pem", "p12"]:
|
for ext in ["pem", "p12"]:
|
||||||
resp = self.app("/cert/%s" % ext)
|
resp = self.app("/cert/%s" % ext)
|
||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
|
@ -66,9 +66,6 @@ def test_configure():
|
|||||||
with pytest.raises(exceptions.OptionsError):
|
with pytest.raises(exceptions.OptionsError):
|
||||||
ctx.configure(up, proxyauth="any", mode="socks5")
|
ctx.configure(up, proxyauth="any", mode="socks5")
|
||||||
|
|
||||||
ctx.configure(up, mode="regular")
|
|
||||||
assert up.mode == "regular"
|
|
||||||
|
|
||||||
|
|
||||||
def test_check():
|
def test_check():
|
||||||
up = proxyauth.ProxyAuth()
|
up = proxyauth.ProxyAuth()
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import traceback
|
import traceback
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import watchdog.events
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
@ -16,34 +15,6 @@ from mitmproxy import master
|
|||||||
from mitmproxy.addons import script
|
from mitmproxy.addons import script
|
||||||
|
|
||||||
|
|
||||||
class Called:
|
|
||||||
def __init__(self):
|
|
||||||
self.called = False
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
self.called = True
|
|
||||||
|
|
||||||
|
|
||||||
def test_reloadhandler():
|
|
||||||
rh = script.ReloadHandler(Called())
|
|
||||||
assert not rh.filter(watchdog.events.DirCreatedEvent("path"))
|
|
||||||
assert not rh.filter(watchdog.events.FileModifiedEvent("/foo/.bar"))
|
|
||||||
assert not rh.filter(watchdog.events.FileModifiedEvent("/foo/bar"))
|
|
||||||
assert rh.filter(watchdog.events.FileModifiedEvent("/foo/bar.py"))
|
|
||||||
|
|
||||||
assert not rh.callback.called
|
|
||||||
rh.on_modified(watchdog.events.FileModifiedEvent("/foo/bar"))
|
|
||||||
assert not rh.callback.called
|
|
||||||
rh.on_modified(watchdog.events.FileModifiedEvent("/foo/bar.py"))
|
|
||||||
assert rh.callback.called
|
|
||||||
rh.callback.called = False
|
|
||||||
|
|
||||||
rh.on_created(watchdog.events.FileCreatedEvent("foo"))
|
|
||||||
assert not rh.callback.called
|
|
||||||
rh.on_created(watchdog.events.FileCreatedEvent("foo.py"))
|
|
||||||
assert rh.callback.called
|
|
||||||
|
|
||||||
|
|
||||||
def test_load_script():
|
def test_load_script():
|
||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
ns = script.load_script(
|
ns = script.load_script(
|
||||||
@ -89,6 +60,8 @@ class TestScript:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
tctx.master.addons.add(sc)
|
tctx.master.addons.add(sc)
|
||||||
|
tctx.configure(sc)
|
||||||
|
sc.tick()
|
||||||
|
|
||||||
rec = tctx.master.addons.get("recorder")
|
rec = tctx.master.addons.get("recorder")
|
||||||
|
|
||||||
@ -107,10 +80,12 @@ class TestScript:
|
|||||||
f.write("\n")
|
f.write("\n")
|
||||||
sc = script.Script(str(f))
|
sc = script.Script(str(f))
|
||||||
tctx.configure(sc)
|
tctx.configure(sc)
|
||||||
for _ in range(5):
|
sc.tick()
|
||||||
sc.reload()
|
for _ in range(3):
|
||||||
|
sc.last_load, sc.last_mtime = 0, 0
|
||||||
sc.tick()
|
sc.tick()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
tctx.master.has_log("Loading")
|
||||||
|
|
||||||
def test_exception(self):
|
def test_exception(self):
|
||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
@ -118,10 +93,12 @@ class TestScript:
|
|||||||
tutils.test_data.path("mitmproxy/data/addonscripts/error.py")
|
tutils.test_data.path("mitmproxy/data/addonscripts/error.py")
|
||||||
)
|
)
|
||||||
tctx.master.addons.add(sc)
|
tctx.master.addons.add(sc)
|
||||||
|
tctx.configure(sc)
|
||||||
|
sc.tick()
|
||||||
|
|
||||||
f = tflow.tflow(resp=True)
|
f = tflow.tflow(resp=True)
|
||||||
tctx.master.addons.trigger("request", f)
|
tctx.master.addons.trigger("request", f)
|
||||||
|
|
||||||
assert tctx.master.logs[0].level == "error"
|
|
||||||
tctx.master.has_log("ValueError: Error!")
|
tctx.master.has_log("ValueError: Error!")
|
||||||
tctx.master.has_log("error.py")
|
tctx.master.has_log("error.py")
|
||||||
|
|
||||||
@ -133,8 +110,10 @@ class TestScript:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
tctx.master.addons.add(sc)
|
tctx.master.addons.add(sc)
|
||||||
|
tctx.configure(sc)
|
||||||
|
sc.tick()
|
||||||
assert sc.ns.event_log == [
|
assert sc.ns.event_log == [
|
||||||
'scriptload', 'addonload'
|
'scriptload', 'addonload', 'scriptconfigure', 'addonconfigure'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -207,21 +186,23 @@ class TestScriptLoader:
|
|||||||
"%s/c.py" % rec,
|
"%s/c.py" % rec,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
tctx.master.addons.invoke_addon(sc, "tick")
|
||||||
debug = [i.msg for i in tctx.master.logs if i.level == "debug"]
|
debug = [i.msg for i in tctx.master.logs if i.level == "debug"]
|
||||||
assert debug == [
|
assert debug == [
|
||||||
'a load',
|
'a load',
|
||||||
'a running',
|
'a running',
|
||||||
|
'a configure',
|
||||||
|
'a tick',
|
||||||
|
|
||||||
'b load',
|
'b load',
|
||||||
'b running',
|
'b running',
|
||||||
|
'b configure',
|
||||||
|
'b tick',
|
||||||
|
|
||||||
'c load',
|
'c load',
|
||||||
'c running',
|
'c running',
|
||||||
|
|
||||||
'a configure',
|
|
||||||
'b configure',
|
|
||||||
'c configure',
|
'c configure',
|
||||||
|
'c tick',
|
||||||
]
|
]
|
||||||
|
|
||||||
tctx.master.logs = []
|
tctx.master.logs = []
|
||||||
@ -233,6 +214,7 @@ class TestScriptLoader:
|
|||||||
"%s/b.py" % rec,
|
"%s/b.py" % rec,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
debug = [i.msg for i in tctx.master.logs if i.level == "debug"]
|
debug = [i.msg for i in tctx.master.logs if i.level == "debug"]
|
||||||
assert debug == [
|
assert debug == [
|
||||||
'c configure',
|
'c configure',
|
||||||
@ -248,13 +230,16 @@ class TestScriptLoader:
|
|||||||
"%s/a.py" % rec,
|
"%s/a.py" % rec,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
tctx.master.addons.invoke_addon(sc, "tick")
|
||||||
|
|
||||||
debug = [i.msg for i in tctx.master.logs if i.level == "debug"]
|
debug = [i.msg for i in tctx.master.logs if i.level == "debug"]
|
||||||
assert debug == [
|
assert debug == [
|
||||||
'c done',
|
'c done',
|
||||||
'b done',
|
'b done',
|
||||||
|
'a configure',
|
||||||
'e load',
|
'e load',
|
||||||
'e running',
|
'e running',
|
||||||
'e configure',
|
'e configure',
|
||||||
'a configure',
|
'e tick',
|
||||||
|
'a tick',
|
||||||
]
|
]
|
||||||
|
@ -6,7 +6,6 @@ from mitmproxy.test import tflow
|
|||||||
|
|
||||||
import mitmproxy.test.tutils
|
import mitmproxy.test.tutils
|
||||||
from mitmproxy.addons import serverplayback
|
from mitmproxy.addons import serverplayback
|
||||||
from mitmproxy import options
|
|
||||||
from mitmproxy import exceptions
|
from mitmproxy import exceptions
|
||||||
from mitmproxy import io
|
from mitmproxy import io
|
||||||
|
|
||||||
@ -39,7 +38,8 @@ def test_tick():
|
|||||||
|
|
||||||
def test_server_playback():
|
def test_server_playback():
|
||||||
sp = serverplayback.ServerPlayback()
|
sp = serverplayback.ServerPlayback()
|
||||||
sp.configure(options.Options(), [])
|
with taddons.context() as tctx:
|
||||||
|
tctx.configure(sp)
|
||||||
f = tflow.tflow(resp=True)
|
f = tflow.tflow(resp=True)
|
||||||
|
|
||||||
assert not sp.flowmap
|
assert not sp.flowmap
|
||||||
@ -57,7 +57,8 @@ def test_server_playback():
|
|||||||
|
|
||||||
def test_ignore_host():
|
def test_ignore_host():
|
||||||
sp = serverplayback.ServerPlayback()
|
sp = serverplayback.ServerPlayback()
|
||||||
sp.configure(options.Options(server_replay_ignore_host=True), [])
|
with taddons.context() as tctx:
|
||||||
|
tctx.configure(sp, server_replay_ignore_host=True)
|
||||||
|
|
||||||
r = tflow.tflow(resp=True)
|
r = tflow.tflow(resp=True)
|
||||||
r2 = tflow.tflow(resp=True)
|
r2 = tflow.tflow(resp=True)
|
||||||
@ -71,7 +72,8 @@ def test_ignore_host():
|
|||||||
|
|
||||||
def test_ignore_content():
|
def test_ignore_content():
|
||||||
s = serverplayback.ServerPlayback()
|
s = serverplayback.ServerPlayback()
|
||||||
s.configure(options.Options(server_replay_ignore_content=False), [])
|
with taddons.context() as tctx:
|
||||||
|
tctx.configure(s, server_replay_ignore_content=False)
|
||||||
|
|
||||||
r = tflow.tflow(resp=True)
|
r = tflow.tflow(resp=True)
|
||||||
r2 = tflow.tflow(resp=True)
|
r2 = tflow.tflow(resp=True)
|
||||||
@ -82,7 +84,7 @@ def test_ignore_content():
|
|||||||
r2.request.content = b"bar"
|
r2.request.content = b"bar"
|
||||||
assert not s._hash(r) == s._hash(r2)
|
assert not s._hash(r) == s._hash(r2)
|
||||||
|
|
||||||
s.configure(options.Options(server_replay_ignore_content=True), [])
|
tctx.configure(s, server_replay_ignore_content=True)
|
||||||
r = tflow.tflow(resp=True)
|
r = tflow.tflow(resp=True)
|
||||||
r2 = tflow.tflow(resp=True)
|
r2 = tflow.tflow(resp=True)
|
||||||
r.request.content = b"foo"
|
r.request.content = b"foo"
|
||||||
@ -98,17 +100,16 @@ def test_ignore_content():
|
|||||||
|
|
||||||
def test_ignore_content_wins_over_params():
|
def test_ignore_content_wins_over_params():
|
||||||
s = serverplayback.ServerPlayback()
|
s = serverplayback.ServerPlayback()
|
||||||
s.configure(
|
with taddons.context() as tctx:
|
||||||
options.Options(
|
tctx.configure(
|
||||||
|
s,
|
||||||
server_replay_ignore_content=True,
|
server_replay_ignore_content=True,
|
||||||
server_replay_ignore_payload_params=[
|
server_replay_ignore_payload_params=[
|
||||||
"param1", "param2"
|
"param1", "param2"
|
||||||
]
|
]
|
||||||
),
|
|
||||||
[]
|
|
||||||
)
|
)
|
||||||
# NOTE: parameters are mutually exclusive in options
|
|
||||||
|
|
||||||
|
# NOTE: parameters are mutually exclusive in options
|
||||||
r = tflow.tflow(resp=True)
|
r = tflow.tflow(resp=True)
|
||||||
r.request.headers["Content-Type"] = "application/x-www-form-urlencoded"
|
r.request.headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||||
r.request.content = b"paramx=y"
|
r.request.content = b"paramx=y"
|
||||||
@ -147,7 +148,8 @@ def test_ignore_payload_params_other_content_type():
|
|||||||
|
|
||||||
def test_hash():
|
def test_hash():
|
||||||
s = serverplayback.ServerPlayback()
|
s = serverplayback.ServerPlayback()
|
||||||
s.configure(options.Options(), [])
|
with taddons.context() as tctx:
|
||||||
|
tctx.configure(s)
|
||||||
|
|
||||||
r = tflow.tflow()
|
r = tflow.tflow()
|
||||||
r2 = tflow.tflow()
|
r2 = tflow.tflow()
|
||||||
@ -166,7 +168,8 @@ def test_hash():
|
|||||||
|
|
||||||
def test_headers():
|
def test_headers():
|
||||||
s = serverplayback.ServerPlayback()
|
s = serverplayback.ServerPlayback()
|
||||||
s.configure(options.Options(server_replay_use_headers=["foo"]), [])
|
with taddons.context() as tctx:
|
||||||
|
tctx.configure(s, server_replay_use_headers=["foo"])
|
||||||
|
|
||||||
r = tflow.tflow(resp=True)
|
r = tflow.tflow(resp=True)
|
||||||
r.request.headers["foo"] = "bar"
|
r.request.headers["foo"] = "bar"
|
||||||
@ -184,7 +187,8 @@ def test_headers():
|
|||||||
|
|
||||||
def test_load():
|
def test_load():
|
||||||
s = serverplayback.ServerPlayback()
|
s = serverplayback.ServerPlayback()
|
||||||
s.configure(options.Options(), [])
|
with taddons.context() as tctx:
|
||||||
|
tctx.configure(s)
|
||||||
|
|
||||||
r = tflow.tflow(resp=True)
|
r = tflow.tflow(resp=True)
|
||||||
r.request.headers["key"] = "one"
|
r.request.headers["key"] = "one"
|
||||||
@ -210,7 +214,8 @@ def test_load():
|
|||||||
|
|
||||||
def test_load_with_server_replay_nopop():
|
def test_load_with_server_replay_nopop():
|
||||||
s = serverplayback.ServerPlayback()
|
s = serverplayback.ServerPlayback()
|
||||||
s.configure(options.Options(server_replay_nopop=True), [])
|
with taddons.context() as tctx:
|
||||||
|
tctx.configure(s, server_replay_nopop=True)
|
||||||
|
|
||||||
r = tflow.tflow(resp=True)
|
r = tflow.tflow(resp=True)
|
||||||
r.request.headers["key"] = "one"
|
r.request.headers["key"] = "one"
|
||||||
@ -227,11 +232,10 @@ def test_load_with_server_replay_nopop():
|
|||||||
|
|
||||||
def test_ignore_params():
|
def test_ignore_params():
|
||||||
s = serverplayback.ServerPlayback()
|
s = serverplayback.ServerPlayback()
|
||||||
s.configure(
|
with taddons.context() as tctx:
|
||||||
options.Options(
|
tctx.configure(
|
||||||
|
s,
|
||||||
server_replay_ignore_params=["param1", "param2"]
|
server_replay_ignore_params=["param1", "param2"]
|
||||||
),
|
|
||||||
[]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
r = tflow.tflow(resp=True)
|
r = tflow.tflow(resp=True)
|
||||||
@ -249,11 +253,11 @@ def test_ignore_params():
|
|||||||
|
|
||||||
def thash(r, r2, setter):
|
def thash(r, r2, setter):
|
||||||
s = serverplayback.ServerPlayback()
|
s = serverplayback.ServerPlayback()
|
||||||
s.configure(
|
with taddons.context() as tctx:
|
||||||
options.Options(
|
s = serverplayback.ServerPlayback()
|
||||||
|
tctx.configure(
|
||||||
|
s,
|
||||||
server_replay_ignore_payload_params=["param1", "param2"]
|
server_replay_ignore_payload_params=["param1", "param2"]
|
||||||
),
|
|
||||||
[]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
setter(r, paramx="x", param1="1")
|
setter(r, paramx="x", param1="1")
|
||||||
|
@ -5,6 +5,7 @@ from mitmproxy.test import taddons
|
|||||||
def test_configure():
|
def test_configure():
|
||||||
ts = termstatus.TermStatus()
|
ts = termstatus.TermStatus()
|
||||||
with taddons.context() as ctx:
|
with taddons.context() as ctx:
|
||||||
|
ctx.configure(ts, server=False)
|
||||||
ts.running()
|
ts.running()
|
||||||
assert not ctx.master.logs
|
assert not ctx.master.logs
|
||||||
ctx.configure(ts, server=True)
|
ctx.configure(ts, server=True)
|
||||||
|
@ -9,12 +9,12 @@ class Addon:
|
|||||||
def load(self, opts):
|
def load(self, opts):
|
||||||
event_log.append("addonload")
|
event_log.append("addonload")
|
||||||
|
|
||||||
def configure(self, options, updated):
|
def configure(self, updated):
|
||||||
event_log.append("addonconfigure")
|
event_log.append("addonconfigure")
|
||||||
|
|
||||||
|
|
||||||
def configure(options, updated):
|
def configure(updated):
|
||||||
event_log.append("addonconfigure")
|
event_log.append("scriptconfigure")
|
||||||
|
|
||||||
|
|
||||||
def load(l):
|
def load(l):
|
||||||
|
@ -2,5 +2,5 @@ from mitmproxy.script import concurrent
|
|||||||
|
|
||||||
|
|
||||||
@concurrent
|
@concurrent
|
||||||
def start(opts):
|
def load(v):
|
||||||
pass
|
pass
|
||||||
|
@ -297,7 +297,7 @@ class TestHTTPAuth(tservers.HTTPProxyTest):
|
|||||||
def test_auth(self):
|
def test_auth(self):
|
||||||
self.master.addons.add(proxyauth.ProxyAuth())
|
self.master.addons.add(proxyauth.ProxyAuth())
|
||||||
self.master.addons.trigger(
|
self.master.addons.trigger(
|
||||||
"configure", self.master.options, self.master.options.keys()
|
"configure", self.master.options.keys()
|
||||||
)
|
)
|
||||||
self.master.options.proxyauth = "test:test"
|
self.master.options.proxyauth = "test:test"
|
||||||
assert self.pathod("202").status_code == 407
|
assert self.pathod("202").status_code == 407
|
||||||
|
@ -2,10 +2,7 @@ from mitmproxy.test import tflow
|
|||||||
from mitmproxy.test import tutils
|
from mitmproxy.test import tutils
|
||||||
from mitmproxy.test import taddons
|
from mitmproxy.test import taddons
|
||||||
|
|
||||||
from mitmproxy import addonmanager
|
|
||||||
from mitmproxy import controller
|
from mitmproxy import controller
|
||||||
from mitmproxy.addons import script
|
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from .. import tservers
|
from .. import tservers
|
||||||
@ -36,25 +33,20 @@ class TestConcurrent(tservers.MasterTest):
|
|||||||
|
|
||||||
def test_concurrent_err(self):
|
def test_concurrent_err(self):
|
||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
sc = script.Script(
|
tctx.script(
|
||||||
tutils.test_data.path(
|
tutils.test_data.path(
|
||||||
"mitmproxy/data/addonscripts/concurrent_decorator_err.py"
|
"mitmproxy/data/addonscripts/concurrent_decorator_err.py"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
l = addonmanager.Loader(tctx.master)
|
|
||||||
sc.load(l)
|
|
||||||
assert tctx.master.has_log("decorator not supported")
|
assert tctx.master.has_log("decorator not supported")
|
||||||
|
|
||||||
def test_concurrent_class(self):
|
def test_concurrent_class(self):
|
||||||
with taddons.context() as tctx:
|
with taddons.context() as tctx:
|
||||||
sc = script.Script(
|
sc = tctx.script(
|
||||||
tutils.test_data.path(
|
tutils.test_data.path(
|
||||||
"mitmproxy/data/addonscripts/concurrent_decorator_class.py"
|
"mitmproxy/data/addonscripts/concurrent_decorator_class.py"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
l = addonmanager.Loader(tctx.master)
|
|
||||||
sc.load(l)
|
|
||||||
|
|
||||||
f1, f2 = tflow.tflow(), tflow.tflow()
|
f1, f2 = tflow.tflow(), tflow.tflow()
|
||||||
tctx.cycle(sc, f1)
|
tctx.cycle(sc, f1)
|
||||||
tctx.cycle(sc, f2)
|
tctx.cycle(sc, f2)
|
||||||
|
@ -30,7 +30,7 @@ class TestMaster(tservers.MasterTest):
|
|||||||
opts["verbosity"] = 1
|
opts["verbosity"] = 1
|
||||||
o = options.Options(**opts)
|
o = options.Options(**opts)
|
||||||
m = console.master.ConsoleMaster(o, proxy.DummyServer())
|
m = console.master.ConsoleMaster(o, proxy.DummyServer())
|
||||||
m.addons.trigger("configure", o, o.keys())
|
m.addons.trigger("configure", o.keys())
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
|
@ -74,7 +74,7 @@ class TestMaster(taddons.RecordingMaster):
|
|||||||
self.state = TestState()
|
self.state = TestState()
|
||||||
self.addons.add(self.state)
|
self.addons.add(self.state)
|
||||||
self.addons.add(*addons)
|
self.addons.add(*addons)
|
||||||
self.addons.trigger("configure", self.options, self.options.keys())
|
self.addons.trigger("configure", self.options.keys())
|
||||||
self.addons.trigger("running")
|
self.addons.trigger("running")
|
||||||
|
|
||||||
def reset(self, addons):
|
def reset(self, addons):
|
||||||
|
Loading…
Reference in New Issue
Block a user