merge with master

This commit is contained in:
Clemens 2016-08-09 19:49:36 +02:00
commit b27e456ce2
78 changed files with 656 additions and 2075 deletions

View File

@ -5,10 +5,10 @@ environment:
CI_DEPS: codecov>=2.0.5
CI_COMMANDS: codecov
matrix:
- PYTHON: "C:\\Python27"
TOXENV: "py27"
- PYTHON: "C:\\Python35"
TOXENV: "py35"
- PYTHON: "C:\\Python27"
TOXENV: "py27"
SNAPSHOT_HOST:
secure: NeTo57s2rJhCd/mjKHetXVxCFd3uhr8txnjnAXD1tUI=
@ -35,8 +35,8 @@ deploy_script:
) {
pip install -U virtualenv
.\dev.ps1
cmd /c "python .\release\rtool.py bdist 2>&1"
python .\release\rtool.py upload-snapshot --bdist
cmd /c "python -u .\release\rtool.py bdist 2>&1"
python -u .\release\rtool.py upload-snapshot --bdist --wheel
}
cache:

View File

@ -23,9 +23,9 @@ matrix:
- os: osx
osx_image: xcode7.3
language: generic
env: TOXENV=py35
env: TOXENV=py35 BDIST=1
- python: 3.5
env: TOXENV=py35
env: TOXENV=py35 BDIST=1
- python: 3.5
env: TOXENV=py35 NO_ALPN=1
- python: 2.7
@ -57,14 +57,14 @@ script: set -o pipefail; python -m tox -- --cov netlib --cov mitmproxy --cov pat
after_success:
- |
if [[ $TRAVIS_OS_NAME == "osx" && $TRAVIS_PULL_REQUEST == "false" && ($TRAVIS_BRANCH == "master" || -n $TRAVIS_TAG) ]]
if [[ $BDIST == "1" && $TRAVIS_PULL_REQUEST == "false" && ($TRAVIS_BRANCH == "master" || -n $TRAVIS_TAG) ]]
then
git fetch --unshallow
./dev.sh 3.5
source venv3.5/bin/activate
pip install -e ./release
python ./release/rtool.py bdist
python ./release/rtool.py upload-snapshot --bdist --wheel
python -u ./release/rtool.py bdist
python -u ./release/rtool.py upload-snapshot --bdist
fi
notifications:

View File

@ -1,10 +1,13 @@
1813 Aldo Cortesi
1228 Maximilian Hils
282 Thomas Kriechbaumer
2118 Aldo Cortesi
1666 Maximilian Hils
450 Thomas Kriechbaumer
210 Shadab Zafar
94 Jason
83 Marcelo Glezer
36 Clemens
28 Jim Shaver
18 Henrik Nordstrom
17 Shadab Zafar
16 Matthew Shao
14 David Weinstein
14 Pedro Worcel
13 Thomas Roth
@ -14,39 +17,47 @@
10 András Veres-Szentkirályi
10 Chris Czub
10 Sandor Nemes
10 Zohar Lorberbaum
9 Kyle Morton
9 Legend Tang
9 Matthew Shao
9 Rouli
9 ikoz
8 Chandler Abraham
8 Jason A. Novak
7 Alexis Hildebrandt
7 Brad Peabody
7 Matthias Urlichs
7 dufferzafar
6 Felix Yan
5 Choongwoo Han
5 Sam Cleveland
5 Tomaz Muraus
5 Will Coster
5 elitest
5 iroiro123
4 Bryan Bishop
4 Clemens Brunner
4 Marc Liyanage
4 Michael J. Bazzinotti
4 Valtteri Virtanen
4 Wade 524
4 Youhei Sakurai
4 root
4 yonder
3 Benjamin Lee
3 Chris Neasbitt
3 Eli Shvartsman
3 Felix Yan
3 Guillem Anguera
3 Kyle Manna
3 MatthewShao
3 Ryan Welton
3 Zack B
3 redfast00
3 requires.io
2 Anant
2 Bennett Blodinger
2 Colin Bendell
2 Cory Benfield
2 Heikki Hannikainen
2 Israel Nir
2 Jaime Soriano Pastor
@ -59,34 +70,50 @@
2 Paul
2 Rob Wills
2 Sean Coates
2 Steven Van Acker
2 Terry Long
2 Wade Catron
2 alts
2 isra17
2 israel
2 requires.io
2 jpkrause
2 lilydjwg
2 strohu
2 依云
1 Aditya
1 Andrey Plotnikov
1 Andy Smith
1 Anthony Zhang
1 BSalita
1 Ben Lerner
1 Bradley Baetz
1 Brett Randall
1 Chris Hamant
1 Christian Frichot
1 Dan Wilbraham
1 David Dworken
1 David Shaw
1 Doug Freed
1 Doug Lethin
1 Drake Caraker
1 Eric Entzel
1 Felix Wolfsteller
1 FreeArtMan
1 Gabriel Kirkpatrick
1 Henrik Nordström
1 Israel Blancas
1 Ivaylo Popov
1 JC
1 Jakub Nawalaniec
1 Jakub Wilk
1 James Billingham
1 Jason Pepas
1 Jean Regisser
1 Jonathan Jones
1 Jorge Villacorta
1 Kit Randel
1 Kostya Esmukov
1 Linmiao Xu
1 Lucas Cimon
1 M. Utku Altinkaya
1 Mathieu Mitchell
@ -98,27 +125,33 @@
1 Nick Raptis
1 Nicolas Esteves
1 Oleksandr Sheremet
1 Parth Ganatra
1 Pritam Baral
1 Rich Somerfield
1 Rory McCann
1 Rune Halvorsen
1 Ryo Onodera
1 Sachin Kelkar
1 Sahn Lam
1 Seppo Yli-Olli
1 Sergey Chipiga
1 Stefan Wärting
1 Steve Phillips
1 Steven Van Acker
1 Steven Noble
1 Suyash
1 Tai Dickerson
1 Tarashish Mishra
1 TearsDontFalls
1 Thiago Arrais
1 Tim Becker
1 Timothy Elliott
1 Ulrich Petri
1 Vyacheslav Bakhmutov
1 Will Coster
1 Wes Turner
1 Yoginski
1 Yuangxuan Wang
1 capt8bit
1 cle1000
1 davidpshaw
1 deployable
1 gecko655
@ -133,4 +166,3 @@
1 sethp-jive
1 starenka
1 vzvu3k6k
1 依云

View File

@ -13,7 +13,7 @@ This was tested on a fully patched installation of Ubuntu 14.04.
.. code:: bash
sudo apt-get install python-pip python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev
sudo apt-get install python-pip python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev g++
sudo pip install mitmproxy # or pip install --user mitmproxy
Once installation is complete you can run :ref:`mitmproxy` or :ref:`mitmdump` from a terminal.

View File

@ -20,7 +20,7 @@ class Addons(object):
def add(self, options, *addons):
if not addons:
raise ValueError("No adons specified.")
raise ValueError("No addons specified.")
self.chain.extend(addons)
for i in addons:
self.invoke_with_context(i, "start")

View File

@ -1,7 +1,6 @@
from __future__ import absolute_import, print_function, division
import itertools
import traceback
import click
@ -243,4 +242,4 @@ class Dumper(object):
server=repr(f.server_conn.address),
direction=direction,
))
self._echo_message(message)
self._echo_message(message)

View File

@ -61,13 +61,13 @@ def scriptenv(path, args):
try:
yield
except Exception:
_, _, tb = sys.exc_info()
etype, value, tb = sys.exc_info()
scriptdir = os.path.dirname(os.path.abspath(path))
for i, s in enumerate(reversed(traceback.extract_tb(tb))):
tb = tb.tb_next
if not os.path.abspath(s[0]).startswith(scriptdir):
break
ctx.log.error("Script error: %s" % "".join(traceback.format_tb(tb)))
ctx.log.error("Script error: %s" % "".join(traceback.format_exception(etype, value, tb)))
finally:
sys.argv = oldargs
sys.path.pop()

View File

@ -260,7 +260,7 @@ def get_common_options(args):
upstream_auth = args.upstream_auth,
ssl_version_client = args.ssl_version_client,
ssl_version_server = args.ssl_version_server,
ssl_verify_upstream_cert = args.ssl_verify_upstream_cert,
ssl_insecure = args.ssl_insecure,
ssl_verify_upstream_trusted_cadir = args.ssl_verify_upstream_trusted_cadir,
ssl_verify_upstream_trusted_ca = args.ssl_verify_upstream_trusted_ca,
tcp_hosts = args.tcp_hosts,
@ -519,10 +519,9 @@ def proxy_ssl_options(parser):
"that will be served to the proxy client, as extras."
)
group.add_argument(
"--verify-upstream-cert", default=False,
action="store_true", dest="ssl_verify_upstream_cert",
help="Verify upstream server SSL/TLS certificates and fail if invalid "
"or not present."
"--insecure", default=False,
action="store_true", dest="ssl_insecure",
help="Do not verify upstream server SSL/TLS certificates."
)
group.add_argument(
"--upstream-trusted-cadir", default=None, action="store",
@ -773,7 +772,7 @@ def mitmproxy():
help="Show event log."
)
parser.add_argument(
"-f", "--follow",
"--follow",
action="store_true", dest="follow",
help="Follow flow list."
)
@ -792,9 +791,9 @@ def mitmproxy():
help="Intercept filter expression."
)
group.add_argument(
"-l", "--limit", action="store",
type=str, dest="limit", default=None,
help="Limit filter expression."
"-f", "--filter", action="store",
type=str, dest="filter", default=None,
help="Filter view expression."
)
return parser

View File

@ -379,7 +379,7 @@ def raw_format_flow(f, focus, extended):
4: "code_400",
5: "code_500",
}
ccol = codes.get(f["resp_code"] / 100, "code_other")
ccol = codes.get(f["resp_code"] // 100, "code_other")
resp.append(fcol(SYMBOL_RETURN, ccol))
if f["resp_is_replay"]:
resp.append(fcol(SYMBOL_REPLAY, "replay"))

View File

@ -18,8 +18,8 @@ def _mkhelp():
("d", "delete flow"),
("D", "duplicate flow"),
("e", "toggle eventlog"),
("f", "filter view"),
("F", "toggle follow flow list"),
("l", "set limit filter pattern"),
("L", "load saved flows"),
("m", "toggle flow mark"),
("M", "toggle marked flow view"),
@ -367,11 +367,11 @@ class FlowListBox(urwid.ListBox):
elif key == "G":
self.master.state.set_focus(self.master.state.flow_count())
signals.flowlist_change.send(self)
elif key == "l":
elif key == "f":
signals.status_prompt.send(
prompt = "Limit",
text = self.master.state.limit_txt,
callback = self.master.set_limit
prompt = "Filter View",
text = self.master.state.filter_txt,
callback = self.master.set_view_filter
)
elif key == "L":
signals.status_prompt_path.send(

View File

@ -3,14 +3,12 @@ from __future__ import absolute_import, print_function, division
import math
import os
import sys
import traceback
import urwid
from typing import Optional, Union # noqa
from mitmproxy import contentviews
from mitmproxy import controller
from mitmproxy import exceptions
from mitmproxy import models
from mitmproxy import utils
from mitmproxy.console import common
@ -705,4 +703,4 @@ class FlowView(tabs.Tabs):
"b": "brotli",
}
conn.encode(encoding_map[key])
signals.flow_change.send(self, flow = self.flow)
signals.flow_change.send(self, flow = self.flow)

View File

@ -75,8 +75,8 @@ class ConsoleState(flow.State):
self.update_focus()
return f
def set_limit(self, limit):
ret = super(ConsoleState, self).set_limit(limit)
def set_view_filter(self, txt):
ret = super(ConsoleState, self).set_view_filter(txt)
self.set_focus(self.focus)
return ret
@ -153,8 +153,8 @@ class ConsoleState(flow.State):
last_focus, _ = self.get_focus()
nearest_marked = self.get_nearest_matching_flow(last_focus, marked_filter)
self.last_filter = self.limit_txt
self.set_limit(marked_filter)
self.last_filter = self.filter_txt
self.set_view_filter(marked_filter)
# Restore Focus
if last_focus.marked:
@ -171,7 +171,7 @@ class ConsoleState(flow.State):
last_focus, _ = self.get_focus()
nearest_marked = self.get_nearest_matching_flow(last_focus, marked_filter)
self.set_limit(self.last_filter)
self.set_view_filter(self.last_filter)
self.last_filter = ""
# Restore Focus
@ -203,7 +203,7 @@ class Options(mitmproxy.options.Options):
eventlog=False, # type: bool
follow=False, # type: bool
intercept=False, # type: bool
limit=None, # type: Optional[str]
filter=None, # type: Optional[str]
palette=None, # type: Optional[str]
palette_transparent=False, # type: bool
no_mouse=False, # type: bool
@ -212,7 +212,7 @@ class Options(mitmproxy.options.Options):
self.eventlog = eventlog
self.follow = follow
self.intercept = intercept
self.limit = limit
self.filter = filter
self.palette = palette
self.palette_transparent = palette_transparent
self.no_mouse = no_mouse
@ -234,8 +234,8 @@ class ConsoleMaster(flow.FlowMaster):
print("Intercept error: {}".format(r), file=sys.stderr)
sys.exit(1)
if options.limit:
self.set_limit(options.limit)
if options.filter:
self.set_view_filter(options.filter)
self.set_stream_large_bodies(options.stream_large_bodies)
@ -258,6 +258,7 @@ class ConsoleMaster(flow.FlowMaster):
signals.call_in.connect(self.sig_call_in)
signals.pop_view_state.connect(self.sig_pop_view_state)
signals.replace_view_state.connect(self.sig_replace_view_state)
signals.push_view_state.connect(self.sig_push_view_state)
signals.sig_add_log.connect(self.sig_add_log)
self.addons.add(options, *builtins.default_addons())
@ -276,11 +277,11 @@ class ConsoleMaster(flow.FlowMaster):
if self.options.verbosity < utils.log_tier(level):
return
if level == "error":
if level in ("error", "warn"):
signals.status_message.send(
message = "Error: %s" % str(e)
message = "{}: {}".format(level.title(), e)
)
e = urwid.Text(("error", str(e)))
e = urwid.Text((level, str(e)))
else:
e = urwid.Text(str(e))
self.logbuffer.append(e)
@ -296,7 +297,19 @@ class ConsoleMaster(flow.FlowMaster):
return callback(*args)
self.loop.set_alarm_in(seconds, cb)
def sig_replace_view_state(self, sender):
"""
A view has been pushed onto the stack, and is intended to replace
the current view rather tha creating a new stack entry.
"""
if len(self.view_stack) > 1:
del self.view_stack[1]
def sig_pop_view_state(self, sender):
"""
Pop the top view off the view stack. If no more views will be left
after this, prompt for exit.
"""
if len(self.view_stack) > 1:
self.view_stack.pop()
self.loop.widget = self.view_stack[-1]
@ -312,6 +325,9 @@ class ConsoleMaster(flow.FlowMaster):
)
def sig_push_view_state(self, sender, window):
"""
Push a new view onto the view stack.
"""
self.view_stack.append(window)
self.loop.widget = window
self.loop.draw_screen()
@ -352,8 +368,8 @@ class ConsoleMaster(flow.FlowMaster):
def toggle_eventlog(self):
self.options.eventlog = not self.options.eventlog
signals.pop_view_state.send(self)
self.view_flowlist()
signals.replace_view_state.send(self)
def _readflows(self, path):
"""
@ -656,8 +672,8 @@ class ConsoleMaster(flow.FlowMaster):
def accept_all(self):
self.state.accept_all(self)
def set_limit(self, txt):
v = self.state.set_limit(txt)
def set_view_filter(self, txt):
v = self.state.set_view_filter(txt)
signals.flowlist_change.send(self)
return v

View File

@ -91,6 +91,12 @@ class Options(urwid.WidgetWrap):
lambda: master.options.tcp_hosts,
self.tcp_hosts
),
select.Option(
"Don't Verify SSL/TLS Certificates",
"V",
lambda: master.options.ssl_insecure,
master.options.toggler("ssl_insecure")
),
select.Heading("Utility"),
select.Option(
@ -134,15 +140,17 @@ class Options(urwid.WidgetWrap):
title = urwid.Text("Options")
title = urwid.Padding(title, align="left", width=("relative", 100))
title = urwid.AttrWrap(title, "heading")
self._w = urwid.Frame(
w = urwid.Frame(
self.lb,
header = title
)
super(Options, self).__init__(w)
self.master.loop.widget.footer.update("")
signals.update_settings.connect(self.sig_update_settings)
master.options.changed.connect(lambda sender, updated: self.sig_update_settings(sender))
master.options.changed.connect(self.sig_update_settings)
def sig_update_settings(self, sender):
def sig_update_settings(self, sender, updated=None):
self.lb.walker._modified()
def keypress(self, size, key):

View File

@ -43,3 +43,4 @@ flowlist_change = blinker.Signal()
# Pop and push view state onto a stack
pop_view_state = blinker.Signal()
push_view_state = blinker.Signal()
replace_view_state = blinker.Signal()

View File

@ -124,10 +124,10 @@ class StatusBar(urwid.WidgetWrap):
super(StatusBar, self).__init__(urwid.Pile([self.ib, self.master.ab]))
signals.update_settings.connect(self.sig_update_settings)
signals.flowlist_change.connect(self.sig_update_settings)
master.options.changed.connect(lambda sender, updated: self.sig_update_settings(sender))
master.options.changed.connect(self.sig_update_settings)
self.redraw()
def sig_update_settings(self, sender):
def sig_update_settings(self, sender, updated=None):
self.redraw()
def keypress(self, *args, **kwargs):
@ -167,10 +167,10 @@ class StatusBar(urwid.WidgetWrap):
r.append("[")
r.append(("heading_key", "i"))
r.append(":%s]" % self.master.state.intercept_txt)
if self.master.state.limit_txt:
if self.master.state.filter_txt:
r.append("[")
r.append(("heading_key", "l"))
r.append(":%s]" % self.master.state.limit_txt)
r.append(("heading_key", "f"))
r.append(":%s]" % self.master.state.filter_txt)
if self.master.options.stickycookie:
r.append("[")
r.append(("heading_key", "t"))

View File

@ -668,4 +668,4 @@ def get_content_view(viewmode, data, **metadata):
traceback.format_exc()
)
return desc, safe_to_print(content), error
return desc, safe_to_print(content), error

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
# UNPACKERS SPECIFICATIONS
Nothing very difficult: an unpacker is a submodule placed in the directory
where this file was found. Each unpacker must define three symbols:
* `PRIORITY` : integer number expressing the priority in applying this
unpacker. Lower number means higher priority.
Makes sense only if a source file has been packed with
more than one packer.
* `detect(source)` : returns `True` if source is packed, otherwise, `False`.
* `unpack(source)` : takes a `source` string and unpacks it. Must always return
valid JavaScript. That is to say, your code should look
like:
```
if detect(source):
return do_your_fancy_things_with(source)
else:
return source
```
*You can safely define any other symbol in your module, as it will be ignored.*
`__init__` code will automatically load new unpackers, without any further step
to be accomplished. Simply drop it in this directory.

View File

@ -1,66 +0,0 @@
#
# General code for JSBeautifier unpackers infrastructure. See README.specs
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
"""General code for JSBeautifier unpackers infrastructure."""
import pkgutil
import re
from . import evalbased
# NOTE: AT THE MOMENT, IT IS DEACTIVATED FOR YOUR SECURITY: it runs js!
BLACKLIST = ['jsbeautifier.unpackers.evalbased']
class UnpackingError(Exception):
"""Badly packed source or general error. Argument is a
meaningful description."""
def getunpackers():
"""Scans the unpackers dir, finds unpackers and add them to UNPACKERS list.
An unpacker will be loaded only if it is a valid python module (name must
adhere to naming conventions) and it is not blacklisted (i.e. inserted
into BLACKLIST."""
path = __path__
prefix = __name__ + '.'
unpackers = []
interface = ['unpack', 'detect', 'PRIORITY']
for _importer, modname, _ispkg in pkgutil.iter_modules(path, prefix):
if 'tests' not in modname and modname not in BLACKLIST:
try:
module = __import__(modname, fromlist=interface)
except ImportError:
raise UnpackingError('Bad unpacker: %s' % modname)
else:
unpackers.append(module)
return sorted(unpackers, key = lambda mod: mod.PRIORITY)
UNPACKERS = getunpackers()
def run(source, evalcode=False):
"""Runs the applicable unpackers and return unpacked source as a string."""
for unpacker in [mod for mod in UNPACKERS if mod.detect(source)]:
source = unpacker.unpack(source)
if evalcode and evalbased.detect(source):
source = evalbased.unpack(source)
return source
def filtercomments(source):
"""NOT USED: strips trailing comments and put them at the top."""
trailing_comments = []
comment = True
while comment:
if re.search(r'^\s*\/\*', source):
comment = source[0, source.index('*/') + 2]
elif re.search(r'^\s*\/\/', source):
comment = re.search(r'^\s*\/\/', source).group(0)
else:
comment = None
if comment:
source = re.sub(r'^\s+', '', source[len(comment):])
trailing_comments.append(comment)
return '\n'.join(trailing_comments) + source

View File

@ -1,39 +0,0 @@
#
# Unpacker for eval() based packers, a part of javascript beautifier
# by Einar Lielmanis <einar@jsbeautifier.org>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
# unpacked = unpack(some_string)
#
"""Unpacker for eval() based packers: runs JS code and returns result.
Works only if a JS interpreter (e.g. Mozilla's Rhino) is installed and
properly set up on host."""
from subprocess import PIPE, Popen
PRIORITY = 3
def detect(source):
"""Detects if source is likely to be eval() packed."""
return source.strip().lower().startswith('eval(function(')
def unpack(source):
"""Runs source and return resulting code."""
return jseval('print %s;' % source[4:]) if detect(source) else source
# In case of failure, we'll just return the original, without crashing on user.
def jseval(script):
"""Run code in the JS interpreter and return output."""
try:
interpreter = Popen(['js'], stdin=PIPE, stdout=PIPE)
except OSError:
return script
result, errors = interpreter.communicate(script)
if interpreter.poll() or errors:
return script
return result

View File

@ -1,58 +0,0 @@
#
# simple unpacker/deobfuscator for scripts messed up with
# javascriptobfuscator.com
#
# written by Einar Lielmanis <einar@jsbeautifier.org>
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# Will always return valid javascript: if `detect()` is false, `code` is
# returned, unmodified.
#
# usage:
#
# if javascriptobfuscator.detect(some_string):
# some_string = javascriptobfuscator.unpack(some_string)
#
"""deobfuscator for scripts messed up with JavascriptObfuscator.com"""
import re
PRIORITY = 1
def smartsplit(code):
"""Split `code` at " symbol, only if it is not escaped."""
strings = []
pos = 0
while pos < len(code):
if code[pos] == '"':
word = '' # new word
pos += 1
while pos < len(code):
if code[pos] == '"':
break
if code[pos] == '\\':
word += '\\'
pos += 1
word += code[pos]
pos += 1
strings.append('"%s"' % word)
pos += 1
return strings
def detect(code):
"""Detects if `code` is JavascriptObfuscator.com packed."""
# prefer `is not` idiom, so that a true boolean is returned
return (re.search(r'^var _0x[a-f0-9]+ ?\= ?\[', code) is not None)
def unpack(code):
"""Unpacks JavascriptObfuscator.com packed code."""
if detect(code):
matches = re.search(r'var (_0x[a-f\d]+) ?\= ?\[(.*?)\];', code)
if matches:
variable = matches.group(1)
dictionary = smartsplit(matches.group(2))
code = code[len(matches.group(0)):]
for key, value in enumerate(dictionary):
code = code.replace(r'%s[%s]' % (variable, key), value)
return code

View File

@ -1,86 +0,0 @@
#
# deobfuscator for scripts messed up with myobfuscate.com
# by Einar Lielmanis <einar@jsbeautifier.org>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
# unpacked = unpack(some_string)
#
# CAVEAT by Einar Lielmanis
#
# You really don't want to obfuscate your scripts there: they're tracking
# your unpackings, your script gets turned into something like this,
# as of 2011-08-26:
#
# var _escape = 'your_script_escaped';
# var _111 = document.createElement('script');
# _111.src = 'http://api.www.myobfuscate.com/?getsrc=ok' +
# '&ref=' + encodeURIComponent(document.referrer) +
# '&url=' + encodeURIComponent(document.URL);
# var 000 = document.getElementsByTagName('head')[0];
# 000.appendChild(_111);
# document.write(unescape(_escape));
#
"""Deobfuscator for scripts messed up with MyObfuscate.com"""
import re
import base64
# Python 2 retrocompatibility
# pylint: disable=F0401
# pylint: disable=E0611
try:
from urllib import unquote
except ImportError:
from urllib.parse import unquote
from . import UnpackingError
PRIORITY = 1
CAVEAT = """//
// Unpacker warning: be careful when using myobfuscate.com for your projects:
// scripts obfuscated by the free online version call back home.
//
"""
SIGNATURE = (r'["\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F'
r'\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5A\x61\x62\x63\x64\x65'
r'\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75'
r'\x76\x77\x78\x79\x7A\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2B'
r'\x2F\x3D","","\x63\x68\x61\x72\x41\x74","\x69\x6E\x64\x65\x78'
r'\x4F\x66","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","'
r'\x6C\x65\x6E\x67\x74\x68"]')
def detect(source):
"""Detects MyObfuscate.com packer."""
return SIGNATURE in source
def unpack(source):
"""Unpacks js code packed with MyObfuscate.com"""
if not detect(source):
return source
payload = unquote(_filter(source))
match = re.search(r"^var _escape\='<script>(.*)<\/script>'",
payload, re.DOTALL)
polished = match.group(1) if match else source
return CAVEAT + polished
def _filter(source):
"""Extracts and decode payload (original file) from `source`"""
try:
varname = re.search(r'eval\(\w+\(\w+\((\w+)\)\)\);', source).group(1)
reverse = re.search(r"var +%s *\= *'(.*)';" % varname, source).group(1)
except AttributeError:
raise UnpackingError('Malformed MyObfuscate data.')
try:
return base64.b64decode(reverse[::-1].encode('utf8')).decode('utf8')
except TypeError:
raise UnpackingError('MyObfuscate payload is not base64-encoded.')

View File

@ -1,103 +0,0 @@
#
# Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier
# by Einar Lielmanis <einar@jsbeautifier.org>
#
# written by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# usage:
#
# if detect(some_string):
# unpacked = unpack(some_string)
#
"""Unpacker for Dean Edward's p.a.c.k.e.r"""
import re
from . import UnpackingError
PRIORITY = 1
def detect(source):
"""Detects whether `source` is P.A.C.K.E.R. coded."""
return source.replace(' ', '').startswith('eval(function(p,a,c,k,e,r')
def unpack(source):
"""Unpacks P.A.C.K.E.R. packed js code."""
payload, symtab, radix, count = _filterargs(source)
if count != len(symtab):
raise UnpackingError('Malformed p.a.c.k.e.r. symtab.')
try:
unbase = Unbaser(radix)
except TypeError:
raise UnpackingError('Unknown p.a.c.k.e.r. encoding.')
def lookup(match):
"""Look up symbols in the synthetic symtab."""
word = match.group(0)
return symtab[unbase(word)] or word
source = re.sub(r'\b\w+\b', lookup, payload)
return _replacestrings(source)
def _filterargs(source):
"""Juice from a source file the four args needed by decoder."""
argsregex = (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\."
r"split\('\|'\), *(\d+), *(.*)\)\)")
args = re.search(argsregex, source, re.DOTALL).groups()
try:
return args[0], args[3].split('|'), int(args[1]), int(args[2])
except ValueError:
raise UnpackingError('Corrupted p.a.c.k.e.r. data.')
def _replacestrings(source):
"""Strip string lookup table (list) and replace values in source."""
match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL)
if match:
varname, strings = match.groups()
startpoint = len(match.group(0))
lookup = strings.split('","')
variable = '%s[%%d]' % varname
for index, value in enumerate(lookup):
source = source.replace(variable % index, '"%s"' % value)
return source[startpoint:]
return source
class Unbaser(object):
"""Functor for a given base. Will efficiently convert
strings to natural numbers."""
ALPHABET = {
62 : '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
95 : (' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
}
def __init__(self, base):
self.base = base
# If base can be handled by int() builtin, let it do it for us
if 2 <= base <= 36:
self.unbase = lambda string: int(string, base)
else:
# Build conversion dictionary cache
try:
self.dictionary = dict((cipher, index) for
index, cipher in enumerate(self.ALPHABET[base]))
except KeyError:
raise TypeError('Unsupported base encoding.')
self.unbase = self._dictunbaser
def __call__(self, string):
return self.unbase(string)
def _dictunbaser(self, string):
"""Decodes a value to an integer."""
ret = 0
for index, cipher in enumerate(string[::-1]):
ret += (self.base ** index) * self.dictionary[cipher]
return ret

View File

@ -1,34 +0,0 @@
#
# Trivial bookmarklet/escaped script detector for the javascript beautifier
# written by Einar Lielmanis <einar@jsbeautifier.org>
# rewritten in Python by Stefano Sanfilippo <a.little.coder@gmail.com>
#
# Will always return valid javascript: if `detect()` is false, `code` is
# returned, unmodified.
#
# usage:
#
# some_string = urlencode.unpack(some_string)
#
"""Bookmarklet/escaped script unpacker."""
# Python 2 retrocompatibility
# pylint: disable=F0401
# pylint: disable=E0611
try:
from urllib import unquote_plus
except ImportError:
from urllib.parse import unquote_plus
PRIORITY = 0
def detect(code):
"""Detects if a scriptlet is urlencoded."""
# the fact that script doesn't contain any space, but has %20 instead
# should be sufficient check for now.
return ' ' not in code and ('%20' in code or code.count('%') > 3)
def unpack(code):
"""URL decode `code` source string."""
return unquote_plus(code) if detect(code) else code

View File

@ -104,7 +104,7 @@ class DumpMaster(flow.FlowMaster):
click.secho(
e,
file=self.options.tfile,
fg="red" if level == "error" else None,
fg=dict(error="red", warn="yellow").get(level),
dim=(level == "debug"),
err=(level == "error")
)
@ -118,5 +118,6 @@ class DumpMaster(flow.FlowMaster):
def run(self): # pragma: no cover
if self.options.rfile and not self.options.keepserving:
self.addons.done()
return
super(DumpMaster, self).run()

View File

@ -44,6 +44,12 @@ class ClientHandshakeException(TlsProtocolException):
self.server = server
class InvalidServerCertificate(TlsProtocolException):
def __repr__(self):
# In contrast to most others, this is a user-facing error which needs to look good.
return str(self)
class Socks5ProtocolException(ProtocolException):
pass

View File

@ -233,6 +233,7 @@ class FlowMaster(controller.Master):
if self.server_playback:
pb = self.do_server_playback(f)
if not pb and self.kill_nonreplay:
self.add_log("Killed {}".format(f.request.url), "info")
f.kill(self)
def replay_request(self, f, block=False):

View File

@ -191,7 +191,7 @@ class State(object):
self.intercept = None
@property
def limit_txt(self):
def filter_txt(self):
return getattr(self.view.filt, "pattern", None)
def flow_count(self):
@ -225,8 +225,8 @@ class State(object):
def load_flows(self, flows):
self.flows._extend(flows)
def set_limit(self, txt):
if txt == self.limit_txt:
def set_view_filter(self, txt):
if txt == self.filter_txt:
return
if txt:
f = filt.parse(txt)

View File

@ -68,7 +68,7 @@ def mitmproxy(args=None): # pragma: no cover
console_options.eventlog = args.eventlog
console_options.follow = args.follow
console_options.intercept = args.intercept
console_options.limit = args.limit
console_options.filter = args.filter
console_options.no_mouse = args.no_mouse
server = process_options(parser, console_options, args)
@ -92,6 +92,7 @@ def mitmdump(args=None): # pragma: no cover
if args.quiet:
args.flow_detail = 0
master = None
try:
dump_options = dump.Options(**cmdline.get_common_options(args))
dump_options.flow_detail = args.flow_detail
@ -110,7 +111,7 @@ def mitmdump(args=None): # pragma: no cover
sys.exit(1)
except (KeyboardInterrupt, _thread.error):
pass
if master.has_errored:
if master is None or master.has_errored:
print("mitmdump: errors occurred during run", file=sys.stderr)
sys.exit(1)

View File

@ -225,7 +225,7 @@ class HTTPFlow(Flow):
def make_error_response(status_code, message, headers=None):
response = status_codes.RESPONSES.get(status_code, "Unknown").encode()
response = status_codes.RESPONSES.get(status_code, "Unknown")
body = """
<html>
<head>

View File

@ -48,6 +48,7 @@ class PEM(tornado.web.RequestHandler):
def get(self):
p = os.path.join(self.request.master.options.cadir, self.filename)
p = os.path.expanduser(p)
self.set_header("Content-Type", "application/x-x509-ca-cert")
self.set_header(
"Content-Disposition",

View File

@ -73,7 +73,7 @@ class Options(optmanager.OptManager):
upstream_auth = "", # type: str
ssl_version_client="secure", # type: str
ssl_version_server="secure", # type: str
ssl_verify_upstream_cert=False, # type: bool
ssl_insecure=False, # type: bool
ssl_verify_upstream_trusted_cadir=None, # type: str
ssl_verify_upstream_trusted_ca=None, # type: str
tcp_hosts = (), # type: Sequence[str]
@ -130,7 +130,7 @@ class Options(optmanager.OptManager):
self.upstream_auth = upstream_auth
self.ssl_version_client = ssl_version_client
self.ssl_version_server = ssl_version_server
self.ssl_verify_upstream_cert = ssl_verify_upstream_cert
self.ssl_insecure = ssl_insecure
self.ssl_verify_upstream_trusted_cadir = ssl_verify_upstream_trusted_cadir
self.ssl_verify_upstream_trusted_ca = ssl_verify_upstream_trusted_ca
self.tcp_hosts = tcp_hosts

View File

@ -86,7 +86,10 @@ class OptManager(object):
"""
if attr not in self._opts:
raise KeyError("No such option: %s" % attr)
return lambda x: self.__setattr__(attr, x)
def setter(x):
setattr(self, attr, x)
return setter
def toggler(self, attr):
"""
@ -95,7 +98,10 @@ class OptManager(object):
"""
if attr not in self._opts:
raise KeyError("No such option: %s" % attr)
return lambda: self.__setattr__(attr, not getattr(self, attr))
def toggle():
setattr(self, attr, not getattr(self, attr))
return toggle
def __repr__(self):
options = pprint.pformat(self._opts, indent=4).strip(" {}")

View File

@ -1,8 +1,9 @@
import sys
import re
resolver = None
if sys.platform == "linux2":
if re.match(r"linux(?:2)?", sys.platform):
from . import linux
resolver = linux.Resolver
elif sys.platform == "darwin":

View File

@ -543,25 +543,12 @@ class TlsLayer(base.Layer):
)
tls_cert_err = self.server_conn.ssl_verification_error
if tls_cert_err is not None:
self.log(
"TLS verification failed for upstream server at depth %s with error: %s" %
(tls_cert_err['depth'], tls_cert_err['errno']),
"error")
self.log("Ignoring server verification error, continuing with connection", "error")
self.log(str(tls_cert_err), "warn")
self.log("Ignoring server verification error, continuing with connection", "warn")
except netlib.exceptions.InvalidCertificateException as e:
tls_cert_err = self.server_conn.ssl_verification_error
self.log(
"TLS verification failed for upstream server at depth %s with error: %s" %
(tls_cert_err['depth'], tls_cert_err['errno']),
"error")
self.log("Aborting connection attempt", "error")
six.reraise(
exceptions.TlsProtocolException,
exceptions.TlsProtocolException("Cannot establish TLS with {address} (sni: {sni}): {e}".format(
address=repr(self.server_conn.address),
sni=self.server_sni,
e=repr(e),
)),
exceptions.InvalidServerCertificate,
exceptions.InvalidServerCertificate(str(e)),
sys.exc_info()[2]
)
except netlib.exceptions.TlsException as e:

View File

@ -83,24 +83,18 @@ class ProxyConfig:
options.changed.connect(self.configure)
def configure(self, options, updated):
conflict = all(
[
options.add_upstream_certs_to_client_chain,
options.ssl_verify_upstream_cert
]
)
if conflict:
# type: (mitmproxy.options.Options, Any) -> None
if options.add_upstream_certs_to_client_chain and not options.ssl_insecure:
raise exceptions.OptionsError(
"The verify-upstream-cert and add-upstream-certs-to-client-chain "
"options are mutually exclusive. If upstream certificates are verified "
"then extra upstream certificates are not available for inclusion "
"to the client chain."
"The verify-upstream-cert requires certificate verification to be disabled. "
"If upstream certificates are verified then extra upstream certificates are "
"not available for inclusion to the client chain."
)
if options.ssl_verify_upstream_cert:
self.openssl_verification_mode_server = SSL.VERIFY_PEER
else:
if options.ssl_insecure:
self.openssl_verification_mode_server = SSL.VERIFY_NONE
else:
self.openssl_verification_mode_server = SSL.VERIFY_PEER
self.check_ignore = HostMatcher(options.ignore_hosts)
self.check_tcp = HostMatcher(options.tcp_hosts)

View File

@ -125,11 +125,14 @@ class ConnectionHandler(object):
self.log(
"Client Handshake failed. "
"The client may not trust the proxy's certificate for {}.".format(e.server),
"error"
"warn"
)
self.log(repr(e), "debug")
elif isinstance(e, exceptions.InvalidServerCertificate):
self.log(str(e), "warn")
self.log("Invalid certificate, closing connection. Pass --insecure to disable validation.", "warn")
else:
self.log(repr(e), "info")
self.log(repr(e), "warn")
self.log(traceback.format_exc(), "debug")
# If an error propagates to the topmost level,

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

View File

@ -6,10 +6,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/vendor.css"/>
<link rel="stylesheet" href="/static/app.css"/>
<link rel="icon" href="/static/images/favicon.ico" type="image/x-icon"/>
<script src="/static/vendor.js"></script>
<script src="/static/app.js"></script>
</head>
<body>
<div id="mitmproxy"></div>
</body>
</html>
</html>

View File

@ -8,6 +8,7 @@ import collections
from io import BytesIO
import gzip
import zlib
import brotli
from typing import Union # noqa
@ -45,7 +46,7 @@ def decode(encoded, encoding, errors='strict'):
decoded = custom_decode[encoding](encoded)
except KeyError:
decoded = codecs.decode(encoded, encoding, errors)
if encoding in ("gzip", "deflate"):
if encoding in ("gzip", "deflate", "br"):
_cache = CachedDecode(encoded, encoding, errors, decoded)
return decoded
except Exception as e:
@ -81,7 +82,7 @@ def encode(decoded, encoding, errors='strict'):
encoded = custom_encode[encoding](decoded)
except KeyError:
encoded = codecs.encode(decoded, encoding, errors)
if encoding in ("gzip", "deflate"):
if encoding in ("gzip", "deflate", "br"):
_cache = CachedDecode(encoded, encoding, errors, decoded)
return encoded
except Exception as e:
@ -113,6 +114,14 @@ def encode_gzip(content):
return s.getvalue()
def decode_brotli(content):
return brotli.decompress(content)
def encode_brotli(content):
return brotli.compress(content)
def decode_deflate(content):
"""
Returns decompressed data for DEFLATE. Some servers may respond with
@ -139,11 +148,13 @@ custom_decode = {
"identity": identity,
"gzip": decode_gzip,
"deflate": decode_deflate,
"br": decode_brotli,
}
custom_encode = {
"identity": identity,
"gzip": encode_gzip,
"deflate": encode_deflate,
"br": encode_brotli,
}
__all__ = ["encode", "decode"]

View File

@ -9,7 +9,8 @@ from netlib.http import http1, http2, status_codes, multipart
__all__ = [
"Request",
"Response",
"Message",
"Headers", "parse_content_type",
"decoded",
"http1", "http2", "status_codes", "multipart",
]
]

View File

@ -248,7 +248,7 @@ class Message(basetypes.Serializable):
def encode(self, e):
"""
Encodes body with the encoding e, where e is "gzip", "deflate" or "identity".
Encodes body with the encoding e, where e is "gzip", "deflate", "identity", or "br".
Any existing content-encodings are overwritten,
the content is not decoded beforehand.

View File

@ -337,7 +337,7 @@ class Request(message.Message):
self.headers["accept-encoding"] = (
', '.join(
e
for e in {"gzip", "identity", "deflate"}
for e in {"gzip", "identity", "deflate", "br"}
if e in accept_encoding
)
)

View File

@ -69,7 +69,7 @@ def escape_control_characters(text, keep_spacing=True):
return text.translate(trans)
def bytes_to_escaped_str(data, keep_spacing=False):
def bytes_to_escaped_str(data, keep_spacing=False, escape_single_quotes=False):
"""
Take bytes and return a safe string that can be displayed to the user.
@ -86,6 +86,8 @@ def bytes_to_escaped_str(data, keep_spacing=False):
# We always insert a double-quote here so that we get a single-quoted string back
# https://stackoverflow.com/questions/29019340/why-does-python-use-different-quotes-for-representing-strings-depending-on-their
ret = repr(b'"' + data).lstrip("b")[2:-1]
if not escape_single_quotes:
ret = re.sub(r"(?<!\\)(\\\\)*\\'", lambda m: (m.group(1) or "") + "'", ret)
if keep_spacing:
ret = re.sub(
r"(?<!\\)(\\\\)*\\([nrt])",

View File

@ -8,6 +8,10 @@ import time
import traceback
import binascii
from typing import Optional # noqa
from netlib import strutils
from six.moves import range
import certifi
@ -35,7 +39,7 @@ EINTR = 4
if os.environ.get("NO_ALPN"):
HAS_ALPN = False
else:
HAS_ALPN = OpenSSL._util.lib.Cryptography_HAS_ALPN
HAS_ALPN = SSL._lib.Cryptography_HAS_ALPN
# To enable all SSL methods use: SSLv23
# then add options to disable certain methods
@ -287,16 +291,7 @@ class Reader(_FileLike):
raise exceptions.TcpException(repr(e))
elif isinstance(self.o, SSL.Connection):
try:
if tuple(int(x) for x in OpenSSL.__version__.split(".")[:2]) > (0, 15):
return self.o.recv(length, socket.MSG_PEEK)
else:
# TODO: remove once a new version is released
# Polyfill for pyOpenSSL <= 0.15.1
# Taken from https://github.com/pyca/pyopenssl/commit/1d95dea7fea03c7c0df345a5ea30c12d8a0378d2
buf = SSL._ffi.new("char[]", length)
result = SSL._lib.SSL_peek(self.o._ssl, buf, length)
self.o._raise_ssl_error(self.o._ssl, result)
return SSL._ffi.buffer(buf, result)[:]
return self.o.recv(length, socket.MSG_PEEK)
except SSL.Error as e:
six.reraise(exceptions.TlsException, exceptions.TlsException(str(e)), sys.exc_info()[2])
else:
@ -511,6 +506,7 @@ class _Connection(object):
alpn_protos=None,
alpn_select=None,
alpn_select_callback=None,
sni=None,
):
"""
Creates an SSL Context.
@ -532,8 +528,14 @@ class _Connection(object):
if verify_options is not None:
def verify_cert(conn, x509, errno, err_depth, is_cert_verified):
if not is_cert_verified:
self.ssl_verification_error = dict(errno=errno,
depth=err_depth)
self.ssl_verification_error = exceptions.InvalidCertificateException(
"Certificate Verification Error for {}: {} (errno: {}, depth: {})".format(
sni,
strutils.native(SSL._ffi.string(SSL._lib.X509_verify_cert_error_string(errno)), "utf8"),
errno,
err_depth
)
)
return is_cert_verified
context.set_verify(verify_options, verify_cert)
@ -609,7 +611,7 @@ class TCPClient(_Connection):
self.source_address = source_address
self.cert = None
self.server_certs = []
self.ssl_verification_error = None
self.ssl_verification_error = None # type: Optional[exceptions.InvalidCertificateException]
self.sni = None
@property
@ -671,6 +673,7 @@ class TCPClient(_Connection):
context = self.create_ssl_context(
alpn_protos=alpn_protos,
sni=sni,
**sslctx_kwargs
)
self.connection = SSL.Connection(context, self.connection)
@ -682,14 +685,14 @@ class TCPClient(_Connection):
self.connection.do_handshake()
except SSL.Error as v:
if self.ssl_verification_error:
raise exceptions.InvalidCertificateException("SSL handshake error: %s" % repr(v))
raise self.ssl_verification_error
else:
raise exceptions.TlsException("SSL handshake error: %s" % repr(v))
else:
# Fix for pre v1.0 OpenSSL, which doesn't throw an exception on
# certificate validation failure
if verification_mode == SSL.VERIFY_PEER and self.ssl_verification_error is not None:
raise exceptions.InvalidCertificateException("SSL handshake error: certificate verify failed")
if verification_mode == SSL.VERIFY_PEER and self.ssl_verification_error:
raise self.ssl_verification_error
self.cert = certutils.SSLCert(self.connection.get_peer_certificate())
@ -710,9 +713,14 @@ class TCPClient(_Connection):
hostname = "no-hostname"
ssl_match_hostname.match_hostname(crt, hostname)
except (ValueError, ssl_match_hostname.CertificateError) as e:
self.ssl_verification_error = dict(depth=0, errno="Invalid Hostname")
self.ssl_verification_error = exceptions.InvalidCertificateException(
"Certificate Verification Error for {}: {}".format(
sni or repr(self.address),
str(e)
)
)
if verification_mode == SSL.VERIFY_PEER:
raise exceptions.InvalidCertificateException("Presented certificate for {} is not valid: {}".format(sni, str(e)))
raise self.ssl_verification_error
self.ssl_established = True
self.rfile.set_descriptor(self.connection)

View File

@ -136,7 +136,7 @@ class TokValueLiteral(_TokValueLiteral):
def spec(self):
inner = strutils.bytes_to_escaped_str(self.val)
inner = inner.replace(r"\'", r"\x27")
inner = inner.replace(r"'", r"\x27")
return "'" + inner + "'"
@ -148,7 +148,7 @@ class TokValueNakedLiteral(_TokValueLiteral):
return e.setParseAction(lambda x: cls(*x))
def spec(self):
return strutils.bytes_to_escaped_str(self.val)
return strutils.bytes_to_escaped_str(self.val, escape_single_quotes=True)
class TokValueGenerate(Token):
@ -166,7 +166,7 @@ class TokValueGenerate(Token):
def freeze(self, settings):
g = self.get_generator(settings)
return TokValueLiteral(strutils.bytes_to_escaped_str(g[:]))
return TokValueLiteral(strutils.bytes_to_escaped_str(g[:], escape_single_quotes=True))
@classmethod
def expr(cls):
@ -578,4 +578,4 @@ class NestedMessage(Token):
def freeze(self, settings):
f = self.parsed.freeze(settings).spec()
return self.__class__(TokValueLiteral(strutils.bytes_to_escaped_str(f.encode())))
return self.__class__(TokValueLiteral(strutils.bytes_to_escaped_str(f.encode(), escape_single_quotes=True)))

View File

@ -444,7 +444,7 @@ class Pathoc(tcp.TCPClient):
finally:
if resp:
lg("<< %s %s: %s bytes" % (
resp.status_code, strutils.bytes_to_escaped_str(resp.reason.encode()), len(resp.content)
resp.status_code, strutils.escape_control_characters(resp.reason) if resp.reason else "", len(resp.content)
))
if resp.status_code in self.ignorecodes:
lg.suppress()

View File

@ -1,29 +1,30 @@
#!/usr/bin/env python
#!/usr/bin/env python3
from __future__ import absolute_import, print_function, division
from os.path import join
import contextlib
import fnmatch
import os
import platform
import runpy
import shlex
import shutil
import subprocess
import re
import shlex
import runpy
import zipfile
import sys
import tarfile
import platform
import zipfile
from os.path import join, abspath, normpath, dirname, exists, basename
import click
import pysftp
import fnmatch
# https://virtualenv.pypa.io/en/latest/userguide.html#windows-notes
# scripts and executables on Windows go in ENV\Scripts\ instead of ENV/bin/
import sys
if platform.system() == "Windows":
VENV_BIN = "Scripts"
else:
VENV_BIN = "bin"
# ZipFile and tarfile have slightly different APIs. Fix that.
if platform.system() == "Windows":
def Archive(name):
a = zipfile.ZipFile(name, "w")
@ -33,13 +34,13 @@ else:
def Archive(name):
return tarfile.open(name, "w:gz")
RELEASE_DIR = join(os.path.dirname(os.path.realpath(__file__)))
DIST_DIR = join(RELEASE_DIR, "dist")
ROOT_DIR = os.path.normpath(join(RELEASE_DIR, ".."))
RELEASE_SPEC_DIR = join(RELEASE_DIR, "specs")
VERSION_FILE = join(ROOT_DIR, "netlib/version.py")
ROOT_DIR = abspath(join(dirname(__file__), ".."))
RELEASE_DIR = join(ROOT_DIR, "release")
BUILD_DIR = join(RELEASE_DIR, "build")
DIST_DIR = join(RELEASE_DIR, "dist")
PYINSTALLER_SPEC = join(RELEASE_DIR, "specs")
PYINSTALLER_TEMP = join(BUILD_DIR, "pyinstaller")
PYINSTALLER_DIST = join(BUILD_DIR, "binaries")
@ -47,27 +48,35 @@ VENV_DIR = join(BUILD_DIR, "venv")
VENV_PIP = join(VENV_DIR, VENV_BIN, "pip")
VENV_PYINSTALLER = join(VENV_DIR, VENV_BIN, "pyinstaller")
project = {
"name": "mitmproxy",
"tools": ["pathod", "pathoc", "mitmproxy", "mitmdump", "mitmweb"],
"bdists": {
"mitmproxy": ["mitmproxy", "mitmdump", "mitmweb"],
"pathod": ["pathoc", "pathod"]
},
"dir": ROOT_DIR,
"python_version": "py2.py3",
# Project Configuration
VERSION_FILE = join(ROOT_DIR, "netlib", "version.py")
PROJECT_NAME = "mitmproxy"
PYTHON_VERSION = "py2.py3"
BDISTS = {
"mitmproxy": ["mitmproxy", "mitmdump", "mitmweb"],
"pathod": ["pathoc", "pathod"]
}
if platform.system() == "Windows":
project["tools"].remove("mitmproxy")
project["bdists"]["mitmproxy"].remove("mitmproxy")
BDISTS["mitmproxy"].remove("mitmproxy")
TOOLS = [
tool
for tools in BDISTS.values()
for tool in tools
]
def get_version():
def get_version() -> str:
return runpy.run_path(VERSION_FILE)["VERSION"]
def get_snapshot_version():
last_tag, tag_dist, commit = git("describe --tags --long").strip().rsplit(b"-", 2)
def git(args: str) -> str:
with chdir(ROOT_DIR):
return subprocess.check_output(["git"] + shlex.split(args)).decode()
def get_snapshot_version() -> str:
last_tag, tag_dist, commit = git("describe --tags --long").strip().rsplit("-", 2)
tag_dist = int(tag_dist)
if tag_dist == 0:
return get_version()
@ -76,11 +85,11 @@ def get_snapshot_version():
return "{version}dev{tag_dist:04}-0x{commit}".format(
version=get_version(), # this should already be the next version
tag_dist=tag_dist,
commit=commit.decode()
commit=commit
)
def archive_name(project):
def archive_name(bdist: str) -> str:
platform_tag = {
"Darwin": "osx",
"Windows": "win32",
@ -91,18 +100,18 @@ def archive_name(project):
else:
ext = "tar.gz"
return "{project}-{version}-{platform}.{ext}".format(
project=project,
project=bdist,
version=get_version(),
platform=platform_tag,
ext=ext
)
def wheel_name():
def wheel_name() -> str:
return "{project}-{version}-{py_version}-none-any.whl".format(
project=project["name"],
project=PROJECT_NAME,
version=get_version(),
py_version=project["python_version"]
py_version=PYTHON_VERSION
)
@ -119,18 +128,13 @@ def empty_pythonpath():
@contextlib.contextmanager
def chdir(path):
def chdir(path: str):
old_dir = os.getcwd()
os.chdir(path)
yield
os.chdir(old_dir)
def git(args):
with chdir(ROOT_DIR):
return subprocess.check_output(["git"] + shlex.split(args))
@click.group(chain=True)
def cli():
"""
@ -147,95 +151,79 @@ def contributors():
with chdir(ROOT_DIR):
print("Updating CONTRIBUTORS...")
contributors_data = git("shortlog -n -s")
with open("CONTRIBUTORS", "w") as f:
f.write(contributors_data)
with open("CONTRIBUTORS", "wb") as f:
f.write(contributors_data.encode())
@cli.command("set-version")
@click.argument('version')
def set_version(version):
@cli.command("wheel")
def make_wheel():
"""
Update version information
"""
print("Update versions...")
version = ", ".join(version.split("."))
print("Update %s..." % VERSION_FILE)
with open(VERSION_FILE, "rb") as f:
content = f.read()
new_content = re.sub(
r"IVERSION\s*=\s*\([\d,\s]+\)", "IVERSION = (%s)" % version,
content
)
with open(VERSION_FILE, "wb") as f:
f.write(new_content)
@cli.command("wheels")
def wheels():
"""
Build wheels
Build wheel
"""
with empty_pythonpath():
print("Building release...")
if os.path.exists(DIST_DIR):
if exists(DIST_DIR):
shutil.rmtree(DIST_DIR)
print("Creating wheel for %s ..." % project["name"])
print("Creating wheel...")
subprocess.check_call(
[
"python", "./setup.py", "-q",
"bdist_wheel", "--dist-dir", DIST_DIR, "--universal"
],
cwd=project["dir"]
cwd=ROOT_DIR
)
print("Creating virtualenv for test install...")
if os.path.exists(VENV_DIR):
if exists(VENV_DIR):
shutil.rmtree(VENV_DIR)
subprocess.check_call(["virtualenv", "-q", VENV_DIR])
with chdir(DIST_DIR):
print("Installing %s..." % project["name"])
print("Install wheel into virtualenv...")
# lxml...
if platform.system() == "Windows" and sys.version_info[0] == 3:
subprocess.check_call([VENV_PIP, "install", "-q", "https://snapshots.mitmproxy.org/misc/lxml-3.6.0-cp35-cp35m-win32.whl"])
subprocess.check_call(
[VENV_PIP, "install", "-q", "https://snapshots.mitmproxy.org/misc/lxml-3.6.0-cp35-cp35m-win32.whl"]
)
subprocess.check_call([VENV_PIP, "install", "-q", wheel_name()])
print("Running binaries...")
for tool in project["tools"]:
print("Running tools...")
for tool in TOOLS:
tool = join(VENV_DIR, VENV_BIN, tool)
print("> %s --version" % tool)
print(subprocess.check_output([tool, "--version"]))
print(subprocess.check_output([tool, "--version"]).decode())
print("Virtualenv available for further testing:")
print("source %s" % os.path.normpath(join(VENV_DIR, VENV_BIN, "activate")))
print("source %s" % normpath(join(VENV_DIR, VENV_BIN, "activate")))
@cli.command("bdist")
@click.option("--use-existing-wheels/--no-use-existing-wheels", default=False)
@click.option("--use-existing-wheel/--no-use-existing-wheel", default=False)
@click.argument("pyinstaller_version", envvar="PYINSTALLER_VERSION", default="PyInstaller~=3.1.1")
@click.argument("setuptools_version", envvar="SETUPTOOLS_VERSION", default="setuptools>=25.1.0,!=25.1.1")
@click.pass_context
def bdist(ctx, use_existing_wheels, pyinstaller_version):
def make_bdist(ctx, use_existing_wheel, pyinstaller_version, setuptools_version):
"""
Build a binary distribution
"""
if os.path.exists(PYINSTALLER_TEMP):
if exists(PYINSTALLER_TEMP):
shutil.rmtree(PYINSTALLER_TEMP)
if os.path.exists(PYINSTALLER_DIST):
if exists(PYINSTALLER_DIST):
shutil.rmtree(PYINSTALLER_DIST)
if not use_existing_wheels:
ctx.invoke(wheels)
if not use_existing_wheel:
ctx.invoke(make_wheel)
print("Installing PyInstaller...")
subprocess.check_call([VENV_PIP, "install", "-q", pyinstaller_version])
print("Installing PyInstaller and setuptools...")
subprocess.check_call([VENV_PIP, "install", "-q", pyinstaller_version, setuptools_version])
print(subprocess.check_output([VENV_PIP, "freeze"]).decode())
for bdist_project, tools in project["bdists"].items():
with Archive(join(DIST_DIR, archive_name(bdist_project))) as archive:
for bdist, tools in BDISTS.items():
with Archive(join(DIST_DIR, archive_name(bdist))) as archive:
for tool in tools:
# This is PyInstaller, so it messes up paths.
# We need to make sure that we are in the spec folder.
with chdir(RELEASE_SPEC_DIR):
with chdir(PYINSTALLER_SPEC):
print("Building %s binary..." % tool)
subprocess.check_call(
[
@ -255,10 +243,10 @@ def bdist(ctx, use_existing_wheels, pyinstaller_version):
if platform.system() == "Windows":
executable += ".exe"
print("> %s --version" % executable)
subprocess.check_call([executable, "--version"])
print(subprocess.check_output([executable, "--version"]).decode())
archive.add(executable, os.path.basename(executable))
print("Packed {}.".format(archive_name(bdist_project)))
archive.add(executable, basename(executable))
print("Packed {}.".format(archive_name(bdist)))
@cli.command("upload-release")
@ -298,90 +286,50 @@ def upload_snapshot(host, port, user, private_key, private_key_password, wheel,
username=user,
private_key=private_key,
private_key_pass=private_key_password) as sftp:
dir_name = "snapshots/v{}".format(get_version())
sftp.makedirs(dir_name)
with sftp.cd(dir_name):
files = []
if wheel:
files.append(wheel_name())
for bdist in project["bdists"].keys():
dir_name = "snapshots/v{}".format(get_version())
sftp.makedirs(dir_name)
with sftp.cd(dir_name):
files = []
if wheel:
files.append(wheel_name())
if bdist:
for bdist in BDISTS.keys():
files.append(archive_name(bdist))
for f in files:
local_path = join(DIST_DIR, f)
remote_filename = f.replace(get_version(), get_snapshot_version())
symlink_path = "../{}".format(f.replace(get_version(), "latest"))
for f in files:
local_path = join(DIST_DIR, f)
remote_filename = f.replace(get_version(), get_snapshot_version())
symlink_path = "../{}".format(f.replace(get_version(), "latest"))
# Delete old versions
old_version = f.replace(get_version(), "*")
for f_old in sftp.listdir():
if fnmatch.fnmatch(f_old, old_version):
print("Removing {}...".format(f_old))
sftp.remove(f_old)
# Upload new version
print("Uploading {} as {}...".format(f, remote_filename))
with click.progressbar(length=os.stat(local_path).st_size) as bar:
# We hide the file during upload
sftp.put(
local_path,
"." + remote_filename,
callback=lambda done, total: bar.update(done - bar.pos)
)
# Upload new version
print("Uploading {} as {}...".format(f, remote_filename))
with click.progressbar(length=os.stat(local_path).st_size) as bar:
sftp.put(
local_path,
"." + remote_filename,
callback=lambda done, total: bar.update(done - bar.pos)
)
# We hide the file during upload.
sftp.rename("." + remote_filename, remote_filename)
# Delete old versions
old_version = f.replace(get_version(), "*")
for f_old in sftp.listdir():
if fnmatch.fnmatch(f_old, old_version):
print("Removing {}...".format(f_old))
sftp.remove(f_old)
# update symlink for the latest release
if sftp.lexists(symlink_path):
print("Removing {}...".format(symlink_path))
sftp.remove(symlink_path)
# Show new version
sftp.rename("." + remote_filename, remote_filename)
# update symlink for the latest release
if sftp.lexists(symlink_path):
print("Removing {}...".format(symlink_path))
sftp.remove(symlink_path)
if f != wheel_name():
# "latest" isn't a proper wheel version, so this could not be installed.
# https://github.com/mitmproxy/mitmproxy/issues/1065
sftp.symlink("v{}/{}".format(get_version(), remote_filename), symlink_path)
@cli.command("wizard")
@click.option('--next-version', prompt=True)
@click.option('--username', prompt="PyPI Username")
@click.password_option(confirmation_prompt=False, prompt="PyPI Password")
@click.option('--repository', default="pypi")
@click.pass_context
def wizard(ctx, next_version, username, password, repository):
"""
Interactive Release Wizard
"""
is_dirty = git("status --porcelain")
if is_dirty:
raise RuntimeError("Repository is not clean.")
# update contributors file
ctx.invoke(contributors)
# Build test release
ctx.invoke(bdist)
try:
click.confirm("Please test the release now. Is it ok?", abort=True)
except click.Abort:
# undo changes
git("checkout CONTRIBUTORS")
raise
# Everything ok - let's ship it!
git("tag v{}".format(get_version()))
git("push --tags")
ctx.invoke(
upload_release,
username=username, password=password, repository=repository
)
click.confirm("Now please wait until CI has built binaries. Finished?")
# version bump commit
ctx.invoke(set_version, version=next_version)
git("commit -a -m \"bump version\"")
git("push")
click.echo("All done!")
if __name__ == "__main__":
cli()

View File

@ -67,10 +67,12 @@ setup(
"configargparse>=0.10, <0.11",
"construct>=2.5.2, <2.6",
"cryptography>=1.3, <1.5",
"cssutils>=1.0.1, <1.1",
"Flask>=0.10.1, <0.12",
"h2>=2.4.0, <3",
"html2text>=2016.1.8, <=2016.5.29",
"hyperframe>=4.0.1, <5",
"jsbeautifier>=1.6.3, <1.7",
"lxml>=3.5.0, <=3.6.0", # no wheels for 3.6.1 yet.
"Pillow>=3.2, <3.4",
"passlib>=1.6.5, <1.7",
@ -83,6 +85,7 @@ setup(
"tornado>=4.3, <4.5",
"urwid>=1.3.1, <1.4",
"watchdog>=0.8.3, <0.9",
"brotlipy>=0.3.0, <0.4",
],
extras_require={
':sys_platform == "win32"': [
@ -110,7 +113,6 @@ setup(
"sphinx_rtd_theme>=0.1.9, <0.2",
],
'contentviews': [
"cssutils>=1.0.1, <1.1",
# TODO: Find Python 3 replacements
# "protobuf>=2.6.1, <2.7",
# "pyamf>=0.8.0, <0.9",

View File

@ -81,4 +81,4 @@ class TestContentView(mastertest.MasterTest):
d = dumper.Dumper()
m.addons.add(o, d)
self.invoke(m, "response", tutils.tflow())
assert "Content viewer failed" in m.event_log[0][1]
assert "Content viewer failed" in m.event_log[0][1]

View File

@ -76,7 +76,7 @@ class TestConsoleState:
self._add_response(c)
self._add_request(c)
self._add_response(c)
assert not c.set_limit("~s")
assert not c.set_view_filter("~s")
assert len(c.view) == 3
assert c.focus == 0

View File

@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAPAfPQGCV/Z4MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTAxMTY0ODAxWhcNMTgwODIxMTY0ODAxWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEArp8LD34JhKCwcQbwIYQMg4+eCgLVN8fwB7+/qOfJbArPs0djFBN+F7c6
HGvMr24BKUk5u8pn4dPtNurm/vPC8ovNGmcXz62BQJpcMX2veVdRsF7yNwhNacNJ
Arq+70zNMwYBznx0XUxMF6j6nVFf3AW6SU04ylT4Mp3SY/BUUDAdfl1eRo0mPLNS
8rpsN+8YBw1Q7SCuBRVqpOgVIsL88svgQUSOlzvMZPBpG/cmB3BNKNrltwb5iFEI
1jAV7uSj5IcIuNO/246kfsDVPTFMJIzav/CUoidd5UNw+SoFDlzh8sA7L1Bm7D1/
3KHYSKswGsSR3kynAl10w/SJKDtn8wIDAQABo1AwTjAdBgNVHQ4EFgQUgOcrtxBX
LxbpnOT65d+vpfyWUkgwHwYDVR0jBBgwFoAUgOcrtxBXLxbpnOT65d+vpfyWUkgw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEE9bFmUCA+6cvESKPoi2
TGSpV652d0xd2U66LpEXeiWRJFLz8YGgoJCx3QFGBscJDXxrLxrBBBV/tCpEqypo
pYIqsawH7M66jpOr83Us3M8JC2eFBZJocMpXxdytWqHik5VKZNx6VQFT8bS7+yVC
VoUKePhlgcg+pmo41qjqieBNKRMh/1tXS77DI1lgO5wZLVrLXcdqWuDpmaQOKJeq
G/nxytCW/YJA7bFn/8Gjy8DYypJSeeaKu7o3P3+ONJHdIMHb+MdcheDBS9AOFSeo
xI0D5EbO9F873O77l7nbD7B0X34HFN0nGczC4poexIpbDFG3hAPekwZ5KC6VwJLc
1Q==
-----END CERTIFICATE-----

View File

@ -0,0 +1,46 @@
-----BEGIN CERTIFICATE-----
MIIDEzCCAfugAwIBAgIJAJ945xt1FRsfMA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNV
BAMMFWV4YW1wbGUubWl0bXByb3h5Lm9yZzAeFw0xNTExMDExNjQ4MDJaFw0xODA4
MjExNjQ4MDJaMCAxHjAcBgNVBAMMFWV4YW1wbGUubWl0bXByb3h5Lm9yZzCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALFxyzPfjgIghOMMnJlW80yB84xC
nJtko3tuyOdozgTCyha2W+NdIKPNZJtWrzN4P0B5PlozCDwfcSYffLs0WZs8LRWv
BfZX8+oX+14qQjKFsiqgO65cTLP3qlPySYPJQQ37vOP1Y5Yf8nQq2mwQdC18hLtT
QOANG6OFoSplpBLsYF+QeoMgqCTa6hrl/5GLmQoDRTjXkv3Sj379AUDMybuBqccm
q5EIqCrE4+xJ8JywJclAVn2YP14baiFrrYCsYYg4sS1Od6xFj+xtpLe7My3AYjB9
/aeHd8vDiob0cqOW1TFwhqgJKuErfFyg8lZ2hJmStJKyfofWuY/gl/vnvX0CAwEA
AaNQME4wHQYDVR0OBBYEFB8d32zK8eqZIoKw4jXzYzhw4amPMB8GA1UdIwQYMBaA
FB8d32zK8eqZIoKw4jXzYzhw4amPMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAJmo2oKv1OEjZ0Q4yELO6BAnHAkmBKpW+zmLyQa8idxtLVkI9uXk3iqY
GWugkmcUZCTVFRWv/QXQQSex+00IY3x2rdHbtuZwcyKiz2u8WEmfW1rOIwBaFJ1i
v7+SA2aZs6vepN2sE56X54c/YbwQooaKZtOb+djWXYMJrc/Ezj0J7oQIJTptYV8v
/3216yCHRp/KCL7yTLtiw25xKuXNu/gkcd8wZOY9rS2qMUD897MJF0MvgJoauRBd
d4XEYCNKkrIRmfqrkiRQfAZpvpoutH6NCk7KuQYcI0BlOHlsnHHcs/w72EEqHwFq
x6476tW/t8GJDZVD74+pNBcLifXxArE=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAsXHLM9+OAiCE4wycmVbzTIHzjEKcm2Sje27I52jOBMLKFrZb
410go81km1avM3g/QHk+WjMIPB9xJh98uzRZmzwtFa8F9lfz6hf7XipCMoWyKqA7
rlxMs/eqU/JJg8lBDfu84/Vjlh/ydCrabBB0LXyEu1NA4A0bo4WhKmWkEuxgX5B6
gyCoJNrqGuX/kYuZCgNFONeS/dKPfv0BQMzJu4GpxyarkQioKsTj7EnwnLAlyUBW
fZg/XhtqIWutgKxhiDixLU53rEWP7G2kt7szLcBiMH39p4d3y8OKhvRyo5bVMXCG
qAkq4St8XKDyVnaEmZK0krJ+h9a5j+CX++e9fQIDAQABAoIBAQCT+FvGbych2PJX
0D2KlXqgE0IAdc/YuYymstSwPLKIP9N8KyfnKtK8Jdw+uYOyfRTp8/EuEJ5OXL3j
V6CRD++lRwIlseVb7y5EySjh9oVrUhgn+aSrGucPsHkGNeZeEmbAfWugARLBrvRl
MRMhyHrJL6wT9jIEZInmy9mA3G99IuFW3rS8UR1Yu7zyvhtjvop1xg/wfEUu24Ty
PvMfnwaDcZHCz2tmu2KJvaxSBAG3FKmAqeMvk1Gt5m2keKgw03M+EX0LrM8ybWqn
VwB8tnSyMBLVFLIXMpIiSfpji10+p9fdKFMRF++D6qVwyoxPiIq+yEJapxXiqLea
mkhtJW91AoGBAOvIb7bZvH4wYvi6txs2pygF3ZMjqg/fycnplrmYMrjeeDeeN4v1
h/5tkN9TeTkHRaN3L7v49NEUDhDyuopLTNfWpYdv63U/BVzvgMm/guacTYkx9whB
OvQ2YekR/WKg7kuyrTZidTDz+mjU+1b8JaWGjiDc6vFwxZA7uWicaGGHAoGBAMCo
y/2AwFGwCR+5bET1nTTyxok6iKo4k6R/7DJe4Bq8VLifoyX3zDlGG/33KN3xVqBU
xnT9gkii1lfX2U+4iM+GOSPl0nG0hOEqEH+vFHszpHybDeNez3FEyIbgOzg6u7sV
NOy+P94L5EMQVEmWp5g6Vm3k9kr92Bd9UacKQPnbAoGAMN8KyMu41i8RVJze9zUM
0K7mjmkGBuRL3x4br7xsRwVVxbF1sfzig0oSjTewGLH5LTi3HC8uD2gowjqNj7yr
4NEM3lXEaDj305uRBkA70bD0IUvJ+FwM7DGZecXQz3Cr8+TFIlCmGc94R+Jddlot
M3IAY69mw0SsroiylYxV1mECgYAcSGtx8rXJCDO+sYTgdsI2ZLGasbogax/ZlWIC
XwU9R4qUc/MKft8/RTiUxvT76BMUhH2B7Tl0GlunF6vyVR/Yf1biGzoSsTKUr40u
gXBbSdCK7mRSjbecZEGf80keTxkCNPHJE4DiwxImej41c2V1JpNLnMI/bhaMFDyp
bgrt4wKBgHFzZgAgM1v07F038tAkIBGrYLukY1ZFBaZoGZ9xHfy/EmLJM3HCHLO5
8wszMGhMTe2+39EeChwgj0kFaq1YnDiucU74BC57KR1tD59y7l6UnsQXTm4/32j8
Or6i8GekBibCb97DzzOU0ZK//fNhHTXpDDXsYt5lJUWSmgW+S9Qp
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,45 @@
-----BEGIN CERTIFICATE-----
MIIC4TCCAckCCQCj6D9oVylb8jANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMB4XDTE1MTEwMTE2NDgwMloXDTE4MDgyMTE2NDgwMlowIDEeMBwG
A1UEAwwVZXhhbXBsZS5taXRtcHJveHkub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAy/L5JYHS7QFhSIsjmd6bJTgs2rdqEn6tsmPBVZKZ7SqCAVjW
hPpEu7Q23akmU6Zm9Fp/vENc3jzxQLlEKhrv7eWmFYSOrCYtbJOz3RQorlwjjfdY
LlNQh1wYUXQX3PN3r3dyYtt5vTtXKc8+aP4M4vX7qlbW+4j4LrQfmPjS0XOdYpu3
wh+i1ZMIhZye3hpCjwnpjTf7/ff45ZFxtkoi1uzEC/+swr1RSvamY8Foe12Re17Z
5ij8ZB0NIdoSk1tDkY3sJ8iNi35+qartl0UYeG9IUXRwDRrPsEKpF4RxY1+X2bdZ
r6PKb/E4CA5JlMvS5SVmrvxjCVqTQBmTjXfxqwIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQBmpSZJrTDvzSlo6P7P7x1LoETzHyVjwgPeqGYw6ndGXeJMN9rhhsFvRsiB
I/aHh58MIlSjti7paikDAoFHB3dBvFHR+JUa/ailWEbcZReWRSE3lV6wFiN3G3lU
OyofR7MKnPW7bv8hSqOLqP1mbupXuQFB5M6vPLRwg5VgiCHI/XBiTvzMamzvNAR3
UHHZtsJkRqzogYm6K9YJaga7jteSx2nNo+ujLwrxeXsLChTyFMJGnVkp5IyKeNfc
qwlzNncb3y+4KnUdNkPEtuydgAxAfuyXufiFBYRcUWbQ5/9ycgF7131ySaj9f/Y2
kMsv2jg+soKvwwVYCABsk1KSHtfz
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAy/L5JYHS7QFhSIsjmd6bJTgs2rdqEn6tsmPBVZKZ7SqCAVjW
hPpEu7Q23akmU6Zm9Fp/vENc3jzxQLlEKhrv7eWmFYSOrCYtbJOz3RQorlwjjfdY
LlNQh1wYUXQX3PN3r3dyYtt5vTtXKc8+aP4M4vX7qlbW+4j4LrQfmPjS0XOdYpu3
wh+i1ZMIhZye3hpCjwnpjTf7/ff45ZFxtkoi1uzEC/+swr1RSvamY8Foe12Re17Z
5ij8ZB0NIdoSk1tDkY3sJ8iNi35+qartl0UYeG9IUXRwDRrPsEKpF4RxY1+X2bdZ
r6PKb/E4CA5JlMvS5SVmrvxjCVqTQBmTjXfxqwIDAQABAoIBAQC956DWq+wbhA1x
3x1nSUBth8E8Z0z9q7dRRFHhvIBXth0X5ADcEa2umj/8ZmSpv2heX2ZRhugSh+yc
t+YgzrRacFwV7ThsU6A4WdBBK2Q19tWke4xAlpOFdtut/Mu7kXkAidiY9ISHD5o5
9B/I48ZcD3AnTHUiAogV9OL3LbogDD4HasLt4mWkbq8U2thdjxMIvxdg36olJEuo
iAZrAUCPZEXuU89BtvPLUYioe9n90nzkyneGNS0SHxotlEc9ZYK9VTsivtXJb4wB
ptDMCp+TH3tjo8BTGnbnoZEybgyyOEd0UTzxK4DlxnvRVWexFY6NXwPFhIxKlB0Y
Bg8NkAkBAoGBAOiRnmbC5QkqrKrTkLx3fghIHPqgEXPPYgHLSuY3UjTlMb3APXpq
vzQnlCn3QuSse/1fWnQj+9vLVbx1XNgKjzk7dQhn5IUY+mGN4lLmoSnTebxvSQ43
VAgTYjST9JFmJ3wK4KkWDsEsVao8LAx0h5JEQXUTT5xZpFA2MLztYbgfAoGBAOB/
MvhLMAwlx8+m/zXMEPLk/KOd2dVZ4q5se8bAT/GiGsi8JUcPnCk140ZZabJqryAp
JFzUHIjfVsS9ejAfocDk1JeIm7Uus4um6fQEKIPMBxI/M/UAwYCXAG9ULXqilbO3
pTdeeuraVKrTu1Z4ea6x4du1JWKcyDfYfsHepcT1AoGBAM2fskV5G7e3G2MOG3IG
1E/OMpEE5WlXenfLnjVdxDkwS4JRbgnGR7d9JurTyzkTp6ylmfwFtLDoXq15ttTs
wSUBBMCh2tIy+201XV2eu++XIpMQca84C/v352RFTH8hqtdpZqkY74KsCDGzcd6x
SQxxfM5efIzoVPb2crEX0MZRAoGAQ2EqFSfL9flo7UQ8GRN0itJ7mUgJV2WxCZT5
2X9i/y0eSN1feuKOhjfsTPMNLEWk5kwy48GuBs6xpj8Qa10zGUgVHp4bzdeEgAfK
9DhDSLt1694YZBKkAUpRERj8xXAC6nvWFLZAwjhhbRw7gAqMywgMt/q4i85usYRD
F0ESE/kCgYBbc083PcLmlHbkn/d1i4IcLI6wFk+tZYIEVYDid7xDOgZOBcOTTyYB
BrDzNqbKNexKRt7QHVlwR+VOGMdN5P0hf7oH3SMW23OxBKoQe8pUSGF9a4DjCS1v
vCXMekifb9kIhhUWaG71L8+MaOzNBVAmk1+3NzPZgV/YxHjAWWhGHQ==
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1,48 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAPAfPQGCV/Z4MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTAxMTY0ODAxWhcNMTgwODIxMTY0ODAxWjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEArp8LD34JhKCwcQbwIYQMg4+eCgLVN8fwB7+/qOfJbArPs0djFBN+F7c6
HGvMr24BKUk5u8pn4dPtNurm/vPC8ovNGmcXz62BQJpcMX2veVdRsF7yNwhNacNJ
Arq+70zNMwYBznx0XUxMF6j6nVFf3AW6SU04ylT4Mp3SY/BUUDAdfl1eRo0mPLNS
8rpsN+8YBw1Q7SCuBRVqpOgVIsL88svgQUSOlzvMZPBpG/cmB3BNKNrltwb5iFEI
1jAV7uSj5IcIuNO/246kfsDVPTFMJIzav/CUoidd5UNw+SoFDlzh8sA7L1Bm7D1/
3KHYSKswGsSR3kynAl10w/SJKDtn8wIDAQABo1AwTjAdBgNVHQ4EFgQUgOcrtxBX
LxbpnOT65d+vpfyWUkgwHwYDVR0jBBgwFoAUgOcrtxBXLxbpnOT65d+vpfyWUkgw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEE9bFmUCA+6cvESKPoi2
TGSpV652d0xd2U66LpEXeiWRJFLz8YGgoJCx3QFGBscJDXxrLxrBBBV/tCpEqypo
pYIqsawH7M66jpOr83Us3M8JC2eFBZJocMpXxdytWqHik5VKZNx6VQFT8bS7+yVC
VoUKePhlgcg+pmo41qjqieBNKRMh/1tXS77DI1lgO5wZLVrLXcdqWuDpmaQOKJeq
G/nxytCW/YJA7bFn/8Gjy8DYypJSeeaKu7o3P3+ONJHdIMHb+MdcheDBS9AOFSeo
xI0D5EbO9F873O77l7nbD7B0X34HFN0nGczC4poexIpbDFG3hAPekwZ5KC6VwJLc
1Q==
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEArp8LD34JhKCwcQbwIYQMg4+eCgLVN8fwB7+/qOfJbArPs0dj
FBN+F7c6HGvMr24BKUk5u8pn4dPtNurm/vPC8ovNGmcXz62BQJpcMX2veVdRsF7y
NwhNacNJArq+70zNMwYBznx0XUxMF6j6nVFf3AW6SU04ylT4Mp3SY/BUUDAdfl1e
Ro0mPLNS8rpsN+8YBw1Q7SCuBRVqpOgVIsL88svgQUSOlzvMZPBpG/cmB3BNKNrl
twb5iFEI1jAV7uSj5IcIuNO/246kfsDVPTFMJIzav/CUoidd5UNw+SoFDlzh8sA7
L1Bm7D1/3KHYSKswGsSR3kynAl10w/SJKDtn8wIDAQABAoIBAFgMzjDzpqz/sbhs
fS0JPp4gDtqRbx3/bSMbJvNuXPxjvzNxLZ5z7cLbmyu1l7Jlz6QXzkrI1vTiPdzR
OcUY+RYANF252iHYJTKEIzS5YX/X7dL3LT9eqlpIJEqCC8Dygw3VW5fY3Xwl+sB7
blNhMuro4HQRwi8UBUrQlcPa7Ui5BBi323Q6en+VjYctkqpJHzNKPSqPTbsdLaK+
B0XuXxFatM09rmeRKZCL71Lk1T8N/l0hqEzej7zxgVD7vG/x1kMFN4T3yCmXCbPa
izGHYr1EBHglm4qMNWveXCZiVJ+wmwCjdjqvggyHiZFXE2N0OCrWPhxQPdqFf5y7
bUO9U2ECgYEA6GM1UzRnbVpjb20ezFy7dU7rlWM0nHBfG27M3bcXh4HnPpnvKp0/
8a1WFi4kkRywrNXx8hFEd43vTbdObLpVXScXRKiY3MHmFk4k4hbWuTpmumCubQZO
AWlX6TE0HRKn1wQahgpQcxcWaDN2xJJmRQ1zVmlnNkT48/4kFgRxyykCgYEAwF08
ngrF35oYoU/x+KKq2NXGeNUzoZMj568dE1oWW0ZFpqCi+DGT+hAbG3yUOBSaPqy9
zn1obGo0YRlrayvtebz118kG7a/rzY02VcAPlT/GpEhvkZlXTwEK17zRJc1nJrfP
39QAZWZsaOru9NRIg/8HcdG3JPR2MhRD/De9GbsCgYAaiZnBUq6s8jGAu/lUZRKT
JtwIRzfu1XZG77Q9bXcmZlM99t41A5gVxTGbftF2MMyMMDJc7lPfQzocqd4u1GiD
Jr+le4tZSls4GNxlZS5IIL8ycW/5y0qFJr5/RrsoxsSb7UAKJothWTWZ2Karc/xx
zkNpjsfWjrHPSypbyU4lYQKBgFh1R5/BgnatjO/5LGNSok/uFkOQfxqo6BTtYOh6
P9efO/5A1lBdtBeE+oIsSphzWO7DTtE6uB9Kw2V3Y/83hw+5RjABoG8Cu+OdMURD
eqb+WeFH8g45Pn31E8Bbcq34g5u5YR0jhz8Z13ZzuojZabNRPmIntxmGVSf4S78a
/plrAoGBANMHNng2lyr03nqnHrOM6NXD+60af0YR/YJ+2d/H40RnXxGJ4DXn7F00
a4vJFPa97uq+xpd0HE+TE+NIrOdVDXPePD2qzBzMTsctGtj30vLzojMOT+Yf/nvO
WxTL5Q8GruJz2Dn0awSZO2z/3A8S1rmpuVZ/jT5NtRrvOSY6hmxF
-----END RSA PRIVATE KEY-----

View File

@ -1,14 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICJzCCAZACCQCo1BdopddN/TANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMRAwDgYDVQQDEwdUUlVTVEVEMCAXDTE1MDYxOTE4MDEzMVoYDzIx
MTUwNTI2MTgwMTMxWjBXMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRAwDgYDVQQDEwdU
UlVTVEVEMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC00Jf3KrBAmLQWl+Dz
8Qrig8ActB94kv0/Lu03P/2DwOR8kH2h3w4OC3b3CFKX31h7hm/H1PPHq7cIX6IR
fwrYCtBE77UbxklSlrwn06j6YSotz0/dwLEQEFDXWITJq7AyntaiafDHazbbXESN
m/+I/YEl2wKemEHE//qWbeM9kwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAF0NREP3
X+fTebzJGttzrFkDhGVFKRNyLXblXRVanlGOYF+q8grgZY2ufC/55gqf+ub6FRT5
gKPhL4V2rqL8UAvCE7jq8ujpVfTB8kRAKC675W2DBZk2EJX9mjlr89t7qXGsI5nF
onpfJ1UtiJshNoV7h/NFHeoag91kx628807n
-----END CERTIFICATE-----

View File

@ -1,14 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICJzCCAZACCQCo1BdopddN/TANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMRAwDgYDVQQDEwdUUlVTVEVEMCAXDTE1MDYxOTE4MDEzMVoYDzIx
MTUwNTI2MTgwMTMxWjBXMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRAwDgYDVQQDEwdU
UlVTVEVEMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC00Jf3KrBAmLQWl+Dz
8Qrig8ActB94kv0/Lu03P/2DwOR8kH2h3w4OC3b3CFKX31h7hm/H1PPHq7cIX6IR
fwrYCtBE77UbxklSlrwn06j6YSotz0/dwLEQEFDXWITJq7AyntaiafDHazbbXESN
m/+I/YEl2wKemEHE//qWbeM9kwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAF0NREP3
X+fTebzJGttzrFkDhGVFKRNyLXblXRVanlGOYF+q8grgZY2ufC/55gqf+ub6FRT5
gKPhL4V2rqL8UAvCE7jq8ujpVfTB8kRAKC675W2DBZk2EJX9mjlr89t7qXGsI5nF
onpfJ1UtiJshNoV7h/NFHeoag91kx628807n
-----END CERTIFICATE-----

View File

@ -1,14 +0,0 @@
-----BEGIN CERTIFICATE-----
MIICJzCCAZACCQCo1BdopddN/TANBgkqhkiG9w0BAQUFADBXMQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMRAwDgYDVQQDEwdUUlVTVEVEMCAXDTE1MDYxOTE4MDEzMVoYDzIx
MTUwNTI2MTgwMTMxWjBXMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0
ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRAwDgYDVQQDEwdU
UlVTVEVEMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC00Jf3KrBAmLQWl+Dz
8Qrig8ActB94kv0/Lu03P/2DwOR8kH2h3w4OC3b3CFKX31h7hm/H1PPHq7cIX6IR
fwrYCtBE77UbxklSlrwn06j6YSotz0/dwLEQEFDXWITJq7AyntaiafDHazbbXESN
m/+I/YEl2wKemEHE//qWbeM9kwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAF0NREP3
X+fTebzJGttzrFkDhGVFKRNyLXblXRVanlGOYF+q8grgZY2ufC/55gqf+ub6FRT5
gKPhL4V2rqL8UAvCE7jq8ujpVfTB8kRAKC675W2DBZk2EJX9mjlr89t7qXGsI5nF
onpfJ1UtiJshNoV7h/NFHeoag91kx628807n
-----END CERTIFICATE-----

View File

@ -1,33 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIC8jCCAlugAwIBAgICEAcwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQVUx
EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg
UHR5IEx0ZDEQMA4GA1UEAxMHVFJVU1RFRDAgFw0xNTA2MjAwMTE4MjdaGA8yMTE1
MDUyNzAxMTgyN1owfjELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUx
ITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UECxMLSU5U
RVJNIFVOSVQxITAfBgNVBAMTGE9SRyBXSVRIIElOVEVSTUVESUFURSBDQTCBnzAN
BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtRPNKgh4WdYGmU2Ae6Tf2Mbd3oaRI/uY
Qm6aKeYk1i7g41C0vVowNcD/qdNpGUNnai/Kak9anHOYyppNo7zHgf3EO8zQ4NTQ
pkDKsdCqbUQcjGfhjWXKnOw+I5er4Rj+MwM1f5cbwb8bYHiSPmXaxzdL0/SNXGAA
ys/UswgwkU8CAwEAAaOBozCBoDAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBTPkPQW
DAPOIy8mipuEsZcP1694EDBxBgNVHSMEajBooVukWTBXMQswCQYDVQQGEwJBVTET
MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ
dHkgTHRkMRAwDgYDVQQDEwdUUlVTVEVEggkAqNQXaKXXTf0wDQYJKoZIhvcNAQEF
BQADgYEApaPbwonY8l+zSxlY2Fw4WNKfl5nwcTW4fuv/0tZLzvsS6P4hTXxbYJNa
k3hQ1qlrr8DiWJewF85hYvEI2F/7eqS5dhhPTEUFPpsjhbgiqnASvW+WKQIgoY2r
aHgOXi7RNFtTcCgk0UZISWOY7ORLy8Xu6vKrLRjDhyfIbGlqnAs=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQC1E80qCHhZ1gaZTYB7pN/Yxt3ehpEj+5hCbpop5iTWLuDjULS9
WjA1wP+p02kZQ2dqL8pqT1qcc5jKmk2jvMeB/cQ7zNDg1NCmQMqx0KptRByMZ+GN
Zcqc7D4jl6vhGP4zAzV/lxvBvxtgeJI+ZdrHN0vT9I1cYADKz9SzCDCRTwIDAQAB
AoGAfKHocKnrzEmXuSSy7meI+vfF9kfA1ndxUSg3S+dwK0uQ1mTSQhI1ZIo2bnlo
uU6/e0Lxm0KLJ2wZGjoifjSNTC8pcxIfAQY4kM9fqoUcXVSBVSS2kByTunhNSVZQ
yQyc+UTq9g1zBnJsZAltn7/PaihU4heWgP/++lposuShqmECQQDaG+7l0qul1xak
9kuZgc88BSTfn9iMK2zIQRcVKuidK4dT3QEp0wmWR5Ue8jq8lvTmVTGNGZbHcheh
KhoZfLgLAkEA1IjwAw/8z02yV3lbc2QUjIl9m9lvjHBoE2sGuSfq/cZskLKrGat+
CVj3spqVAg22tpQwVBuHiipBziWVnEtiTQJAB9FKfchQSLBt6lm9mfHyKJeSm8VR
8Kw5yO+0URjpn4CI6DOasBIVXOKR8LsD6fCLNJpHHWSWZ+2p9SfaKaGzwwJBAM31
Scld89qca4fzNZkT0goCrvOZeUy6HVE79Q72zPVSFSD/02kT1BaQ3bB5to5/5aD2
6AKJjwZoPs7bgykrsD0CQBzU8U/8x2dNQnG0QeqaKQu5kKhZSZ9bsawvrCkxSl6b
WAjl/Jehi5bbQ07zQo3cge6qeR38FCWVCHQ/5wNbc54=
-----END RSA PRIVATE KEY-----

View File

@ -1,32 +0,0 @@
# untrusted-interm.crt, self-signed
-----BEGIN CERTIFICATE-----
MIICdTCCAd4CCQDRSKOnIMbTgDANBgkqhkiG9w0BAQUFADB+MQswCQYDVQQGEwJB
VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0
cyBQdHkgTHRkMRQwEgYDVQQLEwtJTlRFUk0gVU5JVDEhMB8GA1UEAxMYT1JHIFdJ
VEggSU5URVJNRURJQVRFIENBMCAXDTE1MDYyMDAxMzY0M1oYDzIxMTUwNTI3MDEz
NjQzWjB+MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UE
ChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQLEwtJTlRFUk0gVU5J
VDEhMB8GA1UEAxMYT1JHIFdJVEggSU5URVJNRURJQVRFIENBMIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQC1E80qCHhZ1gaZTYB7pN/Yxt3ehpEj+5hCbpop5iTW
LuDjULS9WjA1wP+p02kZQ2dqL8pqT1qcc5jKmk2jvMeB/cQ7zNDg1NCmQMqx0Kpt
RByMZ+GNZcqc7D4jl6vhGP4zAzV/lxvBvxtgeJI+ZdrHN0vT9I1cYADKz9SzCDCR
TwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAGbObAMEajCz4kj7OP2/DB5SRy2+H/G3
8Qvc43xlMMNQyYxsDuLOFL0UMRzoKgntrrm2nni8jND+tuMt+hv3ZlBcJlYJ6ynR
sC1ITTC/1SwwwO0AFIyduUEIJYr/B3sgcVYPLcEfeDZgmEQc9Tnc01aEu3lx2+l9
0JTSPL2L9LdA
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQC1E80qCHhZ1gaZTYB7pN/Yxt3ehpEj+5hCbpop5iTWLuDjULS9
WjA1wP+p02kZQ2dqL8pqT1qcc5jKmk2jvMeB/cQ7zNDg1NCmQMqx0KptRByMZ+GN
Zcqc7D4jl6vhGP4zAzV/lxvBvxtgeJI+ZdrHN0vT9I1cYADKz9SzCDCRTwIDAQAB
AoGAfKHocKnrzEmXuSSy7meI+vfF9kfA1ndxUSg3S+dwK0uQ1mTSQhI1ZIo2bnlo
uU6/e0Lxm0KLJ2wZGjoifjSNTC8pcxIfAQY4kM9fqoUcXVSBVSS2kByTunhNSVZQ
yQyc+UTq9g1zBnJsZAltn7/PaihU4heWgP/++lposuShqmECQQDaG+7l0qul1xak
9kuZgc88BSTfn9iMK2zIQRcVKuidK4dT3QEp0wmWR5Ue8jq8lvTmVTGNGZbHcheh
KhoZfLgLAkEA1IjwAw/8z02yV3lbc2QUjIl9m9lvjHBoE2sGuSfq/cZskLKrGat+
CVj3spqVAg22tpQwVBuHiipBziWVnEtiTQJAB9FKfchQSLBt6lm9mfHyKJeSm8VR
8Kw5yO+0URjpn4CI6DOasBIVXOKR8LsD6fCLNJpHHWSWZ+2p9SfaKaGzwwJBAM31
Scld89qca4fzNZkT0goCrvOZeUy6HVE79Q72zPVSFSD/02kT1BaQ3bB5to5/5aD2
6AKJjwZoPs7bgykrsD0CQBzU8U/8x2dNQnG0QeqaKQu5kKhZSZ9bsawvrCkxSl6b
WAjl/Jehi5bbQ07zQo3cge6qeR38FCWVCHQ/5wNbc54=
-----END RSA PRIVATE KEY-----

View File

@ -224,7 +224,7 @@ def test_get_content_view():
view_auto.side_effect = ValueError
desc, lines, err = cv.get_content_view(
cv.get("JSON"),
cv.get("Auto"),
b"[1, 2",
)
assert err
@ -282,4 +282,4 @@ def test_pretty_json():
assert cv.pretty_json(b'{"foo": 1}')
assert not cv.pretty_json(b"moo")
assert cv.pretty_json(b'{"foo" : "\xe4\xb8\x96\xe7\x95\x8c"}') # utf8 with chinese characters
assert not cv.pretty_json(b'{"foo" : "\xFF"}')
assert not cv.pretty_json(b'{"foo" : "\xFF"}')

View File

@ -504,13 +504,13 @@ class TestState:
c = flow.State()
f = tutils.tflow()
c.add_flow(f)
c.set_limit("~e")
c.set_view_filter("~e")
assert not c.view
f.error = tutils.terr()
assert c.update_flow(f)
assert c.view
def test_set_limit(self):
def test_set_view_filter(self):
c = flow.State()
f = tutils.tflow()
@ -519,24 +519,24 @@ class TestState:
c.add_flow(f)
assert len(c.view) == 1
c.set_limit("~s")
assert c.limit_txt == "~s"
c.set_view_filter("~s")
assert c.filter_txt == "~s"
assert len(c.view) == 0
f.response = HTTPResponse.wrap(netlib.tutils.tresp())
c.update_flow(f)
assert len(c.view) == 1
c.set_limit(None)
c.set_view_filter(None)
assert len(c.view) == 1
f = tutils.tflow()
c.add_flow(f)
assert len(c.view) == 2
c.set_limit("~q")
c.set_view_filter("~q")
assert len(c.view) == 1
c.set_limit("~s")
c.set_view_filter("~s")
assert len(c.view) == 1
assert "Invalid" in c.set_limit("~")
assert "Invalid" in c.set_view_filter("~")
def test_set_intercept(self):
c = flow.State()

View File

@ -102,7 +102,11 @@ class _Http2TestBase(object):
@classmethod
def get_options(cls):
opts = options.Options(listen_port=0, no_upstream_cert=False)
opts = options.Options(
listen_port=0,
no_upstream_cert=False,
ssl_insecure=True
)
opts.cadir = os.path.join(tempfile.gettempdir(), "mitmproxy")
return opts

View File

@ -146,9 +146,9 @@ class TestProcessProxyOptions:
"--singleuser",
"test")
def test_verify_upstream_cert(self):
p = self.assert_noerr("--verify-upstream-cert")
assert p.openssl_verification_mode_server == SSL.VERIFY_PEER
def test_insecure(self):
p = self.assert_noerr("--insecure")
assert p.openssl_verification_mode_server == SSL.VERIFY_NONE
def test_upstream_trusted_cadir(self):
expected_dir = "/path/to/a/ca/dir"

View File

@ -2,22 +2,21 @@ import os
import socket
import time
import types
from OpenSSL import SSL
from netlib.exceptions import HttpReadDisconnect, HttpException
from netlib.tcp import Address
import netlib.tutils
from mitmproxy import controller
from mitmproxy import options
from mitmproxy.builtins import script
from mitmproxy.models import Error, HTTPResponse, HTTPFlow
from mitmproxy.proxy.config import HostMatcher, parse_server_spec
from netlib import tcp, http, socks
from netlib.certutils import SSLCert
from netlib.exceptions import HttpReadDisconnect, HttpException
from netlib.http import authentication, http1
from netlib.tcp import Address
from netlib.tutils import raises
from pathod import pathoc, pathod
from mitmproxy.builtins import script
from mitmproxy import controller
from mitmproxy.proxy.config import HostMatcher, parse_server_spec
from mitmproxy.models import Error, HTTPResponse, HTTPFlow
from . import tutils, tservers
"""
@ -350,6 +349,15 @@ class TestHTTPSCertfile(tservers.HTTPProxyTest, CommonMixin):
assert self.pathod("304")
class TestHTTPSSecureByDefault:
def test_secure_by_default(self):
"""
Certificate verification should be turned on by default.
"""
default_opts = options.Options()
assert not default_opts.ssl_insecure
class TestHTTPSUpstreamServerVerificationWTrustedCert(tservers.HTTPProxyTest):
"""
@ -357,26 +365,35 @@ class TestHTTPSUpstreamServerVerificationWTrustedCert(tservers.HTTPProxyTest):
"""
ssl = True
ssloptions = pathod.SSLOptions(
cn=b"trusted-cert",
cn=b"example.mitmproxy.org",
certs=[
("trusted-cert", tutils.test_data.path("data/trusted-server.crt"))
])
("example.mitmproxy.org", tutils.test_data.path("data/servercert/trusted-leaf.pem"))
]
)
def _request(self):
p = self.pathoc(sni="example.mitmproxy.org")
return p.request("get:/p/242")
def test_verification_w_cadir(self):
self.config.options.update(
ssl_verify_upstream_cert = True,
ssl_verify_upstream_trusted_cadir = tutils.test_data.path(
"data/trusted-cadir/"
)
ssl_insecure=False,
ssl_verify_upstream_trusted_cadir=tutils.test_data.path(
"data/servercert/"
),
ssl_verify_upstream_trusted_ca=None,
)
self.pathoc()
assert self._request().status_code == 242
def test_verification_w_pemfile(self):
self.config.openssl_verification_mode_server = SSL.VERIFY_PEER
self.config.options.ssl_verify_upstream_trusted_ca = tutils.test_data.path(
"data/trusted-cadir/trusted-ca.pem")
self.pathoc()
self.config.options.update(
ssl_insecure=False,
ssl_verify_upstream_trusted_cadir=None,
ssl_verify_upstream_trusted_ca=tutils.test_data.path(
"data/servercert/trusted-root.pem"
),
)
assert self._request().status_code == 242
class TestHTTPSUpstreamServerVerificationWBadCert(tservers.HTTPProxyTest):
@ -386,42 +403,36 @@ class TestHTTPSUpstreamServerVerificationWBadCert(tservers.HTTPProxyTest):
"""
ssl = True
ssloptions = pathod.SSLOptions(
cn=b"untrusted-cert",
cn=b"example.mitmproxy.org",
certs=[
("untrusted-cert", tutils.test_data.path("data/untrusted-server.crt"))
("example.mitmproxy.org", tutils.test_data.path("data/servercert/self-signed.pem"))
])
def _request(self):
p = self.pathoc()
# We need to make an actual request because the upstream connection is lazy-loaded.
p = self.pathoc(sni="example.mitmproxy.org")
return p.request("get:/p/242")
def test_default_verification_w_bad_cert(self):
"""Should use no verification."""
self.config.options.update(
ssl_verify_upstream_trusted_ca = tutils.test_data.path(
"data/trusted-cadir/trusted-ca.pem"
)
@classmethod
def get_options(cls):
opts = super(tservers.HTTPProxyTest, cls).get_options()
opts.ssl_verify_upstream_trusted_ca = tutils.test_data.path(
"data/servercert/trusted-root.pem"
)
assert self._request().status_code == 242
return opts
def test_no_verification_w_bad_cert(self):
self.config.options.update(
ssl_verify_upstream_cert = False,
ssl_verify_upstream_trusted_ca = tutils.test_data.path(
"data/trusted-cadir/trusted-ca.pem"
)
)
assert self._request().status_code == 242
self.config.options.ssl_insecure = True
r = self._request()
assert r.status_code == 242
def test_verification_w_bad_cert(self):
self.config.options.update(
ssl_verify_upstream_cert = True,
ssl_verify_upstream_trusted_ca = tutils.test_data.path(
"data/trusted-cadir/trusted-ca.pem"
)
)
assert self._request().status_code == 502
# We only test for a single invalid cert here.
# Actual testing of different root-causes (invalid hostname, expired, ...)
# is done in netlib.
self.config.options.ssl_insecure = False
r = self._request()
assert r.status_code == 502
assert b"Certificate Verification Error" in r.raw_content
class TestHTTPSNoCommonName(tservers.HTTPProxyTest):
@ -1021,11 +1032,11 @@ class TestProxyChainingSSLReconnect(tservers.HTTPUpstreamProxyTest):
class AddUpstreamCertsToClientChainMixin:
ssl = True
servercert = tutils.test_data.path("data/trusted-server.crt")
servercert = tutils.test_data.path("data/servercert/trusted-root.pem")
ssloptions = pathod.SSLOptions(
cn=b"trusted-cert",
cn=b"example.mitmproxy.org",
certs=[
(b"trusted-cert", servercert)
(b"example.mitmproxy.org", servercert)
]
)

View File

@ -120,7 +120,8 @@ class ProxyTestBase(object):
return options.Options(
listen_port=0,
cadir=cls.cadir,
add_upstream_certs_to_client_chain=cls.add_upstream_certs_to_client_chain
add_upstream_certs_to_client_chain=cls.add_upstream_certs_to_client_chain,
ssl_insecure=True,
)

View File

@ -21,6 +21,18 @@ def test_gzip():
encoding.decode(b"bogus", "gzip")
def test_brotli():
assert b"string" == encoding.decode(
encoding.encode(
b"string",
"br"
),
"br"
)
with tutils.raises(ValueError):
encoding.decode(b"bogus", "br")
def test_deflate():
assert b"string" == encoding.decode(
encoding.encode(

View File

@ -48,9 +48,12 @@ def test_bytes_to_escaped_str():
assert strutils.bytes_to_escaped_str(b"\b") == r"\x08"
assert strutils.bytes_to_escaped_str(br"&!?=\)") == r"&!?=\\)"
assert strutils.bytes_to_escaped_str(b'\xc3\xbc') == r"\xc3\xbc"
assert strutils.bytes_to_escaped_str(b"'") == r"\'"
assert strutils.bytes_to_escaped_str(b"'") == r"'"
assert strutils.bytes_to_escaped_str(b'"') == r'"'
assert strutils.bytes_to_escaped_str(b"'", escape_single_quotes=True) == r"\'"
assert strutils.bytes_to_escaped_str(b'"', escape_single_quotes=True) == r'"'
assert strutils.bytes_to_escaped_str(b"\r\n\t") == "\\r\\n\\t"
assert strutils.bytes_to_escaped_str(b"\r\n\t", True) == "\r\n\t"

View File

@ -213,7 +213,7 @@ class TestSSLUpstreamCertVerificationWBadServerCert(tservers.ServerTestBase):
# Verification errors should be saved even if connection isn't aborted
# aborted
assert c.ssl_verification_error is not None
assert c.ssl_verification_error
testval = b"echo!\n"
c.wfile.write(testval)
@ -226,7 +226,7 @@ class TestSSLUpstreamCertVerificationWBadServerCert(tservers.ServerTestBase):
c.convert_to_ssl(verify_options=SSL.VERIFY_NONE)
# Verification errors should be saved even if connection isn't aborted
assert c.ssl_verification_error is not None
assert c.ssl_verification_error
testval = b"echo!\n"
c.wfile.write(testval)
@ -243,11 +243,11 @@ class TestSSLUpstreamCertVerificationWBadServerCert(tservers.ServerTestBase):
ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted-root.crt")
)
assert c.ssl_verification_error is not None
assert c.ssl_verification_error
# Unknown issuing certificate authority for first certificate
assert c.ssl_verification_error['errno'] == 18
assert c.ssl_verification_error['depth'] == 0
assert "errno: 18" in str(c.ssl_verification_error)
assert "depth: 0" in str(c.ssl_verification_error)
class TestSSLUpstreamCertVerificationWBadHostname(tservers.ServerTestBase):
@ -276,7 +276,7 @@ class TestSSLUpstreamCertVerificationWBadHostname(tservers.ServerTestBase):
verify_options=SSL.VERIFY_PEER,
ca_pemfile=tutils.test_data.path("data/verificationcerts/trusted-root.crt")
)
assert c.ssl_verification_error is not None
assert c.ssl_verification_error
class TestSSLUpstreamCertVerificationWValidCertChain(tservers.ServerTestBase):

View File

@ -7,6 +7,7 @@
"start": "gulp"
},
"jest": {
"testRegex": "__tests__/.*\\Spec.js$",
"testPathDirs": [
"<rootDir>/src/js"
],

BIN
web/src/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

View File

@ -1,10 +1,10 @@
jest.unmock('../../ducks/ui')
jest.unmock('../../ducks/flows')
jest.unmock('../../../ducks/ui/header')
jest.unmock('../../../ducks/flows')
import reducer, { setActiveMenu } from '../../ducks/ui'
import * as flowActions from '../../ducks/flows'
import reducer, { setActiveMenu } from '../../../ducks/ui/header'
import * as flowActions from '../../../ducks/flows'
describe('ui reducer', () => {
describe('header reducer', () => {
it('should return the initial state', () => {
expect(reducer(undefined, {}).activeMenu).toEqual('Start')
})

View File

@ -66,11 +66,13 @@ describe('view reduce', () => {
it('should update item', () => {
const state = createState([
{ id: 1, val: 1 },
{ id: 2, val: 2 }
{ id: 2, val: 2 },
{ id: 3, val: 3 }
])
const result = createState([
{ id: 1, val: 1 },
{ id: 2, val: 3 }
{ id: 2, val: 3 },
{ id: 3, val: 3 }
])
expect(reduce(state, view.update({ id: 2, val: 3 }))).toEqual(result)
})

View File

@ -54,21 +54,29 @@ export default function reduce(state = defaultState, action) {
}
case UPDATE:
if (state.indexOf[action.item.id] == null) {
return
let hasOldItem = state.indexOf[action.item.id] !== null && state.indexOf[action.item.id] !== undefined
let hasNewItem = action.filter(action.item)
if (!hasNewItem && !hasOldItem) {
return state
}
const nextState = {
...state,
...sortedRemove(state, action.item.id),
if (hasNewItem && !hasOldItem) {
return {
...state,
...sortedInsert(state, action.item, action.sort)
}
}
if (!action.filter(action.item)) {
return nextState
if (!hasNewItem && hasOldItem) {
return {
...state,
...sortedRemove(state, action.item.id)
}
}
return {
...nextState,
...sortedInsert(nextState, action.item, action.sort)
if (hasNewItem && hasOldItem) {
return {
...state,
...sortedUpdate(state, action.item, action.sort),
}
}
case RECEIVE:
{
const data = action.list.filter(action.filter).sort(action.sort)
@ -110,7 +118,7 @@ export function receive(list, filter = defaultFilter, sort = defaultSort) {
function sortedInsert(state, item, sort) {
const index = sortedIndex(state.data, item, sort)
const data = [...state.data]
const data = [ ...state.data ]
const indexOf = { ...state.indexOf }
data.splice(index, 0, item)
@ -134,6 +142,28 @@ function sortedRemove(state, id) {
return { data, indexOf }
}
function sortedUpdate(state, item, sort) {
let data = [ ...state.data ]
let indexOf = { ...state.indexOf }
let index = indexOf[item.id]
data[index] = item
while (index + 1 < data.length && sort(data[index], data[index + 1]) > 0) {
data[index] = data[index + 1]
data[index + 1] = item
indexOf[item.id] = index + 1
indexOf[data[index].id] = index
++index
}
while (index > 0 && sort(data[index], data[index - 1]) < 0) {
data[index] = data[index - 1]
data[index - 1] = item
indexOf[item.id] = index - 1
indexOf[data[index].id] = index
--index
}
return { data, indexOf }
}
function sortedIndex(list, item, sort) {
let low = 0
let high = list.length

View File

@ -6,10 +6,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/static/vendor.css"/>
<link rel="stylesheet" href="/static/app.css"/>
<link rel="icon" href="/static/images/favicon.ico" type="image/x-icon"/>
<script src="/static/vendor.js"></script>
<script src="/static/app.js"></script>
</head>
<body>
<div id="mitmproxy"></div>
</body>
</html>
</html>