diff --git a/.travis.yml b/.travis.yml index b75040973..c22038fd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,8 +29,6 @@ matrix: env: TOXENV=py36 - python: 3.6 env: TOXENV=individual_coverage - - python: 3.6 - env: TOXENV=docs - language: node_js node_js: "node" before_install: diff --git a/README.rst b/README.rst index 2e5c586e9..f28ec8746 100644 --- a/README.rst +++ b/README.rst @@ -110,19 +110,18 @@ suite. The project tries to maintain 100% test coverage and enforces this strict Documentation ------------- -The mitmproxy documentation is build using Sphinx_, which is installed -automatically if you set up a development environment as described above. After -installation, you can render the documentation like this: +The following tools are required to build the mitmproxy docs: + +- Hugo_ +- modd_ +- yarn_ .. code-block:: bash cd docs - make clean - make html - make livehtml + yarn + modd -The last command invokes `sphinx-autobuild`_, which watches the Sphinx directory and rebuilds -the documentation when a change is detected. Code Style ---------- @@ -181,8 +180,9 @@ with the following command: .. _virtualenv: https://virtualenv.pypa.io/ .. _`pytest`: http://pytest.org/ .. _tox: https://tox.readthedocs.io/ -.. _Sphinx: http://sphinx-doc.org/ -.. _sphinx-autobuild: https://pypi.python.org/pypi/sphinx-autobuild +.. _Hugo: https://gohugo.io/ +.. _modd: https://github.com/cortesi/modd +.. _yarn: https://yarnpkg.com/en/ .. _PEP8: https://www.python.org/dev/peps/pep-0008 .. _`Google Style Guide`: https://google.github.io/styleguide/pyguide.html .. _forums: https://discourse.mitmproxy.org/ diff --git a/docs/.gitignore b/docs/.gitignore index 69fa449dd..0eba9c59b 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1,3 @@ -_build/ +src/public +node_modules +public diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index b82551073..000000000 --- a/docs/Makefile +++ /dev/null @@ -1,195 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# User-friendly check for sphinx-build -ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) -$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) -endif - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . -# the i18n builder cannot share the environment and doctrees with the others -I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " applehelp to make an Apple Help Book" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " texinfo to make Texinfo files" - @echo " info to make Texinfo files and run them through makeinfo" - @echo " gettext to make PO message catalogs" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " xml to make Docutils-native XML files" - @echo " pseudoxml to make pseudoxml-XML files for display purposes" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - @echo " coverage to run coverage check of the documentation (if enabled)" - -clean: - rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/mitmproxy.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/mitmproxy.qhc" - -applehelp: - $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp - @echo - @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." - @echo "N.B. You won't be able to view it unless you put it in" \ - "~/Library/Documentation/Help or install it in your application" \ - "bundle." - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/mitmproxy" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/mitmproxy" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through pdflatex..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -latexpdfja: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex - @echo "Running LaTeX files through platex and dvipdfmx..." - $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -texinfo: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo - @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." - @echo "Run \`make' in that directory to run these through makeinfo" \ - "(use \`make info' here to do that automatically)." - -info: - $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo - @echo "Running Texinfo files through makeinfo..." - make -C $(BUILDDIR)/texinfo info - @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." - -gettext: - $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale - @echo - @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." - -coverage: - $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage - @echo "Testing of coverage in the sources finished, look at the " \ - "results in $(BUILDDIR)/coverage/python.txt." - -xml: - $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml - @echo - @echo "Build finished. The XML files are in $(BUILDDIR)/xml." - -pseudoxml: - $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml - @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." - -livehtml: - sphinx-autobuild -b html -z '../mitmproxy' -r '___jb_(old|bak|tmp)___$$' $(ALLSPHINXOPTS) $(BUILDDIR)/html \ No newline at end of file diff --git a/docs/_static/theme_overrides.css b/docs/_static/theme_overrides.css deleted file mode 100644 index 849f9f250..000000000 --- a/docs/_static/theme_overrides.css +++ /dev/null @@ -1,44 +0,0 @@ - -/* override table width restrictions */ -.wy-table-responsive table td, .wy-table-responsive table th { - white-space: normal; -} - -.wy-table-responsive > table > tbody > tr > td { - vertical-align: top !important; -} - -.wy-table-responsive { - margin-bottom: 24px; - max-width: 100%; - overflow: visible; -} - -.wy-menu-vertical header, .wy-menu-vertical p.caption { - color: #e0e0e0; -} - -.code-block-caption { - height: 1.5em; -} - -.code-block-caption .caption-text { - font-size: 0.8em; - float: right; -} - -.code-block-caption .headerlink { - display: none !important; -} - -.function .headerlink { - display: none !important; -} - -dl .reference.internal { - display: none !important; -} - -dl .headerlink { - display: none !important; -} diff --git a/docs/_templates/page.html b/docs/_templates/page.html deleted file mode 100644 index 8002396ad..000000000 --- a/docs/_templates/page.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "!page.html" %} -{% block sidebartitle %} - - - - {{ super() }} -{% endblock %} \ No newline at end of file diff --git a/docs/certinstall.rst b/docs/certinstall.rst deleted file mode 100644 index 6662e34dc..000000000 --- a/docs/certinstall.rst +++ /dev/null @@ -1,211 +0,0 @@ -.. _certinstall: - -About Certificates -================== - -Introduction ------------- - -Mitmproxy can decrypt encrypted traffic on the fly, as long as the client -trusts its built-in certificate authority. Usually this means that the -mitmproxy CA certificates have to be installed on the client device. - -Quick Setup ------------ - -By far the easiest way to install the mitmproxy certificates is to use the -built-in certificate installation app. To do this, just start mitmproxy and -configure your target device with the correct proxy settings. Now start a -browser on the device, and visit the magic domain **mitm.it**. You should see -something like this: - -.. image:: certinstall-webapp.png - -Click on the relevant icon, follow the setup instructions for the platform -you're on and you are good to go. - -Installing the mitmproxy CA certificate manually ------------------------------------------------- - -Sometimes using the quick install app is not an option - Java or the iOS -Simulator spring to mind - or you just need to do it manually for some other -reason. Below is a list of pointers to manual certificate installation -documentation for some common platforms. - -The mitmproxy CA cert is located in ``~/.mitmproxy`` after it has been generated at the first -start of mitmproxy. - -iOS -^^^ - -See http://jasdev.me/intercepting-ios-traffic - -and https://web.archive.org/web/20150920082614/http://kb.mit.edu/confluence/pages/viewpage.action?pageId=152600377 - -On iOS 10.3 and onwards, you also need to enable full trust for the mitmproxy root certificate: - -1. Go to Settings > General > About > Certificate Trust Settings. -2. Under "Enable full trust for root certificates", turn on trust for the mitmproxy certificate. - -iOS Simulator -^^^^^^^^^^^^^ - -See https://github.com/ADVTOOLS/ADVTrustStore#how-to-use-advtruststore - -Java -^^^^ - -See https://docs.oracle.com/cd/E19906-01/820-4916/geygn/index.html - -Android/Android Simulator -^^^^^^^^^^^^^^^^^^^^^^^^^ - -See http://wiki.cacert.org/FAQ/ImportRootCert#Android_Phones_.26_Tablets - -Windows -^^^^^^^ - -See https://web.archive.org/web/20160612045445/http://windows.microsoft.com/en-ca/windows/import-export-certificates-private-keys#1TC=windows-7 - -Windows (automated) -^^^^^^^^^^^^^^^^^^^ - ->>> certutil.exe -importpfx Root mitmproxy-ca-cert.p12 - -See also: https://technet.microsoft.com/en-us/library/cc732443.aspx - -Mac OS X -^^^^^^^^ - -See https://support.apple.com/kb/PH7297?locale=en_US - -Ubuntu/Debian -^^^^^^^^^^^^^ - -See https://askubuntu.com/questions/73287/how-do-i-install-a-root-certificate/94861#94861 - -Mozilla Firefox -^^^^^^^^^^^^^^^ - -See https://wiki.mozilla.org/MozillaRootCertificate#Mozilla_Firefox - -Chrome on Linux -^^^^^^^^^^^^^^^ - -See https://stackoverflow.com/a/15076602/198996 - - -The mitmproxy certificate authority ------------------------------------ - -The first time **mitmproxy** or **mitmdump** is run, the mitmproxy Certificate -Authority (CA) is created in the config directory (``~/.mitmproxy`` by default). -This CA is used for on-the-fly generation of dummy certificates for each of the -SSL sites that your client visits. Since your browser won't trust the -mitmproxy CA out of the box, you will see an SSL certificate warning every -time you visit a new SSL domain through mitmproxy. When you are testing a -single site through a browser, just accepting the bogus SSL cert manually is -not too much trouble, but there are a many circumstances where you will want to -configure your testing system or browser to trust the mitmproxy CA as a -signing root authority. For security reasons, the mitmproxy CA is generated uniquely on the first start and is not shared between mitmproxy installations on different devices. - -Certificate Pinning -^^^^^^^^^^^^^^^^^^^ - -Some applications employ `Certificate Pinning`_ to prevent man-in-the-middle attacks. -This means that **mitmproxy** and **mitmdump's** certificates will not be -accepted by these applications without modifying them. It is recommended to use the -:ref:`passthrough` feature in order to prevent **mitmproxy** and **mitmdump** from intercepting -traffic to these specific domains. If you want to intercept the pinned connections, you need to patch the application manually. For Android and (jailbroken) iOS devices, various tools exist to accomplish this. - - -CA and cert files ------------------ - -The files created by mitmproxy in the .mitmproxy directory are as follows: - -===================== ========================================================================== -mitmproxy-ca.pem The certificate **and the private key** in PEM format. -mitmproxy-ca-cert.pem The certificate in PEM format. - Use this to distribute on most non-Windows platforms. -mitmproxy-ca-cert.p12 The certificate in PKCS12 format. For use on Windows. -mitmproxy-ca-cert.cer Same file as .pem, but with an extension expected by some Android devices. -===================== ========================================================================== - -Using a custom certificate --------------------------- - -You can use your own (leaf) certificate by passing the ``--cert [domain=]path_to_certificate`` option to -mitmproxy. Mitmproxy then uses the provided certificate for interception of the -specified domain instead of generating a certificate signed by its own CA. - -The certificate file is expected to be in the PEM format. You can include -intermediary certificates right below your leaf certificate, so that your PEM -file roughly looks like this: - -.. code-block:: none - - -----BEGIN PRIVATE KEY----- - - -----END PRIVATE KEY----- - -----BEGIN CERTIFICATE----- - - -----END CERTIFICATE----- - -----BEGIN CERTIFICATE----- - - -----END CERTIFICATE----- - - -For example, you can generate a certificate in this format using these instructions: - - ->>> openssl genrsa -out cert.key 2048 ->>> openssl req -new -x509 -key cert.key -out cert.crt - (Specify the mitm domain as Common Name, e.g. *.google.com) ->>> cat cert.key cert.crt > cert.pem - -Now, you can run mitmproxy with the generated certificate: - -**For all domain names** - -``>>>mitmproxy --cert *=cert.pem`` - -**For specific domain names** - -``>>>mitmproxy --cert *.example.com=cert.pem`` - -**Note:** ``*.example.com`` is for all the subdomains. You can also use ``www.example.com`` for a particular subdomain. - - -Using a custom certificate authority ------------------------------------- - -By default, mitmproxy will use ``~/.mitmproxy/mitmproxy-ca.pem`` as -the certificate authority to generate certificates for all domains for which no -custom certificate is provided (see above). You can use your own certificate -authority by passing the ``--cadir DIRECTORY`` option to mitmproxy. Mitmproxy -will then look for ``mitmproxy-ca.pem`` in the specified directory. If -no such file exists, it will be generated automatically. - - -Using a client side certificate -------------------------------- - -You can use a client certificate by passing the ``--client-certs DIRECTORY|FILE`` -option to mitmproxy. Using a directory allows certs to be selected based on -hostname, while using a filename allows a single specific certificate to be used for -all SSL connections. Certificate files must be in the PEM format and should -contain both the unencrypted private key and the certificate. - -Multiple certs by Hostname -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you've specified a directory to ``--client-certs``, then the following -behavior will be taken: - -If you visit example.org, mitmproxy looks for a file named ``example.org.pem`` in the specified -directory and uses this as the client cert. - - - -.. _Certificate Pinning: https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 45840e6e8..000000000 --- a/docs/conf.py +++ /dev/null @@ -1,243 +0,0 @@ -import importlib -import inspect -import os -import subprocess -import sys - -sys.path.insert(0, os.path.abspath('..')) -from mitmproxy import version as mversion - - -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.extlinks', - 'sphinx.ext.linkcode', - 'sphinx.ext.viewcode', - 'sphinx.ext.napoleon', - 'sphinxcontrib.documentedlist' -] - -# https://github.com/sphinx-doc/sphinx/pull/2053 -napoleon_include_special_with_doc = False - -autodoc_member_order = "bysource" - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix(es) of source filenames. -# You can specify multiple suffix as a list of string: -# source_suffix = ['.rst', '.md'] -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'mitmproxy docs' -copyright = u'2016, the mitmproxy project' -author = u'The mitmproxy project' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = mversion.VERSION -# The full version, including alpha/beta/rc tags. -release = mversion.VERSION - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -# -# This is also used if you do content translation via gettext catalogs. -# Usually you set "language" from the command line for these cases. -language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -modindex_common_prefix = ['mitmproxy.'] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - -# If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'sphinx_rtd_theme' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -html_theme_options = { - 'logo_only': True, -} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -html_title = "mitmproxy %s documentation" % version - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -html_logo = "mitmproxy-docs.png" - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -html_favicon = "favicon.ico" - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -html_show_sourcelink = False - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Language to be used for generating the HTML full-text search index. -# Sphinx supports the following languages: -# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' -# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' -#html_search_language = 'en' - -# A dictionary with options for the search language support, empty by default. -# Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} - -# The name of a javascript file (relative to the configuration directory) that -# implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'mitmproxydoc' - -last_tag, tag_dist, commit = ( - subprocess.check_output(["git", "describe", "--tags", "--long"]) - .decode() - .strip() - .rsplit("-", 2) -) -tag_dist = int(tag_dist) -if tag_dist == 0: - tag = last_tag -else: - tag = "master" - -SRCBASE = "https://github.com/mitmproxy/mitmproxy/blob/{}".format(tag) - -extlinks = dict( - src = (SRCBASE + r"/%s", '') -) - - -def linkcode_resolve(domain, info): - if domain != 'py': - return None - module, fullname = info['module'], info['fullname'] - if not module: - return None - obj = importlib.import_module(module) - for item in fullname.split('.'): - obj = getattr(obj, item, None) - if obj is None: - return None - try: - spath = inspect.getsourcefile(obj) - _, line = inspect.getsourcelines(obj) - except (TypeError, IOError): - return None - if spath.rfind("mitmproxy") > -1: - off = spath.rfind("mitmproxy") - mpath = spath[off:] - else: - return None - return SRCBASE + "/%s#L%s" % (mpath, line) - - -def setup(app): - app.add_stylesheet('theme_overrides.css') diff --git a/docs/config.rst b/docs/config.rst deleted file mode 100644 index 1b0921ecc..000000000 --- a/docs/config.rst +++ /dev/null @@ -1,13 +0,0 @@ -.. _config: - -Configuration -============= - -Mitmproxy is configured with a YAML_ file, located at -``~/.mitmproxy/config.yaml``. We'll have complete documentation for all -supported options in the next release in the meantime, please consult the -source_ for a complete list of options and types. - - -.. _YAML: http://www.yaml.org/start.html -.. _source: https://github.com/mitmproxy/mitmproxy/blob/master/mitmproxy/options.py diff --git a/docs/custom-routing.txt b/docs/custom-routing.txt deleted file mode 100644 index 2ba2281fc..000000000 --- a/docs/custom-routing.txt +++ /dev/null @@ -1,24 +0,0 @@ -# Adapted from http://tldp.org/HOWTO/TransparentProxy-6.html (6.2 Second method) -# Note that the choice of firewall mark (3) and routing table (2) was fairly arbitrary. -# If you are already using policy routing or firewall marking for some other purpose, -# make sure you choose unique numbers here. Otherwise, don't worry about it. - - - -# On the router, run - -PROXY_IP=192.168.1.100 -TARGET_IP=192.168.1.110 - -iptables -t mangle -A PREROUTING -j ACCEPT -p tcp -m multiport --dports 80,443 -s ! $TARGET_IP -# Alternative to MITM the whole network: -# iptables -t mangle -A PREROUTING -j ACCEPT -p tcp -m multiport --dports 80,443 -s $PROXY_IP -iptables -t mangle -A PREROUTING -j MARK --set-mark 3 -p tcp -m multiport --dports 80,443 -ip rule add fwmark 3 table 2 -ip route add default via $PROXY_IP dev br0 table 2 - - - -# On the proxy machine, run - -iptables -A PREROUTING -t nat -i eth0 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-port 8080 diff --git a/docs/dev/addingviews.html b/docs/dev/addingviews.html deleted file mode 100644 index f6ba645c6..000000000 --- a/docs/dev/addingviews.html +++ /dev/null @@ -1,52 +0,0 @@ -As discussed in [the Flow View section of the mitmproxy -overview](@!urlTo("mitmproxy.html")!@), mitmproxy allows you to inspect and -manipulate flows. When inspecting a single flow, mitmproxy uses a number of -heuristics to show a friendly view of various content types; if mitmproxy -cannot show a friendly view, mitmproxy defaults to a __raw__ view. - -Each content type invokes a different flow viewer to parse the data and display -the friendly view. Users can add custom content viewers by adding a view class -to contentview.py, discussed below. - -## Adding a new View class to contentview.py - -The content viewers used by mitmproxy to present a friendly view of various -content types are stored in contentview.py. Reviewing this file shows a number -of classes named ViewSomeDataType, each with the properties: __name__, -__prompt__, and __content\_types__ and a function named __\_\_call\_\___. - -Adding a new content viewer to parse a data type is as simple as writing a new -View class. Your new content viewer View class should have the same properties -as the other View classes: __name__, __prompt__, and __content\_types__ and a -__\_\_call\_\___ function to parse the content of the request/response. - -* The __name__ property should be a string describing the contents and new content viewer; -* The __prompt__ property should be a two item tuple: - - - __1__: A string that will be used to display the new content viewer's type; and - - __2__: A one character string that will be the hotkey used to select the new content viewer from the Flow View screen; - -* The __content\_types__ property should be a list of strings of HTTP Content\-Types that the new content viewer can parse. - * Note that mitmproxy will use the content\_types to try and heuristically show a friendly view of content and that you can override the built-in views by populating content\_types with values for content\_types that are already parsed -- e.g. "image/png". - -After defining the __name__, __prompt__, and __content\_types__ properties of -the class, you should write the __\_\_call\_\___ function, which will parse the -request/response data and provide a friendly view of the data. The -__\_\_call\_\___ function should take the following arguments: __self__, -__hdrs__, __content__, __limit__; __hdrs__ is a MultiDict object containing -the headers of the request/response; __content__ is the content of the -request/response, and __limit__ is an integer representing the amount of data -to display in the view window. - -The __\_\_call\_\___ function returns two values: (1) a string describing the -parsed data; and (2) the parsed data for friendly display. The parsed data to -be displayed should be a list of strings formatted for display. You can use -the __\_view\_text__ function in contentview.py to format text for display. -Alternatively, you can display content as a series of key-value pairs; to do -so, prepare a list of lists, where each list item is a two item list -- a key -that describes the data, and then the data itself; after preparing the list of -lists, use the __common.format\_keyvals__ function on it to prepare it as text -for display. - -If the new content viewer fails or throws an exception, mitmproxy will default -to a __raw__ view. diff --git a/docs/dev/contributing.rst b/docs/dev/contributing.rst deleted file mode 100644 index 8e04c270c..000000000 --- a/docs/dev/contributing.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. _contributing: - -Contributing -============ - -As an open source project, **mitmproxy** welcomes contributions of all forms. - -Please head over to the README_ to get started! 😃 - - -.. _README: https://github.com/mitmproxy/mitmproxy/blob/master/README.rst \ No newline at end of file diff --git a/docs/dev/sslkeylogfile.rst b/docs/dev/sslkeylogfile.rst deleted file mode 100644 index cb1f8f094..000000000 --- a/docs/dev/sslkeylogfile.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _sslkeylogfile: - -TLS Master Secrets -================== - -The SSL master keys can be logged by mitmproxy so that external programs can decrypt TLS -connections both from and to the proxy. Key logging is enabled by setting the environment variable -:envvar:`SSLKEYLOGFILE` so that it points to a writable text file. -Recent versions of WireShark can use these log files to decrypt packets. -You can specify the key file path in WireShark via - -:samp:`Edit -> Preferences -> Protocols -> SSL -> (Pre)-Master-Secret log filename`. - -Note that :envvar:`SSLKEYLOGFILE` is respected by other programs as well, e.g. Firefox and Chrome. -If this creates any issues, you can set :envvar:`MITMPROXY_SSLKEYLOGFILE` alternatively. diff --git a/docs/favicon.ico b/docs/favicon.ico deleted file mode 100644 index 3c3b891cd..000000000 Binary files a/docs/favicon.ico and /dev/null differ diff --git a/docs/features/anticache.rst b/docs/features/anticache.rst deleted file mode 100644 index a0c3187a3..000000000 --- a/docs/features/anticache.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _anticache: - -Anticache -========= -When the ``--anticache`` option is passed to mitmproxy, it removes headers -(``if-none-match`` and ``if-modified-since``) that might elicit a -``304 not modified`` response from the server. This is useful when you want to make -sure you capture an HTTP exchange in its totality. It's also often used during -:ref:`clientreplay`, when you want to make sure the server responds with complete data. - - -================== ====================== -command-line ``--anticache`` -mitmproxy shortcut :kbd:`O` then :kbd:`a` -================== ====================== diff --git a/docs/features/clientreplay.rst b/docs/features/clientreplay.rst deleted file mode 100644 index ebe40b5f8..000000000 --- a/docs/features/clientreplay.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _clientreplay: - -Client-side replay -================== - -Client-side replay does what it says on the tin: you provide a previously saved -HTTP conversation, and mitmproxy replays the client requests one by one. Note -that mitmproxy serializes the requests, waiting for a response from the server -before starting the next request. This might differ from the recorded -conversation, where requests may have been made concurrently. - -You may want to use client-side replay in conjunction with the -:ref:`anticache` option, to make sure the server responds with complete data. - -================== =========== -command-line ``-c path`` -mitmproxy shortcut :kbd:`R` then :kbd:`c` -================== =========== diff --git a/docs/features/filters.rst b/docs/features/filters.rst deleted file mode 100644 index e531f7346..000000000 --- a/docs/features/filters.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. _filters: - -Filter expressions -================== - -Many commands in :program:`mitmproxy` and :program:`mitmdump` take a filter expression. -Filter expressions consist of the following operators: - -.. documentedlist:: - :header: "Expression" "Description" - :listobject: mitmproxy.flowfilter.help - -- Regexes are Python-style -- Regexes can be specified as quoted strings -- Header matching (~h, ~hq, ~hs) is against a string of the form "name: value". -- Strings with no operators are matched against the request URL. -- The default binary operator is &. - -Examples --------- - -URL containing "google.com": - -.. code-block:: none - - google\.com - -Requests whose body contains the string "test": - -.. code-block:: none - - ~q ~b test - -Anything but requests with a text/html content type: - -.. code-block:: none - - !(~q & ~t "text/html") diff --git a/docs/features/passthrough.rst b/docs/features/passthrough.rst deleted file mode 100644 index 91fcb9b6e..000000000 --- a/docs/features/passthrough.rst +++ /dev/null @@ -1,102 +0,0 @@ -.. _passthrough: - -Ignore Domains -============== - -There are two main reasons why you may want to exempt some traffic from mitmproxy's interception -mechanism: - -- **Certificate pinning:** Some traffic is is protected using `Certificate Pinning`_ and - mitmproxy's interception leads to errors. For example, the Twitter app, Windows Update or - the Apple App Store fail to work if mitmproxy is active. -- **Convenience:** You really don't care about some parts of the traffic and just want them to go - away. Note that mitmproxy's "Limit" option is often the better alternative here, as it is - not affected by the limitations listed below. - -If you want to peek into (SSL-protected) non-HTTP connections, check out the :ref:`tcp_proxy` -feature. -If you want to ignore traffic from mitmproxy's processing because of large response bodies, -take a look at the :ref:`streaming` feature. - -How it works ------------- - -================== ====================== -command-line ``--ignore regex`` -mitmproxy shortcut :kbd:`O` then :kbd:`I` -================== ====================== - - -mitmproxy allows you to specify a regex which is matched against a ``host:port`` string -(e.g. "example.com:443") to determine hosts that should be excluded. - -Limitations ------------ - -There are two important quirks to consider: - -- **In transparent mode, the ignore pattern is matched against the IP and ClientHello SNI host.** While we usually infer the - hostname from the Host header if the ``--host`` argument is passed to mitmproxy, we do not - have access to this information before the SSL handshake. If the client uses SNI however, then we treat the SNI host as an ignore target. -- **In regular and upstream proxy mode, explicit HTTP requests are never ignored.** [#explicithttp]_ The ignore pattern is - applied on CONNECT requests, which initiate HTTPS or clear-text WebSocket connections. - -Tutorial --------- - -If you just want to ignore one specific domain, there's usually a bulletproof method to do so: - -1. Run mitmproxy or mitmdump in verbose mode (``-v``) and observe the ``host:port`` - information in the serverconnect messages. mitmproxy will filter on these. -2. Take the ``host:port`` string, surround it with ^ and $, escape all dots (. becomes \\.) - and use this as your ignore pattern: - -.. code-block:: none - :emphasize-lines: 6,7,9 - - >>> mitmdump -v - 127.0.0.1:50588: clientconnect - 127.0.0.1:50588: request - -> CONNECT example.com:443 HTTP/1.1 - 127.0.0.1:50588: Set new server address: example.com:443 - 127.0.0.1:50588: serverconnect - -> example.com:443 - ^C - >>> mitmproxy --ignore ^example\.com:443$ - - -Here are some other examples for ignore patterns: - -.. code-block:: none - - # Exempt traffic from the iOS App Store (the regex is lax, but usually just works): - --ignore apple.com:443 - # "Correct" version without false-positives: - --ignore '^(.+\.)?apple\.com:443$' - - # Ignore example.com, but not its subdomains: - --ignore '^example.com:' - - # Ignore everything but example.com and mitmproxy.org: - --ignore '^(?!example\.com)(?!mitmproxy\.org)' - - # Transparent mode: - --ignore 17\.178\.96\.59:443 - # IP address range: - --ignore 17\.178\.\d+\.\d+:443 - - -.. seealso:: - - - :ref:`tcp_proxy` - - :ref:`streaming` - - mitmproxy's "Limit" feature - -.. rubric:: Footnotes - -.. [#explicithttp] This stems from an limitation of explicit HTTP proxying: - A single connection can be re-used for multiple target domains - a - ``GET http://example.com/`` request may be followed by a ``GET http://evil.com/`` request on the - same connection. If we start to ignore the connection after the first request, - we would miss the relevant second one. -.. _Certificate Pinning: https://security.stackexchange.com/questions/29988/what-is-certificate-pinning diff --git a/docs/features/proxyauth.rst b/docs/features/proxyauth.rst deleted file mode 100644 index afdbb6390..000000000 --- a/docs/features/proxyauth.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _proxyauth: - -Proxy Authentication -==================== - - -Asks the user for authentication before they are permitted to use the proxy. -Authentication headers are stripped from the flows, so they are not passed to -upstream servers. For now, only HTTP Basic authentication is supported. The -proxy auth options are not compatible with the transparent, socks or reverse proxy -mode. - -================== ====================== -command-line ``--nonanonymous``, - ``--singleuser USER``, - ``--htpasswd PATH`` -================== ====================== diff --git a/docs/features/replacements.rst b/docs/features/replacements.rst deleted file mode 100644 index 39dccca23..000000000 --- a/docs/features/replacements.rst +++ /dev/null @@ -1,71 +0,0 @@ -.. _replacements: - -Replacements -============ - -Mitmproxy lets you specify an arbitrary number of patterns that define text -replacements within flows. Each pattern has 3 components: a filter that defines -which flows a replacement applies to, a regular expression that defines what -gets replaced, and a target value that defines what is substituted in. - -Replace hooks fire when either a client request or a server response is -received. Only the matching flow component is affected: so, for example, if a -replace hook is triggered on server response, the replacement is only run on -the Response object leaving the Request intact. You control whether the hook -triggers on the request, response or both using the filter pattern. If you need -finer-grained control than this, it's simple to create a script using the -replacement API on Flow components. - -Replacement hooks are extremely handy in interactive testing of applications. -For instance you can use a replace hook to replace the text "XSS" with a -complicated XSS exploit, and then "inject" the exploit simply by interacting -with the application through the browser. When used with tools like Firebug and -mitmproxy's own interception abilities, replacement hooks can be an amazingly -flexible and powerful feature. - - -On the command-line -------------------- - -The replacement hook command-line options use a compact syntax to make it easy -to specify all three components at once. The general form is as follows: - -.. code-block:: none - - /patt/regex/replacement - -Here, **patt** is a mitmproxy filter expression, **regex** is a valid Python -regular expression, and **replacement** is a string literal. The first -character in the expression (``/`` in this case) defines what the separation -character is. Here's an example of a valid expression that replaces "foo" with -"bar" in all requests: - -.. code-block:: none - - :~q:foo:bar - -In practice, it's pretty common for the replacement literal to be long and -complex. For instance, it might be an XSS exploit that weighs in at hundreds or -thousands of characters. To cope with this, there's a variation of the -replacement hook specifier that lets you load the replacement text from a file. -To specify a file as replacement, prefix the file path with ``@``. -You might start **mitmdump** as follows: - ->>> mitmdump --replacements :~q:foo:@~/xss-exploit - -This will load the replacement text from the file ``~/xss-exploit``. - -The ``--replacements`` flag can be passed multiple times. - - -Interactively -------------- - -The :kbd:`R` shortcut key in the mitmproxy options menu (:kbd:`O`) lets you add and edit -replacement hooks using a built-in editor. The context-sensitive help (:kbd:`?`) has -complete usage information. - -================== ======================= -command-line ``--replacements`` -mitmproxy shortcut :kbd:`O` then :kbd:`R` -================== ======================= diff --git a/docs/features/reverseproxy.rst b/docs/features/reverseproxy.rst deleted file mode 100644 index 57b353ae1..000000000 --- a/docs/features/reverseproxy.rst +++ /dev/null @@ -1,43 +0,0 @@ -.. _reverseproxy: - -Reverse Proxy -============= - -In reverse proxy mode, mitmproxy accepts standard HTTP(S) requests and forwards -them to the specified upstream server. This is in contrast to :ref:`upstreamproxy`, in which -mitmproxy forwards HTTP(S) proxy requests to an upstream proxy server. - -================== ================================ -command-line ``-R http[s]://hostname[:port]`` -================== ================================ - -Here, **http[s]** signifies if the proxy should use TLS to connect to the server. -mitmproxy always accepts both encrypted and unencrypted requests and transforms -them to what the server expects. - -.. code-block:: none - - >>> mitmdump -R https://httpbin.org -p 80 - >>> curl http://localhost/ - # requests will be transparently upgraded to TLS by mitmproxy - - >>> mitmdump -R https://httpbin.org -p 443 - >>> curl https://localhost/ - # mitmproxy will use TLS on both ends. - - -Host Header ------------ - -In reverse proxy mode, mitmproxy automatically rewrites the Host header to match the -upstream server. This allows mitmproxy to easily connect to existing endpoints on the -open web (e.g. ``mitmproxy -R https://example.com``). You can disable this behaviour -by passing ``--keep-host-header`` on the console. - -However, keep in mind that absolute URLs within the returned document or HTTP redirects will -NOT be rewritten by mitmproxy. This means that if you click on a link for "http://example.com" -in the returned web page, you will be taken directly to that URL, bypassing mitmproxy. - -One possible way to address this is to modify the hosts file of your OS so that "example.com" -resolves to your proxy's IP, and then access the proxy by going directly to example.com. -Make sure that your proxy can still resolve the original IP, or specify an IP in mitmproxy. diff --git a/docs/features/serverreplay.rst b/docs/features/serverreplay.rst deleted file mode 100644 index aef0296e4..000000000 --- a/docs/features/serverreplay.rst +++ /dev/null @@ -1,52 +0,0 @@ -.. _serverreplay: - -Server-side replay -================== - -Server-side replay lets us replay server responses from a saved HTTP -conversation. - -Matching requests with responses --------------------------------- - -By default, :program:`mitmproxy` excludes request headers when matching incoming -requests with responses from the replay file. This works in most circumstances, -and makes it possible to replay server responses in situations where request -headers would naturally vary, e.g. using a different user agent. -The ``--rheader headername`` command-line option allows you to override -this behaviour by specifying individual headers that should be included in matching. - - -Response refreshing -------------------- - -Simply replaying server responses without modification will often result in -unexpected behaviour. For example cookie timeouts that were in the future at -the time a conversation was recorded might be in the past at the time it is -replayed. By default, :program:`mitmproxy` refreshes server responses before sending -them to the client. The **date**, **expires** and **last-modified** headers are -all updated to have the same relative time offset as they had at the time of -recording. So, if they were in the past at the time of recording, they will be -in the past at the time of replay, and vice versa. Cookie expiry times are -updated in a similar way. - -You can turn off response refreshing using the ``--norefresh`` argument, or using -the :kbd:`O` options shortcut within :program:`mitmproxy`. - - -Replaying a session recorded in Reverse-proxy Mode --------------------------------------------------- - -If you have captured the session in reverse proxy mode, in order to replay it you -still have to specify the server URL, otherwise you may get the error: -'HTTP protocol error in client request: Invalid HTTP request form (expected authority or absolute...)'. - -During replay, when the client's requests match previously recorded requests, then the -respective recorded responses are simply replayed by mitmproxy. -Otherwise, the unmatched requests is forwarded to the upstream server. -If forwarding is not desired, you can use the --kill (-k) switch to prevent that. - -================== =========== -command-line ``-S path`` -mitmproxy shortcut :kbd:`R` then :kbd:`s` -================== =========== diff --git a/docs/features/setheaders.rst b/docs/features/setheaders.rst deleted file mode 100644 index 486f8c763..000000000 --- a/docs/features/setheaders.rst +++ /dev/null @@ -1,19 +0,0 @@ -.. _setheaders: - -Set Headers -=========== - -This feature lets you specify a set of headers to be added to requests or -responses, based on a filter pattern. You can specify these either on the -command-line, or through an interactive editor in mitmproxy. - -Example: Set the **Host** header to "example.com" for all requests. - -.. code-block:: none - - mitmdump -R http://example.com --setheader :~q:Host:example.com - -================== ======================= -command-line ``--setheader PATTERN`` -mitmproxy shortcut :kbd:`O` then :kbd:`H` -================== ======================= diff --git a/docs/features/socksproxy.rst b/docs/features/socksproxy.rst deleted file mode 100644 index e1686f457..000000000 --- a/docs/features/socksproxy.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. _socksproxy: - -SOCKS Mode -========== - -In this mode, mitmproxy acts as a SOCKS5 proxy server. - -================== =========== -command-line ``--socks`` -================== =========== diff --git a/docs/features/sticky.rst b/docs/features/sticky.rst deleted file mode 100644 index 5cf32299b..000000000 --- a/docs/features/sticky.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. _sticky: - -Sticky cookies and auth -======================= - -Sticky cookies --------------- - -When the sticky cookie option is set, __mitmproxy__ will add the cookie most -recently set by the server to any cookie-less request. Consider a service that -sets a cookie to track the session after authentication. Using sticky cookies, -you can fire up mitmproxy, and authenticate to a service as you usually would -using a browser. After authentication, you can request authenticated resources -through mitmproxy as if they were unauthenticated, because mitmproxy will -automatically add the session tracking cookie to requests. Among other things, -this lets you script interactions with authenticated resources (using tools -like wget or curl) without having to worry about authentication. - -Sticky cookies are especially powerful when used in conjunction with :ref:`clientreplay` - you can -record the authentication process once, and simply replay it on startup every time you need -to interact with the secured resources. - -================== ====================== -command-line ``-t FILTER`` -mitmproxy shortcut :kbd:`O` then :kbd:`t` -================== ====================== - - -Sticky auth ------------ - -The sticky auth option is analogous to the sticky cookie option, in that HTTP -**Authorization** headers are simply replayed to the server once they have been -seen. This is enough to allow you to access a server resource using HTTP Basic -authentication through the proxy. Note that :program:`mitmproxy` doesn't (yet) support -replay of HTTP Digest authentication. - -================== ====================== -command-line ``-u FILTER`` -mitmproxy shortcut :kbd:`O` then :kbd:`A` -================== ====================== diff --git a/docs/features/streaming.rst b/docs/features/streaming.rst deleted file mode 100644 index 82510843b..000000000 --- a/docs/features/streaming.rst +++ /dev/null @@ -1,102 +0,0 @@ -.. _streaming: - -HTTP Streaming -============== - -By default, mitmproxy will read the entire request/response, perform any indicated -manipulations on it and then send the (possibly modified) message to -the other party. In some cases this is undesirable and you may wish to "stream" -the request/response. When streaming is enabled, the request/response is -not buffered on the proxy but directly sent to the server/client instead. -HTTP headers are still fully buffered before being sent. - -Request Streaming ------------------ - -Request streaming can be used to incrementally stream a request body to the server -before it has been fully received by the proxy. This is useful for large file uploads. - -Response Streaming ------------------- - -By using mitmproxy's streaming feature, response contents can be passed to the client incrementally -before they have been fully received by the proxy. This is especially useful for large binary files -such as videos, where buffering the whole file slows down the client's browser. - -On the command-line -------------------- - -Streaming can be enabled on the command line for all request and response bodies exceeding a certain size. -The SIZE argument understands k/m/g suffixes, e.g. 3m for 3 megabytes. - -================== ================= -command-line ``--set stream_large_bodies=SIZE`` -================== ================= - -.. warning:: - - When streaming is enabled, **streamed request/response contents will not be - recorded or preserved in any way.** - -.. note:: - - When streaming is enabled, the request/response body cannot be modified by the usual means. - -Customizing Streaming ---------------------- - -You can also use a script to customize exactly which requests or responses are streamed. - -Requests/Responses that should be tagged for streaming by setting their ``.stream`` -attribute to ``True``: - -.. literalinclude:: ../../examples/complex/stream.py - :caption: examples/complex/stream.py - :language: python - -Implementation Details ----------------------- - -When response streaming is enabled, portions of the code which would have otherwise performed -changes on the request/response body will see an empty body. Any modifications will be ignored. - -Streamed bodies are usually sent in chunks of 4096 bytes. If the response is sent with a -``Transfer-Encoding: chunked`` header, the response will be streamed one chunk at a time. - -Modifying streamed data ------------------------ - -If the ``.stream`` attribute is callable, ``.stream`` will wrap the generator that yields all -chunks. - -.. literalinclude:: ../../examples/complex/stream_modify.py - :caption: examples/complex/stream_modify.py - :language: python - -WebSocket Streaming -=================== - -The WebSocket streaming feature can be used to send the frames as soon as they arrive. This can be useful for large binary file transfers. - -On the command-line -------------------- - -Streaming can be enabled on the command line for all WebSocket frames - -================== ================= -command-line ``--set stream_websockets=true`` -================== ================= - -.. note:: - - When Web Socket streaming is enabled, the message payload cannot be modified. - -Implementation Details ----------------------- -When WebSocket streaming is enabled, portions of the code which may perform changes to the WebSocket message payloads will not have -any effect on the actual payload sent to the server as the frames are immediately forwarded to the server. -In contrast to HTTP streaming, where the body is not stored, the message payload will still be stored in the WebSocket Flow. - -.. seealso:: - - - :ref:`passthrough` diff --git a/docs/features/upstreamcerts.rst b/docs/features/upstreamcerts.rst deleted file mode 100644 index 4ef79e1be..000000000 --- a/docs/features/upstreamcerts.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _upstreamcerts: - -Upstream Certificates -===================== - -When mitmproxy receives a connection destined for an SSL-protected service, it -freezes the connection before reading its request data, and makes a connection -to the upstream server to "sniff" the contents of its SSL certificate. The -information gained - the **Common Name** and **Subject Alternative Names** - is -then used to generate the interception certificate, which is sent to the client -so the connection can continue. - -This rather intricate little dance lets us seamlessly generate correct -certificates even if the client has specified only an IP address rather than the -hostname. It also means that we don't need to sniff additional data to generate -certs in transparent mode. - -Upstream cert sniffing is on by default, and can optionally be turned off. - -================== ====================== -command-line ``--no-upstream-cert`` -mitmproxy shortcut :kbd:`O` then :kbd:`U` -================== ====================== diff --git a/docs/features/upstreamproxy.rst b/docs/features/upstreamproxy.rst deleted file mode 100644 index a4ccf57ff..000000000 --- a/docs/features/upstreamproxy.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. _upstreamproxy: - -Upstream proxy mode -=================== - -In this mode, mitmproxy accepts proxy requests and unconditionally forwards all -requests to a specified upstream proxy server. This is in contrast to :ref:`reverseproxy`, -in which mitmproxy forwards ordinary HTTP requests to an upstream server. - -================== ============================= -command-line ``-U http://hostname[:port]`` -================== ============================= diff --git a/docs/howmitmproxy.rst b/docs/howmitmproxy.rst deleted file mode 100644 index 4f3c804e4..000000000 --- a/docs/howmitmproxy.rst +++ /dev/null @@ -1,240 +0,0 @@ -How mitmproxy works -=================== - -Mitmproxy is an enormously flexible tool. Knowing exactly how the proxying -process works will help you deploy it creatively, and take into account its -fundamental assumptions and how to work around them. This document explains -mitmproxy's proxy mechanism in detail, starting with the simplest unencrypted -explicit proxying, and working up to the most complicated interaction - -transparent proxying of TLS-protected traffic [#tls]_ in the presence of `Server -Name Indication`_. - -Explicit HTTP -------------- - -Configuring the client to use mitmproxy as an explicit proxy is the simplest and -most reliable way to intercept traffic. The proxy protocol is codified in the -`HTTP RFC`_, so the behaviour of both the client and the server is well defined, -and usually reliable. In the simplest possible interaction with mitmproxy, a -client connects directly to the proxy, and makes a request that looks like this: - -.. code-block:: none - - GET http://example.com/index.html HTTP/1.1 - -This is a proxy GET request - an extended form of the vanilla HTTP GET request -that includes a schema and host specification, and it includes all the -information mitmproxy needs to proceed. - -.. image:: schematics/how-mitmproxy-works-explicit.png - :align: center - -1. The client connects to the proxy and makes a request. -2. Mitmproxy connects to the upstream server and simply forwards the request on. - - -Explicit HTTPS --------------- - -The process for an explicitly proxied HTTPS connection is quite different. The -client connects to the proxy and makes a request that looks like this: - -.. code-block:: none - - CONNECT example.com:443 HTTP/1.1 - -A conventional proxy can neither view nor manipulate a TLS-encrypted data -stream, so a CONNECT request simply asks the proxy to open a pipe between the -client and server. The proxy here is just a facilitator - it blindly forwards -data in both directions without knowing anything about the contents. The -negotiation of the TLS connection happens over this pipe, and the subsequent -flow of requests and responses are completely opaque to the proxy. - -The MITM in mitmproxy -^^^^^^^^^^^^^^^^^^^^^ - -This is where mitmproxy's fundamental trick comes into play. The MITM in its -name stands for Man-In-The-Middle - a reference to the process we use to -intercept and interfere with these theoretically opaque data streams. The basic -idea is to pretend to be the server to the client, and pretend to be the client -to the server, while we sit in the middle decoding traffic from both sides. The -tricky part is that the `Certificate Authority`_ system is designed to prevent -exactly this attack, by allowing a trusted third-party to cryptographically sign -a server's certificates to verify that they are legit. If this signature doesn't -match or is from a non-trusted party, a secure client will simply drop the -connection and refuse to proceed. Despite the many shortcomings of the CA system -as it exists today, this is usually fatal to attempts to MITM a TLS connection -for analysis. Our answer to this conundrum is to become a trusted Certificate -Authority ourselves. Mitmproxy includes a full CA implementation that generates -interception certificates on the fly. To get the client to trust these -certificates, we :ref:`register mitmproxy as a trusted CA with the device -manually `. - -Complication 1: What's the remote hostname? -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To proceed with this plan, we need to know the domain name to use in the -interception certificate - the client will verify that the certificate is for -the domain it's connecting to, and abort if this is not the case. At first -blush, it seems that the CONNECT request above gives us all we need - in this -example, both of these values are "example.com". But what if the client had -initiated the connection as follows: - -.. code-block:: none - - CONNECT 10.1.1.1:443 HTTP/1.1 - -Using the IP address is perfectly legitimate because it gives us enough -information to initiate the pipe, even though it doesn't reveal the remote -hostname. - -Mitmproxy has a cunning mechanism that smooths this over - :ref:`upstream -certificate sniffing `. As soon as we see the CONNECT request, we -pause the client part of the conversation, and initiate a simultaneous -connection to the server. We complete the TLS handshake with the server, and -inspect the certificates it used. Now, we use the Common Name in the upstream -certificates to generate the dummy certificate for the client. Voila, we have -the correct hostname to present to the client, even if it was never specified. - - -Complication 2: Subject Alternative Name -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Enter the next complication. Sometimes, the certificate Common Name is not, in -fact, the hostname that the client is connecting to. This is because of the -optional `Subject Alternative Name`_ field in the certificate that allows an -arbitrary number of alternative domains to be specified. If the expected domain -matches any of these, the client will proceed, even though the domain doesn't -match the certificate CN. The answer here is simple: when we extract the CN from -the upstream cert, we also extract the SANs, and add them to the generated dummy -certificate. - - -Complication 3: Server Name Indication -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -One of the big limitations of vanilla TLS is that each certificate requires its -own IP address. This means that you couldn't do virtual hosting where multiple -domains with independent certificates share the same IP address. In a world with -a rapidly shrinking IPv4 address pool this is a problem, and we have a solution -in the form of the `Server Name Indication`_ extension to the TLS protocols. -This lets the client specify the remote server name at the start of the TLS -handshake, which then lets the server select the right certificate to complete -the process. - -SNI breaks our upstream certificate sniffing process, because when we connect -without using SNI, we get served a default certificate that may have nothing to -do with the certificate expected by the client. The solution is another tricky -complication to the client connection process. After the client connects, we -allow the TLS handshake to continue until just **after** the SNI value has been -passed to us. Now we can pause the conversation, and initiate an upstream -connection using the correct SNI value, which then serves us the correct -upstream certificate, from which we can extract the expected CN and SANs. - -Putting it all together -^^^^^^^^^^^^^^^^^^^^^^^ - -Lets put all of this together into the complete explicitly proxied HTTPS flow. - -.. image:: schematics/how-mitmproxy-works-explicit-https.png - :align: center - -1. The client makes a connection to mitmproxy, and issues an HTTP CONNECT request. -2. Mitmproxy responds with a ``200 Connection Established``, as if it has set up the CONNECT pipe. -3. The client believes it's talking to the remote server, and initiates the TLS connection. - It uses SNI to indicate the hostname it is connecting to. -4. Mitmproxy connects to the server, and establishes a TLS connection using the SNI hostname - indicated by the client. -5. The server responds with the matching certificate, which contains the CN and SAN values - needed to generate the interception certificate. -6. Mitmproxy generates the interception cert, and continues the - client TLS handshake paused in step 3. -7. The client sends the request over the established TLS connection. -8. Mitmproxy passes the request on to the server over the TLS connection initiated in step 4. - -Transparent HTTP ----------------- - -When a transparent proxy is used, the connection is redirected into a proxy at -the network layer, without any client configuration being required. This makes -transparent proxying ideal for those situations where you can't change client -behaviour - proxy-oblivious Android applications being a common example. - -To achieve this, we need to introduce two extra components. The first is a -redirection mechanism that transparently reroutes a TCP connection destined for -a server on the Internet to a listening proxy server. This usually takes the -form of a firewall on the same host as the proxy server - `iptables`_ on Linux -or pf_ on OSX. Once the client has initiated the connection, it makes a vanilla -HTTP request, which might look something like this: - -.. code-block:: none - - GET /index.html HTTP/1.1 - -Note that this request differs from the explicit proxy variation, in that it -omits the scheme and hostname. How, then, do we know which upstream host to -forward the request to? The routing mechanism that has performed the redirection -keeps track of the original destination for us. Each routing mechanism has a -different way of exposing this data, so this introduces the second component -required for working transparent proxying: a host module that knows how to -retrieve the original destination address from the router. In mitmproxy, this -takes the form of a built-in set of modules_ that know how to talk to each -platform's redirection mechanism. Once we have this information, the process is -fairly straight-forward. - -.. image:: schematics/how-mitmproxy-works-transparent.png - :align: center - -1. The client makes a connection to the server. -2. The router redirects the connection to mitmproxy, which is typically - listening on a local port of the same host. Mitmproxy then consults the - routing mechanism to establish what the original destination was. -3. Now, we simply read the client's request... -4. ... and forward it upstream. - -Transparent HTTPS ------------------ - -The first step is to determine whether we should treat an incoming connection as -HTTPS. The mechanism for doing this is simple - we use the routing mechanism to -find out what the original destination port is. All incoming connections pass -through different layers which can determin the actual protocol to use. -Automatic TLS detection works for SSLv3, TLS 1.0, TLS 1.1, and TLS 1.2 by -looking for a *ClientHello* message at the beginning of each connection. This -works independently of the used TCP port. - -From here, the process is a merger of the methods we've described for -transparently proxying HTTP, and explicitly proxying HTTPS. We use the routing -mechanism to establish the upstream server address, and then proceed as for -explicit HTTPS connections to establish the CN and SANs, and cope with SNI. - -.. image:: schematics/how-mitmproxy-works-transparent-https.png - :align: center - -1. The client makes a connection to the server. -2. The router redirects the connection to mitmproxy, which is typically listening on a local port - of the same host. Mitmproxy then consults the routing mechanism to establish what the original - destination was. -3. The client believes it's talking to the remote server, and initiates the TLS connection. - It uses SNI to indicate the hostname it is connecting to. -4. Mitmproxy connects to the server, and establishes a TLS connection using the SNI hostname - indicated by the client. -5. The server responds with the matching certificate, which contains the CN and SAN values - needed to generate the interception certificate. -6. Mitmproxy generates the interception cert, and continues the client TLS handshake paused in - step 3. -7. The client sends the request over the established TLS connection. -8. Mitmproxy passes the request on to the server over the TLS connection initiated in step 4. - -.. rubric:: Footnotes - -.. [#tls] The use of "TLS" refers to both SSL (outdated and insecure) and TLS - (1.0 and up) in the generic sense, unless otherwise specified. - -.. _Server Name Indication: https://en.wikipedia.org/wiki/Server_Name_Indication -.. _HTTP RFC: https://tools.ietf.org/html/rfc7230 -.. _Certificate Authority: https://en.wikipedia.org/wiki/Certificate_authority -.. _Subject Alternative Name: https://en.wikipedia.org/wiki/SubjectAltName -.. _iptables: http://www.netfilter.org/ -.. _pf: https://en.wikipedia.org/wiki/PF_\(firewall\) -.. _modules: https://github.com/mitmproxy/mitmproxy/tree/master/mitmproxy/platform diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 8dba4d04d..000000000 --- a/docs/index.rst +++ /dev/null @@ -1,99 +0,0 @@ -.. include:: introduction.rst - - -.. toctree:: - :hidden: - :maxdepth: 1 - - introduction - install - certinstall - howmitmproxy - modes - -.. toctree:: - :hidden: - :caption: Tools - - mitmproxy - mitmdump - mitmweb - config - -.. toctree:: - :hidden: - :caption: Protocols - - protocols/http1 - protocols/http2 - protocols/websocket - protocols/tcpproxy - -.. toctree:: - :hidden: - :caption: Features - - features/anticache - features/filters - features/replacements - features/clientreplay - features/serverreplay - features/setheaders - features/passthrough - features/proxyauth - features/reverseproxy - features/streaming - features/socksproxy - features/sticky - features/upstreamproxy - features/upstreamcerts - -.. toctree:: - :hidden: - :caption: Transparent Proxying - - transparent - transparent/linux - transparent/osx - transparent/openbsd - -.. toctree:: - :hidden: - :caption: Scripting - - scripting/overview - scripting/events - scripting/api - - -.. toctree:: - :hidden: - :caption: Tutorials - - tutorials/30second - tutorials/gamecenter - tutorials/transparent-dhcp - - -.. toctree:: - :hidden: - :caption: Pathod & Pathoc - - pathod/intro - pathod/language - pathod/library - pathod/test - - -.. toctree:: - :hidden: - :caption: Development - - dev/contributing - dev/sslkeylogfile - -.. Indices and tables - ================== - - * :ref:`genindex` - * :ref:`modindex` diff --git a/docs/install.rst b/docs/install.rst deleted file mode 100644 index 37bf8f764..000000000 --- a/docs/install.rst +++ /dev/null @@ -1,159 +0,0 @@ -.. _install: - -Installation -============ - -Please follow the steps for your operating system. - -Once installation is complete, you can run :ref:`mitmproxy`, :ref:`mitmdump` or -:ref:`mitmweb` from a terminal. - - -.. _install-macos: - -Installation on macOS ---------------------- - -The recommended way to install mitmproxy on macOS is to use `Homebrew`_: - -.. code:: bash - - brew install mitmproxy - -Alternatively you can download our :ref:`binary-packages` from our `releases`_ -page. - - -.. _install-linux: - -Installation on Linux ---------------------- - -The recommended way to install mitmproxy on Linux is to download our -:ref:`binary-packages` from our `releases`_ page. - -Some Linux distributions and their community provide mitmproxy packages via -their native package repositories (e.g., Arch Linux, Debian, Ubuntu, Kali Linux, -OpenSUSE, etc.). While we do encourage seeing mitmproxy in a great variety of -repositories and distributions, we are not maintaining or involved with their -downstream packaging efforts. If you are looking for the latest version or have -other problems, please contact the repository maintainers directly. - - -.. _install-windows: - -Installation on Windows ------------------------ - -The recommended way to install mitmproxy on Windows is to download our -:ref:`binary-packages` from our `releases`_ page. - -After installation, you'll find shortcuts for :ref:`mitmweb` (the web-based -interface) and :ref:`mitmdump` in the start menu. Both executables are added to -your PATH and can be invoked from the command line. - -.. note:: - The console interface is not supported on Windows, but you can - use `mitmweb` (the web-based interface) and `mitmdump`. - - -.. _install-advanced: - -Advanced Installation ---------------------- - -.. _binary-packages: - -Self-contained Pre-built Binary Packages -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -For some platforms we provide pre-built binary packages containing ready-to-run -executables. This includes a self-contained Python 3 environment, a recent -OpenSSL that support ALPN and HTTP/2, and other dependencies that would -otherwise we cumbersome to compile and install. - -Please be advised that we do not update these binaries after the initial -release. This means we do not include security-related updates of our -dependencies in already released mitmproxy versions. If there is a severe issue, -we might consider releasing a bugfix release of mitmproxy and corresponding -binary packages. - -We only support the latest version of mitmproxy with bugfix and security updates -through regular minor releases. - - -.. _install-docker: - -Docker Images -^^^^^^^^^^^^^ - -You can use the official mitmproxy images from `DockerHub`_. The same security -considerations apply as for our binary packages. - - -.. _install-linux-pip3: - -Installation on Linux via pip3 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Please make sure to install Python 3.5 (or higher) and pip3 for your -distribtion. If your distribution does not provide a suitable Python version, -you can use `pyenv`_ to get a recent Python environment. - -.. code:: bash - - sudo apt install python3-pip # Debian 8 or higher, Ubuntu 16.04 or higher - sudo dnf install python3-pip # Fedora 24 or higher - sudo pacman -S python-pip # Arch Linux - -Please make sure to upgrade pip3 itself: - -.. code:: bash - - sudo pip3 install -U pip - -Now you can install mitmproxy via pip3: - -.. code:: bash - - sudo pip3 install mitmproxy - - -.. _install-windows-pip3: - -Installation on Windows via pip3 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. note:: - The console interface is not supported on Windows, but you can - use `mitmweb` (the web-based interface) and `mitmdump`. - -First, install the latest version of Python 3.5 or higher from the `Python -website`_. During installation, make sure to select `Add Python to PATH`. There -are no other dependencies on Windows. - -Now you can install mitmproxy via pip3: - -.. code:: powershell - - pip3 install mitmproxy - - - -.. _install-from-source: - -Installation from Source Code -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you would like to install mitmproxy directly from source code or the GitHub -master branch, please see the our README_ on GitHub. - - -.. _README: https://github.com/mitmproxy/mitmproxy/blob/master/README.rst -.. _releases: https://github.com/mitmproxy/mitmproxy/releases/latest -.. _mitmproxy.org: https://mitmproxy.org/ -.. _`Python website`: https://www.python.org/downloads/windows/ -.. _pip: https://pip.pypa.io/en/latest/installing.html -.. _pyenv: https://github.com/yyuu/pyenv -.. _DockerHub: https://hub.docker.com/r/mitmproxy/mitmproxy/ -.. _Homebrew: https://brew.sh/ diff --git a/docs/mitmdump.rst b/docs/mitmdump.rst deleted file mode 100644 index 369bf0eb2..000000000 --- a/docs/mitmdump.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. _mitmdump: -.. program:: mitmdump - -mitmdump -======== - - -**mitmdump** is the command-line companion to mitmproxy. It provides -tcpdump-like functionality to let you view, record, and programmatically -transform HTTP traffic. See the ``--help`` flag output for complete -documentation. - - - -Examples --------- - -Saving traffic -^^^^^^^^^^^^^^ - ->>> mitmdump -w outfile - -Start up mitmdump in proxy mode, and write all traffic to **outfile**. - - -Filtering saved traffic -^^^^^^^^^^^^^^^^^^^^^^^ - ->>> mitmdump -nr infile -w outfile "~m post" - -Start mitmdump without binding to the proxy port (``-n``), read all flows from -infile, apply the specified filter expression (only match POSTs), and write to -outfile. - - -Client replay -^^^^^^^^^^^^^ - ->>> mitmdump -nc outfile - -Start mitmdump without binding to the proxy port (``-n``), then replay all -requests from outfile (``-c filename``). Flags combine in the obvious way, so -you can replay requests from one file, and write the resulting flows to -another: - ->>> mitmdump -nc srcfile -w dstfile - -See the :ref:`clientreplay` section for more information. - - -Running a script -^^^^^^^^^^^^^^^^ - ->>> mitmdump -s examples/add_header.py - -This runs the **add_header.py** example script, which simply adds a new header -to all responses. - -Scripted data transformation -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - ->>> mitmdump -ns examples/add_header.py -r srcfile -w dstfile - -This command loads flows from **srcfile**, transforms it according to the -specified script, then writes it back to **dstfile**. - diff --git a/docs/mitmproxy-docs.png b/docs/mitmproxy-docs.png deleted file mode 100644 index 8384db58b..000000000 Binary files a/docs/mitmproxy-docs.png and /dev/null differ diff --git a/docs/mitmproxy.rst b/docs/mitmproxy.rst deleted file mode 100644 index ff6a8857b..000000000 --- a/docs/mitmproxy.rst +++ /dev/null @@ -1,125 +0,0 @@ -.. _mitmproxy: -.. program:: mitmproxy - -mitmproxy -========= - -**mitmproxy** is a console tool that allows interactive examination and -modification of HTTP traffic. It differs from mitmdump in that all flows are -kept in memory, which means that it's intended for taking and manipulating -small-ish samples. Use the :kbd:`?` shortcut key to view, context-sensitive -documentation from any **mitmproxy** screen. - -Flow list ---------- - -The flow list shows an index of captured flows in chronological order. - -.. image:: screenshots/mitmproxy.png - -- **1**: A GET request, returning a 302 Redirect response. -- **2**: A GET request, returning 16.75kb of text/html data. -- **3**: A replayed request. -- **4**: Intercepted flows are indicated with orange text. The user may edit - these flows, and then accept them (using the :kbd:`a` key) to continue. In this - case, the request has been intercepted on the way to the server. -- **5**: A response intercepted from the server on the way to the client. -- **6**: The event log can be toggled on and off using the :kbd:`e` shortcut key. This - pane shows events and errors that may not result in a flow that shows up in the - flow pane. -- **7**: Flow count. -- **8**: Various information on mitmproxy's state. In this case, we have an - interception pattern set to ``.*``. -- **9**: Bind address indicator - mitmproxy is listening on port 8080 of all - interfaces. - - -Flow view ---------- - -The **Flow View** lets you inspect and manipulate a single flow: - -.. image:: screenshots/mitmproxy-flowview.png - -- **1**: Flow summary. -- **2**: The Request/Response tabs, showing you which part of the flow you are - currently viewing. In the example above, we're viewing the Response. Hit :kbd:`tab` - to switch between the Response and the Request. -- **3**: Headers. -- **4**: Body. -- **5**: View Mode indicator. In this case, we're viewing the body in **hex** mode. The other - available modes are **pretty**, which uses a number of heuristics to show you a friendly - view of various content types, and **raw**, which shows you exactly what's there without any - changes. You can change modes using the :kbd:`m` key. - - -Grid Editor ------------ - -Much of the data that we'd like to interact with in mitmproxy is structured. -For instance, headers, queries and form data can all be thought of as a list of -key/value pairs. Mitmproxy has a built-in editor that lays this type of data -out in a grid for easy manipulation. - -At the moment, the Grid Editor is used in four parts of mitmproxy: - - - Editing request or response headers (:kbd:`e` for edit, then :kbd:`h` for headers in flow view) - - Editing a query string (:kbd:`e` for edit, then :kbd:`q` for query in flow view) - - Editing a URL-encoded form (:kbd:`e` for edit, then :kbd:`f` for form in flow view) - - Editing replacement patterns (:kbd:`O` for options, then :kbd:`R` for Replacement Patterns) - -If there is is no data, an empty editor will be started to let you add some. -Here is the editor showing the headers from a request: - -.. image:: screenshots/mitmproxy-kveditor.png - -To edit, navigate to the key or value you want to modify using the arrow or vi -navigation keys, and press enter. The background color will change to show that -you are in edit mode for the specified field: - -.. image:: screenshots/mitmproxy-kveditor-editmode.png - -Modify the field as desired, then press escape to exit edit mode when you're -done. You can also add a row (:kbd:`a` key), delete a row (:kbd:`d` key), spawn an -external editor on a field (:kbd:`e` key). Be sure to consult the context-sensitive -help (:kbd:`?` key) for more. - -Example: Interception ---------------------- - -**mitmproxy**'s interception functionality lets you pause an HTTP request or -response, inspect and modify it, and then accept it to send it on to the server -or client. - - -1: Set an interception pattern -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: screenshots/mitmproxy-intercept-filt.png - -We press :kbd:`i` to set an interception pattern. In this case, the ``~q`` filter -pattern tells **mitmproxy** to intercept all requests. For complete filter -syntax, see the :ref:`filters` section of the documentation, -or the built-in help function in **mitmproxy**. - -2: Intercepted connections are indicated with orange text: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: screenshots/mitmproxy-intercept-mid.png - -3: You can now view and modify the request: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: screenshots/mitmproxy-intercept-options.png - -In this case, we viewed the request by selecting it, pressed :kbd:`e` for "edit" -and :kbd:`m` for "method" to change the HTTP request method. - -4: Accept the intercept to continue: -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: screenshots/mitmproxy-intercept-result.png - -Finally, we press :kbd:`a` to accept the modified request, which is then sent on to -the server. In this case, we changed the request from an HTTP GET to -OPTIONS, and Google's server has responded with a 405 "Method not allowed". diff --git a/docs/mitmweb.rst b/docs/mitmweb.rst deleted file mode 100644 index e57af0595..000000000 --- a/docs/mitmweb.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _mitmweb: -.. program:: mitmweb - -mitmweb -======= - -**mitmweb** is mitmproxy's web-based user interface that allows interactive -examination and modification of HTTP traffic. Like mitmproxy, it differs from -mitmdump in that all flows are kept in memory, which means that it's intended -for taking and manipulating small-ish samples. - -.. warning:: - - Mitmweb is currently in beta. We consider it stable for all features currently - exposed in the UI, but it still misses a lot of mitmproxy's features. - - -.. image:: screenshots/mitmweb.png diff --git a/docs/modd.conf b/docs/modd.conf index 0e8f0dced..ddadc11a4 100644 --- a/docs/modd.conf +++ b/docs/modd.conf @@ -1,6 +1,8 @@ -@build = ./_build - -** !_build/** ../mitmproxy/**/*.py { - prep: sphinx-build -W -d @build/doctrees -b html . @build/html - daemon: devd -m @build/html +{ + daemon: cd src; hugo server -D +} + +style/** { + # This is quite fast, so it can stay a prep rather than a daemon + prep: ./node_modules/.bin/node-sass -o ./src/themes/mitmproxydocs/static/css ./style/style.scss --error-bell } diff --git a/docs/modes.rst b/docs/modes.rst deleted file mode 100644 index 2c87b2a3d..000000000 --- a/docs/modes.rst +++ /dev/null @@ -1,193 +0,0 @@ -.. _modes: - -Modes of Operation -================== - -Mitmproxy has four modes of operation that allow you to use mitmproxy in a -variety of scenarios: - -- **Regular** (the default) -- **Transparent** -- **Reverse Proxy** -- **Upstream Proxy** - - -Now, which one should you pick? Use this flow chart: - -.. image:: schematics/proxy-modes-flowchart.png - :align: center - -Regular Proxy -------------- - -Mitmproxy's regular mode is the simplest and the easiest to set up. - -1. Start mitmproxy. -2. Configure your client to use mitmproxy by explicitly setting an HTTP proxy. -3. Quick Check: You should already be able to visit an unencrypted HTTP site through the proxy. -4. Open the magic domain **mitm.it** and install the certificate for your device. - -.. note:: - Unfortunately, some applications bypass the system HTTP proxy settings - Android applications - are a common example. In these cases, you need to use mitmproxy's transparent mode. - -If you are proxying an external device, your network will probably look like this: - -.. image:: schematics/proxy-modes-regular.png - :align: center - -The square brackets signify the source and destination IP addresses. Your -client explicitly connects to mitmproxy and mitmproxy explicitly connects -to the target server. - -Transparent Proxy ------------------ - -In transparent mode, traffic is directed into a proxy at the network layer, -without any client configuration required. This makes transparent proxying -ideal for situations where you can't change client behaviour. In the graphic -below, a machine running mitmproxy has been inserted between the router and -the internet: - -.. image:: schematics/proxy-modes-transparent-1.png - :align: center - -The square brackets signify the source and destination IP addresses. Round -brackets mark the next hop on the *Ethernet/data link* layer. This distinction -is important: when the packet arrives at the mitmproxy machine, it must still -be addressed to the target server. This means that Network Address Translation -should not be applied before the traffic reaches mitmproxy, since this would -remove the target information, leaving mitmproxy unable to determine the real -destination. - -.. image:: schematics/proxy-modes-transparent-wrong.png - :align: center - -Common Configurations -^^^^^^^^^^^^^^^^^^^^^ - -There are many ways to configure your network for transparent proxying. We'll -look at two common scenarios: - -1. Configuring the client to use a custom gateway/router/"next hop" -2. Implementing custom routing on the router - -In most cases, the first option is recommended due to its ease of use. - -(a) Custom Gateway -~~~~~~~~~~~~~~~~~~ - -One simple way to get traffic to the mitmproxy machine with the destination IP -intact, is to simply configure the client with the mitmproxy box as the -default gateway. - -.. image:: schematics/proxy-modes-transparent-2.png - :align: center - -In this scenario, we would: - -1. Configure the proxy machine for transparent mode. You can find instructions - in the :ref:`transparent` section. -2. Configure the client to use the proxy machine's IP as the default gateway. -3. Quick Check: At this point, you should already be able to visit an - unencrypted HTTP site over the proxy. -4. Open the magic domain **mitm.it** and install the certificate - for your device. - -Setting the custom gateway on clients can be automated by serving the settings -out to clients over DHCP. This lets set up an interception network where all -clients are proxied automatically, which can save time and effort. - -.. admonition:: Troubleshooting Transparent Mode - :class: note - - Incorrect transparent mode configurations are a frequent source of - error. If it doesn't work for you, try the following things: - - - Open mitmproxy's event log (press :kbd:`e`) - do you see clientconnect messages? - If not, the packets are not arriving at the proxy. One common cause is the occurrence of ICMP - redirects, which means that your machine is telling the client that there's a faster way to - the internet by contacting your router directly (see the :ref:`transparent` section on how to - disable them). If in doubt, Wireshark_ may help you to see whether something arrives at your - machine or not. - - Make sure you have not explicitly configured an HTTP proxy on the client. - This is not needed in transparent mode. - - Re-check the instructions in the :ref:`transparent` section. Anything you missed? - - If you encounter any other pitfalls that should be listed here, please let us know! - -(b) Custom Routing -~~~~~~~~~~~~~~~~~~ - -In some cases, you may need more fine-grained control of which traffic reaches -the mitmproxy instance, and which doesn't. You may, for instance, choose only -to divert traffic to some hosts into the transparent proxy. There are a huge -number of ways to accomplish this, and much will depend on the router or -packet filter you're using. In most cases, the configuration will look like -this: - -.. image:: schematics/proxy-modes-transparent-3.png - :align: center - - -Reverse Proxy -------------- - -mitmproxy is usually used with a client that uses the proxy to access the -Internet. Using reverse proxy mode, you can use mitmproxy to act like a normal -HTTP server: - -.. image:: schematics/proxy-modes-reverse.png - :align: center - -There are various use-cases: - -- Say you have an internal API running at http://example.local/. You could now - set up mitmproxy in reverse proxy mode at http://debug.example.local/ and - dynamically point clients to this new API endpoint, which provides them - with the same data and you with debug information. Similarly, you could move - your real server to a different IP/port and set up mitmproxy in the original - place to debug and or redirect all sessions. - -- Say you're a web developer working on http://example.com/ (with a development - version running on http://localhost:8000/). You can modify your hosts file so that - example.com points to 127.0.0.1 and then run mitmproxy in reverse proxy mode - on port 80. You can test your app on the example.com domain and get all - requests recorded in mitmproxy. - -- Say you have some toy project that should get SSL support. Simply set up - mitmproxy as a reverse proxy on port 443 and you're done (``mitmdump -p 443 -R - http://localhost:80/``). Mitmproxy auto-detects TLS traffic and intercepts it dynamically. - There are better tools for this specific task, but mitmproxy is very quick and simple way to - set up an SSL-speaking server. - -- Want to add a non-SSL-capable compression proxy in front of your server? You - could even spawn a mitmproxy instance that terminates SSL (``-R http://...``), - point it to the compression proxy and let the compression proxy point to a - SSL-initiating mitmproxy (``-R https://...``), which then points to the real - server. As you see, it's a fairly flexible thing. - -.. admonition:: Caveat: Interactive Use - :class: warning - - Reverse Proxy mode is usually not sufficient to create a copy of an interactive website at - different URL. The HTML served to the client remains unchanged - as soon as the user clicks on - an non-relative URL (or downloads a non-relative image resource), traffic no longer passes - through mitmproxy. - -Upstream Proxy --------------- - -If you want to chain proxies by adding mitmproxy in front of a different proxy -appliance, you can use mitmproxy's upstream mode. In upstream mode, all -requests are unconditionally transferred to an upstream proxy of your choice. - -.. image:: schematics/proxy-modes-upstream.png - :align: center - -mitmproxy supports both explicit HTTP and explicit HTTPS in upstream proxy -mode. You could in theory chain multiple mitmproxy instances in a row, but -that doesn't make any sense in practice (i.e. outside of our tests). - - -.. _Wireshark: https://wireshark.org/ diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..a950c88d3 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "bulma": "^0.6.1", + "node-sass": "^4.7.2" + } +} diff --git a/docs/pathod/intro.rst b/docs/pathod/intro.rst deleted file mode 100644 index 1c1ad60ed..000000000 --- a/docs/pathod/intro.rst +++ /dev/null @@ -1,307 +0,0 @@ -.. _intro: - -Pathology 101 -============= - -.. _pathod: - -pathod ------- - -Pathod is a pathological HTTP daemon designed to let you craft almost any -conceivable HTTP response, including ones that creatively violate the -standards. HTTP responses are specified using a :ref:`small, terse language -` which pathod shares with its evil twin :ref:`pathoc`. To start -playing with pathod, fire up the daemon: - ->>> pathod - -By default, the service listens on port 9999 of localhost, and the default -crafting anchor point is the path **/p/**. Anything after this URL prefix is -treated as a response specifier. So, hitting the following URL will generate an -HTTP 200 response with 100 bytes of random data: - - http://localhost:9999/p/200:b@100 - -See the :ref:`language documentation ` to get (much) fancier. The -pathod daemon also takes a range of configuration options. To view those, use -the command-line help: - ->>> pathod --help - -Mimicing a proxy -^^^^^^^^^^^^^^^^ - -Pathod automatically responds to both straight HTTP and proxy requests. For -proxy requests, the upstream host is ignored, and the path portion of the URL -is used to match anchors. This lets you test software that supports a proxy -configuration by spoofing responses from upstream servers. - -By default, we treat all proxy CONNECT requests as HTTPS traffic, serving the -response using either pathod's built-in certificates, or the cert/key pair -specified by the user. You can over-ride this behaviour if you're testing a -client that makes a non-SSL CONNECT request using the **-C** command-line -option. - -Anchors -^^^^^^^ - -Anchors provide an alternative to specifying the response in the URL. Instead, -you attach a response to a pre-configured anchor point, specified with a regex. -When a URL matching the regex is requested, the specified response is served. - ->>> pathod -a "/foo=200" - -Here, "/foo" is the regex specifying the anchor path, and the part after the "=" -is a response specifier. - - -File Access -^^^^^^^^^^^ - -There are two operators in the :ref:`language ` that load contents -from file - the **+** operator to load an entire request specification from -file, and the **>** value specifier. In pathod, both of these operators are -restricted to a directory specified at startup, or disabled if no directory is -specified: - ->>> pathod -d ~/staticdir" - - -Internal Error Responses -^^^^^^^^^^^^^^^^^^^^^^^^ - -Pathod uses the non-standard 800 response code to indicate internal errors, to -distinguish them from crafted responses. For example, a request to: - - http://localhost:9999/p/foo - -... will return an 800 response because "foo" is not a valid page specifier. - - - - - -.. _pathoc: - -pathoc ------- - -Pathoc is a perverse HTTP daemon designed to let you craft almost any -conceivable HTTP request, including ones that creatively violate the standards. -HTTP requests are specified using a :ref:`small, terse language `, -which pathoc shares with its server-side twin :ref:`pathod`. To view pathoc's -complete range of options, use the command-line help: - ->>> pathoc --help - - -Getting Started -^^^^^^^^^^^^^^^ - -The basic pattern for pathoc commands is as follows: - - pathoc hostname request [request ...] - -That is, we specify the hostname to connect to, followed by one or more -requests. Lets start with a simple example:: - - > pathoc google.com get:/ - 07-06-16 12:13:43: >> 'GET':/ - << 302 Found: 261 bytes - -Here, we make a GET request to the path / on port 80 of google.com. Pathoc's -output tells us that the server responded with a 302 redirection. We can tell -pathoc to connect using SSL, in which case the default port is changed to 443 -(you can over-ride the default port with the **-p** command-line option):: - - > pathoc -s www.google.com get:/ - 07-06-16 12:14:56: >> 'GET':/ - << 302 Found: 262 bytes - - -Multiple Requests -^^^^^^^^^^^^^^^^^ - -There are two ways to tell pathoc to issue multiple requests. The first is to specify -them on the command-line, like so:: - - > pathoc google.com get:/ get:/ - 07-06-16 12:21:04: >> 'GET':/ - << 302 Found: 261 bytes - 07-06-16 12:21:04: >> 'GET':/ - << 302 Found: 261 bytes - -In this case, pathoc issues the specified requests over the same TCP connection - -so in the above example only one connection is made to google.com - -The other way to issue multiple requests is to use the **-n** flag:: - - > pathoc -n 2 google.com get:/ - 07-06-16 12:21:04: >> 'GET':/ - << 302 Found: 261 bytes - 07-06-16 12:21:04: >> 'GET':/ - << 302 Found: 261 bytes - -The output is identical, but two separate TCP connections are made to the -upstream server. These two specification styles can be combined:: - - pathoc -n 2 google.com get:/ get:/ - - -Here, two distinct TCP connections are made, with two requests issued over -each. - - - -Basic Fuzzing -^^^^^^^^^^^^^ - -The combination of pathoc's powerful request specification language and a few -of its command-line options makes for quite a powerful basic fuzzer. Here's an -example:: - - pathoc -e -I 200 -t 2 -n 1000 localhost get:/:b@10:ir,@1 - -The request specified here is a valid GET with a body consisting of 10 random bytes, -but with 1 random byte inserted in a random place. This could be in the headers, -in the initial request line, or in the body itself. There are a few things -to note here: - -- Corrupting the request in this way will often make the server enter a state where - it's awaiting more input from the client. This is where the - **-t** option comes in, which sets a timeout that causes pathoc to - disconnect after two seconds. -- The **-n** option tells pathoc to repeat the request 1000 times. -- The **-I** option tells pathoc to ignore HTTP 200 response codes. - You can use this to fine-tune what pathoc considers to be an exceptional - condition, and therefore log-worthy. -- The **-e** option tells pathoc to print an explanation of each logged - request, in the form of an expanded pathoc specification with all random - portions and automatic header additions resolved. This lets you precisely - replay a request that triggered an error. - - -Interacting with Proxies -^^^^^^^^^^^^^^^^^^^^^^^^ - -Pathoc has a reasonably sophisticated suite of features for interacting with -proxies. The proxy request syntax very closely mirrors that of straight HTTP, -which means that it is possible to make proxy-style requests using pathoc -without any additional syntax, by simply specifying a full URL instead of a -simple path: - ->>> pathoc -p 8080 localhost "get:'http://google.com'" - -Another common use case is to use an HTTP CONNECT request to probe remote -servers via a proxy. This is done with the **-c** command-line option, which -allows you to specify a remote host and port pair: - ->>> pathoc -c google.com:80 -p 8080 localhost get:/ - -Note that pathoc does **not** negotiate SSL without being explictly instructed -to do so. If you're making a CONNECT request to an SSL-protected resource, you -must also pass the **-s** flag: - ->>> pathoc -sc google.com:443 -p 8080 localhost get:/ - - - -Embedded response specification -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -One interesting feature of the Request specification language is that you can -embed a response specification in it, which is then added to the request path. -Here's an example: - ->>> pathoc localhost:9999 "get:/p/:s'401:ir,@1'" - -This crafts a request that connects to the pathod server, and which then crafts -a response that generates a 401, with one random byte embedded at a random -point. The response specification is parsed and expanded by pathoc, so you see -syntax errors immediately. This really becomes handy when combined with the -**-e** flag to show the expanded request:: - - 07-06-16 12:32:01: >> 'GET':/p/:s'401:i35,\x27\\x1b\x27:h\x27Content-Length\x27=\x270\x27:h\x27Content-Length\x27=\x270\x27':h'Host'='localhost' - << 401 Unauthorized: 0 bytes - -Note that the embedded response has been resolved *before* being sent to -the server, so that "ir,@1" (embed a random byte at a random location) has -become "i15,\'o\'" (embed the character "o" at offset 15). You now have a -pathoc request specification that is precisely reproducible, even with random -components. This feature comes in terribly handy when testing a proxy, since -you can now drive the server response completely from the client, and have a -complete log of reproducible requests to analyze afterwards. - - -Request Examples ----------------- - -.. list-table:: - :widths: 50 50 - :header-rows: 0 - - * - get:/ - - Get path / - - * - get:/:b@100 - - 100 random bytes as the body - - * - get:/:h"Etag"="&;drop table browsers;" - - Add a header - - * - get:/:u"&;drop table browsers;" - - Add a User-Agent header - - * - get:/:b@100:dr - - Drop the connection randomly - - * - get:/:b@100,ascii:ir,@1 - - 100 ASCII bytes as the body, and randomly inject a random byte - - * - ws:/ - - Initiate a websocket handshake. - - -Response Examples ------------------ - -.. list-table:: - :widths: 50 50 - :header-rows: 0 - - - * - 200 - - A basic HTTP 200 response. - - * - 200:r - - A basic HTTP 200 response with no Content-Length header. This will hang. - - * - 200:da - - Server-side disconnect after all content has been sent. - - * - 200:b\@100 - - 100 random bytes as the body. A Content-Length header is added, so the disconnect - is no longer needed. - - * - 200:b\@100:h"Etag"="';drop table servers;" - - Add a Server header - - * - 200:b\@100:dr - - Drop the connection randomly - - * - 200:b\@100,ascii:ir,@1 - - 100 ASCII bytes as the body, and randomly inject a random byte - - * - 200:b\@1k:c"text/json" - - 1k of random bytes, with a text/json content type - - * - 200:b\@1k:p50,120 - - 1k of random bytes, pause for 120 seconds after 50 bytes - - * - 200:b\@1k:pr,f - - 1k of random bytes, but hang forever at a random location - - * - 200:b\@100:h\@1k,ascii_letters='foo' - - 100 ASCII bytes as the body, randomly generated 100k header name, with the value - 'foo'. diff --git a/docs/pathod/language.rst b/docs/pathod/language.rst deleted file mode 100644 index fe4ef6ca0..000000000 --- a/docs/pathod/language.rst +++ /dev/null @@ -1,257 +0,0 @@ -.. _language: - -language spec -============= - -************ -HTTP Request -************ - - **method:path:[colon-separated list of features]** - -.. list-table:: - :widths: 20 80 - :header-rows: 0 - - * - method - - A :ref:`VALUE` specifying the HTTP method to - use. Standard methods do not need to be enclosed in quotes, while - non-standard methods can be specified as quoted strings. - - The special method **ws** creates a valid websocket upgrade - GET request, and signals to pathoc to switch to websocket recieve - mode if the server responds correctly. Apart from that, websocket - requests are just like any other, and all aspects of the request - can be over-ridden. - * - h\ :ref:`VALUE`\ =\ :ref:`VALUE`\ - - Set a header. - * - r - - Set the **raw** flag on this response. Pathod will not calculate a - *Content-Length* header if a body is set. - * - c\ :ref:`VALUE` - - A shortcut for setting the Content-Type header. Equivalent to - ``h"Content-Type"=VALUE`` - * - u\ :ref:`VALUE` - uSHORTCUT - - Set a User-Agent header on this request. You can specify either a - complete :ref:`VALUE`, or a User-Agent shortcut: **android**, - **blackberry**, **bingbot**, **chrome**, **firefox**, **googlebot**, - **ie9**, **ipad**, **iphone**, **safari**. - * - b\ :ref:`VALUE` - - Set the body. The appropriate Content-Length header is added - automatically unless the **r** flag is set. - * - s\ :ref:`VALUE` - - An embedded Response specification, appended to the path of the request. - * - x\ :ref:`INTEGER` - - Repeat this message N times. - * - d\ :ref:`OFFSET` - - Disconnect after OFFSET bytes (HTTP/1 only). - * - i\ :ref:`OFFSET`,\ :ref:`VALUE` - - Inject the specified value at the offset (HTTP/1 only) - * - p\ :ref:`OFFSET`,SECONDS - - Pause for SECONDS seconds after OFFSET bytes. SECONDS can be an integer - or "f" to pause forever (HTTP/1 only) - - -************* -HTTP Response -************* - - **code:[colon-separated list of features]** - -.. list-table:: - :widths: 20 80 - :header-rows: 0 - - * - code - - An integer specifying the HTTP response code. - - The special method **ws** creates a valid websocket upgrade - response (code 101), and moves pathod to websocket mode. Apart - from that, websocket responses are just like any other, and all - aspects of the response can be over-ridden. - * - m\ :ref:`VALUE` - - HTTP Reason message. Automatically chosen according to the response - code if not specified. (HTTP/1 only) - * - h\ :ref:`VALUE`\ =\ :ref:`VALUE`\ - - Set a header. - * - r - - Set the **raw** flag on this response. Pathod will not calculate a - *Content-Length* header if a body is set. - * - l\ :ref:`VALUE` - - A shortcut for setting the Location header. Equivalent to - ``h"Location"=VALUE`` - * - c\ :ref:`VALUE` - - A shortcut for setting the Content-Type header. Equivalent to - ``h"Content-Type"=VALUE`` - * - b\ :ref:`VALUE` - - Set the body. The appropriate Content-Length header is added - automatically unless the **r** flag is set. - * - d\ :ref:`OFFSET` - - Disconnect after OFFSET bytes (HTTP/1 only). - * - i\ :ref:`OFFSET`,\ :ref:`VALUE` - - Inject the specified value at the offset (HTTP/1 only) - * - p\ :ref:`OFFSET`,SECONDS - - Pause for SECONDS seconds after OFFSET bytes. SECONDS can be an integer - or "f" to pause forever (HTTP/1 only) - -*************** -Websocket Frame -*************** - - **wf:[colon-separated list of features]** - -.. list-table:: - :widths: 20 80 - :header-rows: 0 - - * - b\ :ref:`VALUE` - - Set the frame payload. If a masking key is present, the value is - encoded automatically. - * - c\ :ref:`INTEGER` - - Set the op code. This can either be an integer from 0-15, or be one of - the following opcode names: **text** (the default), **continue**, - **binary**, **close**, **ping**, **pong**. - * - d\ :ref:`OFFSET` - - Disconnect after OFFSET bytes - * - i\ :ref:`OFFSET`,\ :ref:`VALUE` - - Inject the specified value at the offset - * - p\ :ref:`OFFSET`,SECONDS - - Pause for SECONDS seconds after OFFSET bytes. SECONDS can be an integer - or "f" to pause forever - * - x\ :ref:`INTEGER` - - Repeat this message N times. - * - [-]fin - - Set or un-set the **fin** bit. - * - k\ :ref:`VALUE` - - Set the masking key. The resulting value must be exactly 4 bytes long. - The special form **knone** specifies that no key should be set, even if - the mask bit is on. - * - l\ :ref:`INTEGER` - - Set the payload length in the frame header, regardless of the actual - body length. - * - [-]mask - - Set or un-set the mask bit. - * - r\ :ref:`VALUE` - - Set the raw frame payload. This disables masking, even if the key is present. - * - [-]rsv1 - - Set or un-set the **rsv1** bit. - * - [-]rsv2 - - Set or un-set the **rsv2** bit. - * - [-]rsv2 - - Set or un-set the **rsv2** bit. - - - -********** -Data types -********** - -.. _INTEGER: - -INTEGER -^^^^^^^ - -.. _OFFSET: - -OFFSET -^^^^^^ - -Offsets are calculated relative to the base message, before any injections or -other transforms are applied. They have 3 flavors: - -======= ========================== -integer An integer byte offset -**r** A random location -**a** The end of the message -======= ========================== - - -.. _VALUE: - -VALUE -^^^^^ - -Literals -"""""""" - -Literal values are specified as a quoted strings:: - - "foo" - -Either single or double quotes are accepted, and quotes can be escaped with -backslashes within the string:: - - 'fo\'o' - -Literal values can contain Python-style backslash escape sequences:: - - 'foo\r\nbar' - - - -Generated -""""""""" - -An @-symbol lead-in specifies that generated data should be used. There are two -components to a generator specification - a size, and a data type. By default -pathod assumes a data type of "bytes". - -Here's a value specifier for generating 100 bytes:: - - @100 - -You can use standard suffixes to indicate larger values. Here, for instance, is -a specifier for generating 100 megabytes: - - @100m - -Data is generated and served efficiently - if you really want to send a -terabyte of data to a client, pathod can do it. The supported suffixes are: - -========== ==================== -b 1024**0 (bytes) -k 1024**1 (kilobytes) -m 1024**2 (megabytes) -g 1024**3 (gigabytes) -t 1024**4 (terabytes) -========== ==================== - -Data types are separated from the size specification by a comma. This specification -generates 100mb of ASCII:: - - @100m,ascii - -Supported data types are: - -================= ============================================== -ascii All ASCII characters -ascii_letters A-Za-z -ascii_lowercase a-z -ascii_uppercase A-Z -bytes All 256 byte values -digits 0-9 -hexdigits 0-f -octdigits 0-7 -punctuation !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ and space -whitespace \\t \\n \\x0b \\x0c \\r and space -================= ============================================== - - - -Files -""""" - -You can load a value from a specified file path. To do so, you have to specify a -_staticdir_ option to pathod on the command-line, like so: - ->>> pathod -d ~/myassets - -All paths are relative paths under this directory. File loads are indicated by -starting the value specifier with the left angle bracket:: - - >> pydoc pathod.test - -The remainder of this page demonstrates some common interaction patterns using -`Nose`_. These examples are -also applicable with only minor modification to most commonly used Python testing -engines. - - -Context Manager ---------------- - -.. literalinclude:: ../../examples/pathod/test_context.py - :caption: examples/pathod/test_context.py - :language: python - - -One instance per test ---------------------- - -.. literalinclude:: ../../examples/pathod/test_setup.py - :caption: examples/pathod/test_setup.py - :language: python - - -.. _Nose: https://nose.readthedocs.org/en/latest/ diff --git a/docs/protocols/http1.rst b/docs/protocols/http1.rst deleted file mode 100644 index 21e68785e..000000000 --- a/docs/protocols/http1.rst +++ /dev/null @@ -1,15 +0,0 @@ -.. _http1_protocol: - -HTTP/1.0 and HTTP/1.1 -=========================== - -.. seealso:: - - - `RFC7230: HTTP/1.1: Message Syntax and Routing `_ - - `RFC7231: HTTP/1.1: Semantics and Content `_ - -HTTP/1.0 and HTTP/1.1 support in mitmproxy is based on our custom HTTP stack, -which takes care of all semantics and on-the-wire parsing/serialization tasks. - -mitmproxy currently does not support HTTP trailers - but if you want to send -us a PR, we promise to take look! diff --git a/docs/protocols/http2.rst b/docs/protocols/http2.rst deleted file mode 100644 index b3268ae5b..000000000 --- a/docs/protocols/http2.rst +++ /dev/null @@ -1,16 +0,0 @@ -.. _http2_protocol: - -HTTP/2 -====== - -.. seealso:: - - - `RFC7540: Hypertext Transfer Protocol Version 2 (HTTP/2) `_ - -HTTP/2 support in mitmproxy is based on the amazing work by the python-hyper -community with the `hyper-h2 `_ -project. It fully encapsulates the internal state of HTTP/2 connections and -provides an easy-to-use event-based API. - -mitmproxy currently does not support HTTP/2 trailers - but if you want to send -us a PR, we promise to take look! diff --git a/docs/protocols/tcpproxy.rst b/docs/protocols/tcpproxy.rst deleted file mode 100644 index 772485732..000000000 --- a/docs/protocols/tcpproxy.rst +++ /dev/null @@ -1,31 +0,0 @@ -.. _tcp_proxy: - -TCP Proxy / Fallback -==================== - -In case mitmproxy does not handle a specific protocol, you can exempt -hostnames from processing, so that mitmproxy acts as a generic TCP forwarder. -This feature is closely related to the :ref:`passthrough` functionality, -but differs in two important aspects: - -- The raw TCP messages are printed to the event log. -- SSL connections will be intercepted. - -Please note that message interception or modification are not possible yet. -If you are not interested in the raw TCP messages, you should use the ignore domains feature. - -How it works ------------- - -================== ====================== -command-line ``--tcp HOST`` -mitmproxy shortcut :kbd:`O` then :kbd:`T` -================== ====================== - -For a detailed description how the hostname pattern works, please look at the :ref:`passthrough` -feature. - -.. seealso:: - - - :ref:`passthrough` - - :ref:`streaming` diff --git a/docs/protocols/websocket.rst b/docs/protocols/websocket.rst deleted file mode 100644 index 8a7e807f7..000000000 --- a/docs/protocols/websocket.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _websocket_protocol: - -WebSocket -========= - -.. seealso:: - - - `RFC6455: The WebSocket Protocol `_ - - `RFC7692: Compression Extensions for WebSocket `_ - -WebSocket support in mitmproxy is based on the amazing work by the python-hyper -community with the `wsproto `_ -project. It fully encapsulates WebSocket frames/messages/connections and -provides an easy-to-use event-based API. - -mitmproxy fully supports the compression extension for WebSocket messages, -provided by wsproto. - -If an endpoint sends a PING to mitmproxy, a PONG will be sent back immediately -(with the same payload if present). To keep the other connection alive, a new -PING (without a payload) is sent to the other endpoint. Unsolicited PONG's are -not forwarded. All PING's and PONG's are logged (with payload if present). diff --git a/docs/schematics/_explicit.graffle/data.plist b/docs/raw/_explicit.graffle/data.plist similarity index 100% rename from docs/schematics/_explicit.graffle/data.plist rename to docs/raw/_explicit.graffle/data.plist diff --git a/docs/schematics/_explicit.graffle/image3.icns b/docs/raw/_explicit.graffle/image3.icns similarity index 100% rename from docs/schematics/_explicit.graffle/image3.icns rename to docs/raw/_explicit.graffle/image3.icns diff --git a/docs/schematics/_explicit.graffle/image6.tiff b/docs/raw/_explicit.graffle/image6.tiff similarity index 100% rename from docs/schematics/_explicit.graffle/image6.tiff rename to docs/raw/_explicit.graffle/image6.tiff diff --git a/docs/schematics/_explicit_https.graffle/data.plist b/docs/raw/_explicit_https.graffle/data.plist similarity index 100% rename from docs/schematics/_explicit_https.graffle/data.plist rename to docs/raw/_explicit_https.graffle/data.plist diff --git a/docs/schematics/_explicit_https.graffle/image3.icns b/docs/raw/_explicit_https.graffle/image3.icns similarity index 100% rename from docs/schematics/_explicit_https.graffle/image3.icns rename to docs/raw/_explicit_https.graffle/image3.icns diff --git a/docs/schematics/_explicit_https.graffle/image6.tiff b/docs/raw/_explicit_https.graffle/image6.tiff similarity index 100% rename from docs/schematics/_explicit_https.graffle/image6.tiff rename to docs/raw/_explicit_https.graffle/image6.tiff diff --git a/docs/schematics/_transparent.graffle/data.plist b/docs/raw/_transparent.graffle/data.plist similarity index 100% rename from docs/schematics/_transparent.graffle/data.plist rename to docs/raw/_transparent.graffle/data.plist diff --git a/docs/schematics/_transparent.graffle/image3.icns b/docs/raw/_transparent.graffle/image3.icns similarity index 100% rename from docs/schematics/_transparent.graffle/image3.icns rename to docs/raw/_transparent.graffle/image3.icns diff --git a/docs/schematics/_transparent.graffle/image6.tiff b/docs/raw/_transparent.graffle/image6.tiff similarity index 100% rename from docs/schematics/_transparent.graffle/image6.tiff rename to docs/raw/_transparent.graffle/image6.tiff diff --git a/docs/schematics/_transparent_https.graffle/data.plist b/docs/raw/_transparent_https.graffle/data.plist similarity index 100% rename from docs/schematics/_transparent_https.graffle/data.plist rename to docs/raw/_transparent_https.graffle/data.plist diff --git a/docs/schematics/_transparent_https.graffle/image3.icns b/docs/raw/_transparent_https.graffle/image3.icns similarity index 100% rename from docs/schematics/_transparent_https.graffle/image3.icns rename to docs/raw/_transparent_https.graffle/image3.icns diff --git a/docs/schematics/_transparent_https.graffle/image6.tiff b/docs/raw/_transparent_https.graffle/image6.tiff similarity index 100% rename from docs/schematics/_transparent_https.graffle/image6.tiff rename to docs/raw/_transparent_https.graffle/image6.tiff diff --git a/docs/schematics/proxy-modes.vsdx b/docs/raw/proxy-modes.vsdx similarity index 100% rename from docs/schematics/proxy-modes.vsdx rename to docs/raw/proxy-modes.vsdx diff --git a/docs/screenshots/firefox3-import.jpg b/docs/screenshots/firefox3-import.jpg deleted file mode 100644 index 47fcd672f..000000000 Binary files a/docs/screenshots/firefox3-import.jpg and /dev/null differ diff --git a/docs/screenshots/firefox3-trust.jpg b/docs/screenshots/firefox3-trust.jpg deleted file mode 100644 index 50a2f3413..000000000 Binary files a/docs/screenshots/firefox3-trust.jpg and /dev/null differ diff --git a/docs/screenshots/firefox3.jpg b/docs/screenshots/firefox3.jpg deleted file mode 100644 index 6c4613b62..000000000 Binary files a/docs/screenshots/firefox3.jpg and /dev/null differ diff --git a/docs/screenshots/ios-gateway.png b/docs/screenshots/ios-gateway.png deleted file mode 100644 index 2489cba31..000000000 Binary files a/docs/screenshots/ios-gateway.png and /dev/null differ diff --git a/docs/screenshots/ios-installed.png b/docs/screenshots/ios-installed.png deleted file mode 100644 index 2071e441a..000000000 Binary files a/docs/screenshots/ios-installed.png and /dev/null differ diff --git a/docs/screenshots/ios-manual.png b/docs/screenshots/ios-manual.png deleted file mode 100644 index 3977acfe4..000000000 Binary files a/docs/screenshots/ios-manual.png and /dev/null differ diff --git a/docs/screenshots/ios-profile.png b/docs/screenshots/ios-profile.png deleted file mode 100644 index 5bcd5a0d4..000000000 Binary files a/docs/screenshots/ios-profile.png and /dev/null differ diff --git a/docs/screenshots/ios-reverse.png b/docs/screenshots/ios-reverse.png deleted file mode 100644 index 6ab5b7c09..000000000 Binary files a/docs/screenshots/ios-reverse.png and /dev/null differ diff --git a/docs/screenshots/ios-warning.png b/docs/screenshots/ios-warning.png deleted file mode 100644 index d882c514a..000000000 Binary files a/docs/screenshots/ios-warning.png and /dev/null differ diff --git a/docs/screenshots/mitmproxy-flowview.png b/docs/screenshots/mitmproxy-flowview.png deleted file mode 100644 index 154963fe2..000000000 Binary files a/docs/screenshots/mitmproxy-flowview.png and /dev/null differ diff --git a/docs/screenshots/mitmproxy-intercept-filt.png b/docs/screenshots/mitmproxy-intercept-filt.png deleted file mode 100644 index 60556ee71..000000000 Binary files a/docs/screenshots/mitmproxy-intercept-filt.png and /dev/null differ diff --git a/docs/screenshots/mitmproxy-intercept-mid.png b/docs/screenshots/mitmproxy-intercept-mid.png deleted file mode 100644 index d5b039223..000000000 Binary files a/docs/screenshots/mitmproxy-intercept-mid.png and /dev/null differ diff --git a/docs/screenshots/mitmproxy-intercept-options.png b/docs/screenshots/mitmproxy-intercept-options.png deleted file mode 100644 index 8dc4ad2cb..000000000 Binary files a/docs/screenshots/mitmproxy-intercept-options.png and /dev/null differ diff --git a/docs/screenshots/mitmproxy-intercept-result.png b/docs/screenshots/mitmproxy-intercept-result.png deleted file mode 100644 index 7d9f5c94d..000000000 Binary files a/docs/screenshots/mitmproxy-intercept-result.png and /dev/null differ diff --git a/docs/screenshots/mitmproxy-kveditor-editmode.png b/docs/screenshots/mitmproxy-kveditor-editmode.png deleted file mode 100644 index a8315ee51..000000000 Binary files a/docs/screenshots/mitmproxy-kveditor-editmode.png and /dev/null differ diff --git a/docs/screenshots/mitmproxy-kveditor.png b/docs/screenshots/mitmproxy-kveditor.png deleted file mode 100644 index 144b9701f..000000000 Binary files a/docs/screenshots/mitmproxy-kveditor.png and /dev/null differ diff --git a/docs/screenshots/mitmproxy.png b/docs/screenshots/mitmproxy.png deleted file mode 100644 index 42a10e329..000000000 Binary files a/docs/screenshots/mitmproxy.png and /dev/null differ diff --git a/docs/screenshots/osx-addcert-alwaystrust.png b/docs/screenshots/osx-addcert-alwaystrust.png deleted file mode 100644 index 4c5cc704e..000000000 Binary files a/docs/screenshots/osx-addcert-alwaystrust.png and /dev/null differ diff --git a/docs/screenshots/win7-certstore-trustedroot.png b/docs/screenshots/win7-certstore-trustedroot.png deleted file mode 100644 index e15a87f5a..000000000 Binary files a/docs/screenshots/win7-certstore-trustedroot.png and /dev/null differ diff --git a/docs/screenshots/win7-certstore.png b/docs/screenshots/win7-certstore.png deleted file mode 100644 index f8ce54bd5..000000000 Binary files a/docs/screenshots/win7-certstore.png and /dev/null differ diff --git a/docs/screenshots/win7-wizard.png b/docs/screenshots/win7-wizard.png deleted file mode 100644 index eff6ad096..000000000 Binary files a/docs/screenshots/win7-wizard.png and /dev/null differ diff --git a/docs/screenshots/winpythoninstaller.jpg b/docs/screenshots/winpythoninstaller.jpg deleted file mode 100644 index 0473c66ae..000000000 Binary files a/docs/screenshots/winpythoninstaller.jpg and /dev/null differ diff --git a/docs/scripting/api.rst b/docs/scripting/api.rst deleted file mode 100644 index 368b9ba8d..000000000 --- a/docs/scripting/api.rst +++ /dev/null @@ -1,54 +0,0 @@ -.. _api: - - -API -=== - -- Errors - - `mitmproxy.flow.Error <#mitmproxy.flow.Error>`_ -- HTTP - - `mitmproxy.http.HTTPRequest <#mitmproxy.http.HTTPRequest>`_ - - `mitmproxy.http.HTTPResponse <#mitmproxy.http.HTTPResponse>`_ - - `mitmproxy.http.HTTPFlow <#mitmproxy.http.HTTPFlow>`_ -- WebSocket - - `mitmproxy.websocket.WebSocketFlow <#mitmproxy.websocket.WebSocketFlow>`_ - - `mitmproxy.websocket.WebSocketMessage <#mitmproxy.websocket.WebSocketMessage>`_ -- Logging - - `mitmproxy.log.Log <#mitmproxy.controller.Log>`_ - - `mitmproxy.log.LogEntry <#mitmproxy.controller.LogEntry>`_ - - -Errors ------- - -.. autoclass:: mitmproxy.flow.Error - :inherited-members: - -HTTP ----- - -.. autoclass:: mitmproxy.http.HTTPRequest - :inherited-members: - -.. autoclass:: mitmproxy.http.HTTPResponse - :inherited-members: - -.. autoclass:: mitmproxy.http.HTTPFlow - :inherited-members: - -WebSocket ---------- - -.. autoclass:: mitmproxy.websocket.WebSocketFlow - :inherited-members: - -.. autoclass:: mitmproxy.websocket.WebSocketMessage - :inherited-members: - -Logging --------- - -.. autoclass:: mitmproxy.log.Log - :inherited-members: -.. autoclass:: mitmproxy.log.LogEntry - :inherited-members: diff --git a/docs/scripting/events.rst b/docs/scripting/events.rst deleted file mode 100644 index d8b1fbb8e..000000000 --- a/docs/scripting/events.rst +++ /dev/null @@ -1,248 +0,0 @@ -.. _events: - -Events -======= - -General -------- - -.. list-table:: - :widths: 40 60 - :header-rows: 0 - - * - .. py:function:: configure(options, updated) - - Called once on startup, and whenever options change. - - *options* - An ``options.Options`` object with the total current configuration - state of mitmproxy. - *updated* - A set of strings indicating which configuration options have been - updated. This contains all options when *configure* is called on - startup, and only changed options subsequently. - - * - .. py:function:: done() - - Called once when the script shuts down, either because it's been - unloaded, or because the proxy itself is shutting down. - - * - .. py:function:: log(entry) - - Called whenever an event log is added. - - *entry* - An ``controller.LogEntry`` object - ``entry.msg`` is the log text, - and ``entry.level`` is the urgency level ("debug", "info", "warn", - "error"). - - * - .. py:function:: start() - - Called once on startup, before any other events. If you return a - value from this event, it will replace the current addon. This - allows you to, "boot into" an addon implemented as a class instance - from the module level. - - * - .. py:function:: tick() - - Called at a regular sub-second interval as long as the addon is - executing. - - -Connection ----------- - -.. list-table:: - :widths: 40 60 - :header-rows: 0 - - * - .. py:function:: clientconnect(root_layer) - - Called when a client initiates a connection to the proxy. Note that a - connection can correspond to multiple HTTP requests. - - *root_layer* - The root layer (see `mitmproxy.proxy.protocol` for an explanation what - the root layer is), provides transparent access to all attributes - of the :py:class:`~mitmproxy.proxy.RootContext`. For example, - ``root_layer.client_conn.address`` gives the remote address of the - connecting client. - - * - .. py:function:: clientdisconnect(root_layer) - - Called when a client disconnects from the proxy. - - *root_layer* - The root layer object. - - * - .. py:function:: next_layer(layer) - - - Called whenever layers are switched. You may change which layer will - be used by returning a new layer object from this event. - - *layer* - The next layer, as determined by mitmpmroxy. - - * - .. py:function:: serverconnect(server_conn) - - Called before the proxy initiates a connection to the target server. - Note that a connection can correspond to multiple HTTP requests. - - *server_conn* - A ``ServerConnection`` object. It is guaranteed to have a non-None - ``address`` attribute. - - * - .. py:function:: serverdisconnect(server_conn) - - Called when the proxy has closed the server connection. - - *server_conn* - A ``ServerConnection`` object. - - -HTTP Events ------------ - -.. list-table:: - :widths: 40 60 - :header-rows: 0 - - * - .. py:function:: http_connect(flow) - - Called when we receive an HTTP CONNECT request. Setting a non 2xx - response on the flow will return the response to the client and abort - the connection. CONNECT requests and responses do not generate the - usual HTTP handler events. CONNECT requests are only valid in regular - and upstream proxy modes. - - *flow* - A ``models.HTTPFlow`` object. The flow is guaranteed to have - non-None ``request`` and ``requestheaders`` attributes. - - - * - .. py:function:: request(flow) - - Called when a client request has been received. - - *flow* - A ``models.HTTPFlow`` object. At this point, the flow is - guaranteed to have a non-None ``request`` attribute. - - * - .. py:function:: requestheaders(flow) - - Called when the headers of a client request have been received, but - before the request body is read. - - *flow* - A ``models.HTTPFlow`` object. At this point, the flow is - guaranteed to have a non-None ``request`` attribute. - - * - .. py:function:: responseheaders(flow) - - - Called when the headers of a server response have been received, but - before the response body is read. - - *flow* - A ``models.HTTPFlow`` object. At this point, the flow is - guaranteed to have a non-none ``request`` and ``response`` - attributes, however the response will have no content. - - * - .. py:function:: response(flow) - - - Called when a server response has been received. - - *flow* - A ``models.HTTPFlow`` object. At this point, the flow is - guaranteed to have a non-none ``request`` and ``response`` - attributes. The raw response body will be in ``response.body``, - unless response streaming has been enabled. - - * - .. py:function:: error(flow) - - Called when a flow error has occurred, e.g. invalid server responses, - or interrupted connections. This is distinct from a valid server HTTP - error response, which is simply a response with an HTTP error code. - - *flow* - The flow containing the error. It is guaranteed to have - non-None ``error`` attribute. - - -WebSocket Events ------------------ - -These events are called only after a connection made an HTTP upgrade with -"101 Switching Protocols". No further HTTP-related events after the handshake -are issued, only new WebSocket messages are called. - -.. list-table:: - :widths: 40 60 - :header-rows: 0 - - * - .. py:function:: websocket_handshake(flow) - - Called when a client wants to establish a WebSocket connection. The - WebSocket-specific headers can be manipulated to alter the - handshake. The ``flow`` object is guaranteed to have a non-None - ``request`` attribute. - - *flow* - The flow containing the HTTP WebSocket handshake request. The - object is guaranteed to have a non-None ``request`` attribute. - - * - .. py:function:: websocket_start(flow) - - Called when WebSocket connection is established after a successful - handshake. - - *flow* - A ``models.WebSocketFlow`` object. - - * - .. py:function:: websocket_message(flow) - - - Called when a WebSocket message is received from the client or server. The - sender and receiver are identifiable. The most recent message will be - ``flow.messages[-1]``. The message is user-modifiable and is killable. - A message is either of TEXT or BINARY type. - - *flow* - A ``models.WebSocketFlow`` object. - - * - .. py:function:: websocket_end(flow) - - Called when WebSocket connection ends. - - *flow* - A ``models.WebSocketFlow`` object. - - * - .. py:function:: websocket_error(flow) - - Called when a WebSocket error occurs - e.g. the connection closing - unexpectedly. - - *flow* - A ``models.WebSocketFlow`` object. - - -TCP Events ----------- - -These events are called only if the connection is in :ref:`TCP mode -`. So, for instance, TCP events are not called for ordinary HTTP/S -connections. - -.. list-table:: - :widths: 40 60 - :header-rows: 0 - - - * - .. py:function:: tcp_start(flow) - - Called when TCP streaming starts. - - *flow* - A ``models.TCPFlow`` object. - - * - .. py:function:: tcp_message(flow) - - - Called when a TCP payload is received from the client or server. The - sender and receiver are identifiable. The most recent message will be - ``flow.messages[-1]``. The message is user-modifiable. - - *flow* - A ``models.TCPFlow`` object. - - * - .. py:function:: tcp_end(flow) - - Called when TCP streaming ends. - - *flow* - A ``models.TCPFlow`` object. - - * - .. py:function:: tcp_error(flow) - - Called when a TCP error occurs - e.g. the connection closing - unexpectedly. - - *flow* - A ``models.TCPFlow`` object. diff --git a/docs/scripting/overview.rst b/docs/scripting/overview.rst deleted file mode 100644 index 5ceb5da36..000000000 --- a/docs/scripting/overview.rst +++ /dev/null @@ -1,148 +0,0 @@ -.. _overview: - -Overview -======== - -Mitmproxy has a powerful scripting API that allows you to control almost any -aspect of traffic being proxied. In fact, much of mitmproxy's own core -functionality is implemented using the exact same API exposed to scripters (see -:src:`mitmproxy/addons`). - - -A simple example ----------------- - -Scripting is event driven, with named handlers on the script object called at -appropriate points of mitmproxy's operation. Here's a complete mitmproxy script -that adds a new header to every HTTP response before it is returned to the -client: - -.. literalinclude:: ../../examples/simple/add_header.py - :caption: :src:`examples/simple/add_header.py` - :language: python - -All events that deal with an HTTP request get an instance of `HTTPFlow -`_, which we can use to manipulate the -response itself. We can now run this script using mitmdump, and the new header -will be added to all responses passing through the proxy: - ->>> mitmdump -s add_header.py - - -Examples --------- - -A collection of addons that demonstrate popular features can be found at :src:`examples/simple`. - - -Using classes -------------- - -In the example above, the script object is the ``add_header`` module itself. -That is, the handlers are declared at the global level of the script. This is -great for quick hacks, but soon becomes limiting as scripts become more -sophisticated. - -When a script first starts up, the `start `_, event is -called before anything else happens. You can replace the current script object -by returning it from this handler. Here's how this looks when applied to the -example above: - -.. literalinclude:: ../../examples/simple/add_header_class.py - :caption: :src:`examples/simple/add_header_class.py` - :language: python - -So here, we're using a module-level script to "boot up" into a class instance. -From this point on, the module-level script is removed from the handler chain, -and is replaced by the class instance. - - -Handling arguments ------------------- - - -FIXME - - -Logging and the context ------------------------ - -Scripts should not output straight to stderr or stdout. Instead, the `log -`_ object on the ``ctx`` context module -should be used, so that the mitmproxy host program can handle output -appropriately. So, mitmdump can print colorised script output to the terminal, -and mitmproxy console can place script output in the event buffer. - -Here's how this looks: - -.. literalinclude:: ../../examples/simple/log_events.py - :caption: :src:`examples/simple/log_events.py` - :language: python - -The ``ctx`` module also exposes the mitmproxy master object at ``ctx.master`` -for advanced usage. - - -Running scripts on saved flows ------------------------------- - -When a flow is loaded from disk, the sequence of events that the flow would -have gone through on the wire is partially replayed. So, for instance, an HTTP -flow loaded from disk will trigger `requestheaders -`_, `request `_, -`responseheaders `_ and `response -`_ in order. We can use this behaviour to transform saved -traffic using scripts. For example, we can invoke the replacer script from -above on saved traffic as follows: - ->>> mitmdump -dd -s "./arguments.py html fakehtml" -r saved -w changed - -This command starts the ``arguments`` script, reads all the flows from -``saved`` transforming them in the process, then writes them all to -``changed``. - -The mitmproxy console tool provides interactive ways to run transforming -scripts on flows - for instance, you can run a one-shot script on a single flow -through the ``|`` (pipe) shortcut. - - -Concurrency ------------ - -The mitmproxy script mechanism is single threaded, and the proxy blocks while -script handlers execute. This hugely simplifies the most common case, where -handlers are light-weight and the blocking doesn't have a performance impact. -It's possible to implement a concurrent mechanism on top of the blocking -framework, and mitmproxy includes a handy example of this that is fit for most -purposes. You can use it as follows: - -.. literalinclude:: ../../examples/complex/nonblocking.py - :caption: :src:`examples/complex/nonblocking.py` - :language: python - - -Testing -------- - -Mitmproxy includes a number of helpers for testing addons. The -``mitmproxy.test.taddons`` module contains a context helper that takes care of -setting up and tearing down the addon event context. The -``mitmproxy.test.tflow`` module contains helpers for quickly creating test -flows. Pydoc is the canonical reference for these modules, and mitmproxy's own -test suite is an excellent source of examples of usage. Here, for instance, is -the mitmproxy unit tests for the `anticache` option, demonstrating a good -cross-section of the test helpers: - -.. literalinclude:: ../../test/mitmproxy/addons/test_anticache.py - :caption: :src:`test/mitmproxy/addons/test_anticache.py` - :language: python - - -Developing scripts ------------------- - -Mitmproxy monitors scripts for modifications, and reloads them on change. When -this happens, the script is shut down (the `done `_ event is -called), and the new instance is started up as if the script had just been -loaded (the `start `_ and `configure -`_ events are called). diff --git a/docs/scripts/filters.py b/docs/scripts/filters.py new file mode 100755 index 000000000..e61733a0c --- /dev/null +++ b/docs/scripts/filters.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +from mitmproxy import flowfilter + + +print("") +for i in flowfilter.help: + print("" % i) +print("
%s%s
") \ No newline at end of file diff --git a/docs/src/config.toml b/docs/src/config.toml new file mode 100644 index 000000000..c9cecd8b0 --- /dev/null +++ b/docs/src/config.toml @@ -0,0 +1,9 @@ +baseURL = "" +languageCode = "en-us" +title = "mitmproxy.org docs" +theme = "mitmproxydocs" +publishDir = "../public" +RelativeURLs = true + +[indexes] + tag = "tags" diff --git a/docs/introduction.rst b/docs/src/content/_index.md similarity index 85% rename from docs/introduction.rst rename to docs/src/content/_index.md index c33cfe483..a977e2db2 100644 --- a/docs/introduction.rst +++ b/docs/src/content/_index.md @@ -1,5 +1,11 @@ -Introduction -============ +--- +title: "Introduction" +menu: + overview: + weight: 1 +--- + +# Introduction **mitmproxy** is an interactive man-in-the-middle proxy for HTTP and HTTPS with a console interface. @@ -9,10 +15,10 @@ with a console interface. **mitmweb** is a web-based interface for mitmproxy. Documentation, tutorials and distribution packages can be found on the -mitmproxy website: `mitmproxy.org `_ +[mitmproxy website](https://mitmproxy.org). -.. rubric:: Features +## Features - Intercept HTTP & HTTPS requests and responses and modify them on the fly - Save complete HTTP conversations for later replay and analysis @@ -23,3 +29,4 @@ mitmproxy website: `mitmproxy.org `_ - Make scripted changes to HTTP traffic using Python - SSL/TLS certificates for interception are generated on the fly - And much, much more... + diff --git a/docs/src/content/addons-commands.md b/docs/src/content/addons-commands.md new file mode 100644 index 000000000..f689d763f --- /dev/null +++ b/docs/src/content/addons-commands.md @@ -0,0 +1,134 @@ +--- +title: "Commands" +menu: + addons: + weight: 4 +--- + +# Commands + +Commands allow users to actively interact with addons - querying their state, +commanding them to perform actions, and having them transform data. Like +[options]({{< relref addons-options >}}), commands are typed, and both +invocations and data returned from commands are checked at runtime. Commands are +a very powerful construct - for instance, all user interaction in mitmproxy +console are built by binding commands to keys. + + +## Simple example + +Let's begin with a simple example. + +{{< example src="examples/addons/commands-simple.py" lang="py" >}} + +To see this example in action, start mitmproxy console with the addon loaded: + +{{< highlight bash >}} +> mitmproxy -s ./examples/addons/commands-simple.py +{{< /highlight >}} + +Now, make sure the event log is showing, and then execute the command at the +prompt (started by typing ":"): + +{{< highlight none>}} +:myaddon.inc +{{< /highlight >}} + +Notice that tab completion works - our addon command has complete parity with +builtin commands. There are a few things to note about this example: + +- Commands are declared through the `command.command` decorator. Each command + has a unique name - by convention, we use period-separated names, with the + name of the addon as a prefix. +- Annotating commands with types is mandatory, including the return type (in + this case `None`). This allows mitmproxy to support addon commands throughout + its toolset - runtime invocations are type checked, addon commands are + included in the built-in help, the command editor in mitmproxy console can + perform sophisticated completion and error checking, and so forth. + + +## Working with flows + +Since command arguments are typed, we can provide special conveniences for +working with certain important data types. The most useful of these are the +`Flows` classes that represent mitmproxy traffic. + +Consider the following addon: + +{{< example src="examples/addons/commands-flows.py" lang="py" >}} + +The `myaddon.addheader` command is quite simple: it takes a sequence of flows, +and adds a header to every request. The really interesting aspect of this +example is how users specify flows. Because mitmproxy can inspect the type +signature, it can expand a text flow selector into a sequence of flows for us +transparently. This means that the user has the full flexibility of [flow +filters]({{< relref addons-options >}}) available. Let's try it out. + +Start by loading the addon into mitmproxy and sending some traffic through so we +have flows to work with: + +{{< highlight bash >}} +> mitmproxy -s ./examples/addons/commands-flows.py +{{< /highlight >}} + +We can now invoke our toy command in various ways. Let's begin by running it +just on the currently focused flow: + +{{< highlight none >}} +:myaddon.addheader @focus +{{< /highlight >}} + +We can also invoke it on all flows: + +{{< highlight none >}} +:myaddon.addheader @all +{{< /highlight >}} + +Or only flows from **google.com**: + +{{< highlight none >}} +:myaddon.addheader ~d google.com +{{< /highlight >}} + +What's more, we can trivially bind these commands to keyboard shortcuts within +mitmproxy if we plan to use them frequently. Flow selectors combined with +commands are amazingly powerful, and lets us build and expose re-usable functions +for operating on flows. + + +## Paths + +Commands can take an arbitrary number of arguments. Let's build on the previous +example to illustrate this, and also demonstrate another special type: paths. + +{{< example src="examples/addons/commands-paths.py" lang="py" >}} + +Our command calculates a histogram of the domains in the specified set of flows, +and writes it to a path which is specified as the second argument to the +command. Try invoking it like this: + +{{< highlight none >}} +:myaddon.histogram @all /tmp/xxx +{{< /highlight >}} + +Notice that mitmproxy provides tab completion both for the flow specification +and the path. + + + +## Supported Types + +The following types are supported for options. If you need to use a type not +listed here, please send us a pull request. + +- Primitive types: `str`, `int`, `bool` +- Sequences: `typing.Sequence[str]` +- Flows and flow sequences: `flow.Flow` and `typing.Sequence[flow.Flow]` +- Multiple choice strings: `types.Choice` +- Meta-types: `types.Command` and `types.Arg`. These are for constructing + commands that invoke other commands. This is most commonly useful in + keybinding - see the built-in mitmproxy console keybindings for a rich suite + of examples. +- Data types: `types.CutSpec` and `types.Data`. The cuts mechanism is in alpha + at the moment, and provides a convenient way to snip up flow data. +- Path: `types.Path` diff --git a/docs/src/content/addons-events.md b/docs/src/content/addons-events.md new file mode 100644 index 000000000..489ea342f --- /dev/null +++ b/docs/src/content/addons-events.md @@ -0,0 +1,24 @@ +--- +title: "Events" +menu: + addons: + weight: 2 +--- + +# Events + +Addons hook into mitmproxy's internal mechanisms through events. These are +implemented on addons as methods with a set of well-known names. Many events +receive `Flow` objects as arguments - by modifying these objects, addons can +change traffic on the fly. For instance, here is an addon that adds a response +header with a count of the number of responses seen: + +{{< example src="examples/addons/addheader.py" lang="py" >}} + + +## Supported Events + +Below is an addon class that implements stubs for all events. We've added +annotations to illustrate the argument types for the various events. + +{{< example src="examples/addons/events.py" lang="py" >}} \ No newline at end of file diff --git a/docs/src/content/addons-options.md b/docs/src/content/addons-options.md new file mode 100644 index 000000000..930847d02 --- /dev/null +++ b/docs/src/content/addons-options.md @@ -0,0 +1,101 @@ +--- +title: "Options" +menu: + addons: + weight: 3 +--- + +# Options + +At the heart of mitmproxy is a global options store, containing the settings +that determine the behaviour of both mitmproxy and its addons. Options can be +read from a configuration file, set on the command-line and changed +interactively by users on the fly. + +All options are annotated with one of a set of supported types. Mitmproxy knows +how to serialise and deserialise these types, and has standard ways of +presenting typed values for editing in interactive programs. Attempting to set a +value with the wrong type will result in an error. This means that addon options +get full support throughout mitmproxy's toolchain simply by declaring a type. + + +## Simple example + +{{< example src="examples/addons/options-simple.py" lang="py" >}} + +The `load` event receives an instance of `mitmproxy.addonmanager.Loader`, which +allows addons declare options and commands. In this case, the addon adds a +single `addheader` option with type `bool`. Let's try this out by running the +script in mitmproxy console: + +{{< highlight bash >}} +> mitmproxy -s ./examples/addons/options-simple.py +{{< /highlight >}} + +You can now use CURL to make a request through the proxy like this: + +{{< highlight bash >}} +> env http_proxy=http://localhost:8080 curl -I http://google.com +{{< /highlight >}} + +If you run this request immediately, you'll notice that no count header is +added. This is because our default value for the option was `false`. Press `O` +to enter the options editor, and find the `addheader` option. You'll notice that +mitmproxy knows this is a boolean, and lets you toggle the value between true +and false. Set the value to `true`, and you should see a result something like +this: + +{{< highlight bash >}} +> env http_proxy=http://localhost:8080 curl -I http://google.com +HTTP/1.1 301 Moved Permanently +Location: http://www.google.com/ +Content-Length: 219 +count: 1 +{{< /highlight >}} + +When this addon is loaded, the `addheader` setting is available in the +persistent [YAML configuration file]({{< relref "concepts-options" >}}). You can +also over-ride the value directly from the command-line for any of the tools +using the `--set` flag: + +{{< highlight bash >}} +mitmproxy -s ./examples/addons/options-simple.py --set addheader=true +{{< /highlight >}} + + +## Handling configuration updates + +Sometimes, simply testing the value of an option from an event is not +sufficient. Instead, we want to act immediately when an option is changed by the +user. This is what the `configure` event is for - when it is triggered, it +receives a set of changed options. An addon can check if an option is in this +set, and then read the value from the options object on the context. + +One common use for this function is to check that an option is valid, and give +the user feedback if it's not. If an `exceptions.OptionsError` exception is +raised during configure, all the changes in the update are automatically rolled +back, and an error is displayed to the user. Let's see an example. + +{{< example src="examples/addons/options-configure.py" lang="py" >}} + +There are a few things to note here. First, the option we add uses +`typing.Optional`. This signals to mitmproxy that `None` is a valid value for +this option - that is, it can be unset. Second, the `configure` method is first +called with our default value (`None`), and then later with an updated value if +the option is changed. If we try to load the script with an incorrect value, we +now see an error: + +{{< highlight none >}} +> mitmdump -s ./examples/addons/options-configure.py --set addheader=1000 +Loading script: ./examples/addons/options-configure.py +/Users/cortesi/mitmproxy/mitmproxy/venv/bin/mitmdump: addheader must be <= 100 +{{< /highlight >}} + + +## Supported Types + +The following types are supported for options. + +- Primitive types - `str`, `int`, `float`, `bool`. +- Optional values, annotated using `typing.Optional`. +- Sequences of values, annotated using `typing.Sequence`. diff --git a/docs/src/content/addons-overview.md b/docs/src/content/addons-overview.md new file mode 100644 index 000000000..6aa1207e7 --- /dev/null +++ b/docs/src/content/addons-overview.md @@ -0,0 +1,74 @@ +--- +title: "Addons" +menu: + addons: + weight: 1 +--- + +# Addons + +Mitmproxy's addon mechanism consists of a set of APIs that support components of +any complexity. Addons interact with mitmproxy by responding to **events**, +which allow them to hook into and change mitmproxy's behaviour. They are +configured through **[options]({{< relref concepts-options >}})**, which can be +set in mitmproxy's config file, changed interactively by users, or passed on the +command-line. Finally, they can expose **commands**, which allows users to +invoke their actions either directly or by binding them to keys in the +interactive tools. + +Addons are an exceptionally powerful part of mitmproxy. In fact, much of +mitmproxy's own functionality is defined in [a suite of built-in +addons](https://github.com/mitmproxy/mitmproxy/tree/master/mitmproxy/addons), +implementing everything from functionality like [anticaching]({{< relref +"overview-features#anticache" >}}) and [sticky cookies]({{< relref +"overview-features#sticky-cookies" >}}) to our onboarding webapp. The built-in +addons make for instructive reading, and you will quickly see that quite complex +functionality can often boil down to a very small, completely self-contained +modules. Mitmproxy provides the exact same set of facilities it uses for its own +functionality to third-party scripters and extenders. + +This document will show you how to build addons using **events**, **options** +and **commands**. However, this is not an API manual, and the mitmproxy source +code remains the canonical reference. One easy way to explore the API from the +command-line is to use [pydoc](https://docs.python.org/3/library/pydoc.html). +Here, for example, is a command that shows the API documentation for the +mitmproxy's HTTP flow classes: + +{{< highlight bash >}} +pydoc mimtproxy.http +{{< /highlight >}} + +You will be referring to the mitmproxy API documentation frequently, so keep +**pydoc** or an equivalent handy. + +# Anatomy of an addon + +{{< example src="examples/addons/anatomy.py" lang="py" >}} + +Above is a simple addon that keeps track of the number of flows (or more +specifically HTTP requests) we've seen. Every time it sees a new flow, it uses +mitmproxy's internal logging mechanism to announce its tally. The output can be +found in the event log in the interactive tools, or on the console in mitmdump. + +Take it for a spin and make sure that it does what it's supposed to, by loading +it into your mitmproxy tool of choice. We'll use mitmpdump in these examples, +but the flag is identical for all tools: + +{{< highlight bash >}} +> mitmdump -s ./anatomy.py +{{< /highlight >}} + +Here are a few things to note about the code above: + +- Mitmproxy picks up the contents of the `addons` global list and loads what it + finds into the addons mechanism. +- Addons are just objects - in this case our addon is an instance of `Counter`. +- The `request` method is an example of an **event**. Addons simply implement a + method for each event they wan to handle. Each event has a signature + consisting of arguments that are passed to the method. For `request`, this is + an instance of `mitmproxy.http.HTTPFlow`. +- Finally, the `ctx` module is a holdall module that exposes a set of standard + objects that are commonly used in addons. We could pass a `ctx` object as the + first parameter to every event, but we've found it neater to just expose it as + an importable global. In this case, we're using the `ctx.log` object to do our + logging. \ No newline at end of file diff --git a/docs/src/content/addons-scripting.md b/docs/src/content/addons-scripting.md new file mode 100644 index 000000000..e31d291a9 --- /dev/null +++ b/docs/src/content/addons-scripting.md @@ -0,0 +1,17 @@ +--- +title: "Scripting" +menu: + addons: + weight: 5 +--- + +# Scripting + +Sometimes, we would like to write a quick script without going through the +trouble of creating a class. The addons mechanism has a shorthand that allows a +module as a whole to be treated as an addon object. This lets us place event +handler functions in the module scope. For instance, here is a complete script +that adds a header to every request. + + +{{< example src="examples/addons/scripting.py" lang="py" >}} \ No newline at end of file diff --git a/docs/src/content/concepts-certificates.md b/docs/src/content/concepts-certificates.md new file mode 100644 index 000000000..6956ff3f6 --- /dev/null +++ b/docs/src/content/concepts-certificates.md @@ -0,0 +1,164 @@ +--- +title: "Certificates" +menu: + concepts: + weight: 3 +--- + +# About Certificates + +Mitmproxy can decrypt encrypted traffic on the fly, as long as the client trusts +its built-in certificate authority. Usually this means that the mitmproxy CA +certificates have to be installed on the client device. + +## Quick Setup + +By far the easiest way to install the mitmproxy certificates is to use the +built-in certificate installation app. To do this, just start mitmproxy and +configure your target device with the correct proxy settings. Now start a +browser on the device, and visit the magic domain **mitm.it**. You should see +something like this: + +{{< figure src="/certinstall-webapp.png" >}} + +Click on the relevant icon, follow the setup instructions for the platform +you're on and you are good to go. + +## Installing the mitmproxy CA certificate manually + +Sometimes using the quick install app is not an option - Java or the iOS +Simulator spring to mind - or you just need to do it manually for some other +reason. Below is a list of pointers to manual certificate installation +documentation for some common platforms. The mitmproxy CA cert is located in +`~/.mitmproxy` after it has been generated at the first start of mitmproxy. + +- [IOS](http://jasdev.me/intercepting-ios-traffic) On + iOS 10.3 and onwards, you also need to enable full trust for the mitmproxy + root certificate: + 1. Go to Settings > General > About > Certificate Trust Settings. + 2. Under "Enable full trust for root certificates", turn on trust for + the mitmproxy certificate. +- [IOS Simulator](https://github.com/ADVTOOLS/ADVTrustStore#how-to-use-advtruststore) +- [Java](https://docs.oracle.com/cd/E19906-01/820-4916/geygn/index.html) +- [Android/Android Simulator](http://wiki.cacert.org/FAQ/ImportRootCert#Android_Phones_.26_Tablets) +- [Windows](https://web.archive.org/web/20160612045445/http://windows.microsoft.com/en-ca/windows/import-export-certificates-private-keys#1TC=windows-7) +- [Windows (automated)](https://technet.microsoft.com/en-us/library/cc732443.aspx) + +{{< highlight bash >}} +certutil.exe -importpfx Root mitmproxy-ca-cert.p12 +{{< / highlight >}} + +- [Mac OS X](https://support.apple.com/kb/PH7297?locale=en_US) +- [Ubuntu/Debian]( https://askubuntu.com/questions/73287/how-do-i-install-a-root-certificate/94861#94861) +- [Mozilla Firefox](https://wiki.mozilla.org/MozillaRootCertificate#Mozilla_Firefox) +- [Chrome on Linux](https://stackoverflow.com/a/15076602/198996) + +## The mitmproxy certificate authority + +The first time **mitmproxy** or **mitmdump** is run, the mitmproxy Certificate +Authority (CA) is created in the config directory (`~/.mitmproxy` by default). +This CA is used for on-the-fly generation of dummy certificates for each of the +SSL sites that your client visits. Since your browser won't trust the mitmproxy +CA out of the box, you will see an SSL certificate warning every time you visit +a new SSL domain through mitmproxy. When you are testing a single site through a +browser, just accepting the bogus SSL cert manually is not too much trouble, but +there are a many circumstances where you will want to configure your testing +system or browser to trust the mitmproxy CA as a signing root authority. For +security reasons, the mitmproxy CA is generated uniquely on the first start and +is not shared between mitmproxy installations on different devices. + +### Certificate Pinning + +Some applications employ [Certificate +Pinning](https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning) to prevent +man-in-the-middle attacks. This means that **mitmproxy** and **mitmdump's** +certificates will not be accepted by these applications without modifying them. +It is recommended to use the passthrough feature in order to prevent +**mitmproxy** and **mitmdump** from intercepting traffic to these specific +domains. If you want to intercept the pinned connections, you need to patch the +application manually. For Android and (jailbroken) iOS devices, various tools +exist to accomplish this. + +## CA and cert files + +The files created by mitmproxy in the .mitmproxy directory are as follows: + +| | | +| --------------------- | ------------------------------------------------------------------------------------ | +| mitmproxy-ca.pem | The certificate **and the private key** in PEM format. | +| mitmproxy-ca-cert.pem | The certificate in PEM format. Use this to distribute on most non-Windows platforms. | +| mitmproxy-ca-cert.p12 | The certificate in PKCS12 format. For use on Windows. | +| mitmproxy-ca-cert.cer | Same file as .pem, but with an extension expected by some Android devices. | + +## Using a custom certificate + +You can use your own (leaf) certificate by passing the `--cert +[domain=]path_to_certificate` option to mitmproxy. Mitmproxy then uses the +provided certificate for interception of the specified domain instead of +generating a certificate signed by its own CA. + +The certificate file is expected to be in the PEM format. You can include +intermediary certificates right below your leaf certificate, so that your PEM +file roughly looks like this: + + -----BEGIN PRIVATE KEY----- + + -----END PRIVATE KEY----- + -----BEGIN CERTIFICATE----- + + -----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- + + -----END CERTIFICATE----- + +For example, you can generate a certificate in this format using these +instructions: + +{{< highlight bash >}} +openssl genrsa -out cert.key 2048 +# (Specify the mitm domain as Common Name, e.g. \*.google.com) +openssl req -new -x509 -key cert.key -out cert.crt +cat cert.key cert.crt \> cert.pem +{{< / highlight >}} + +Now, you can run mitmproxy with the generated certificate: + +**For all domain names** + +{{< highlight bash >}} +mitmproxy --cert *=cert.pem +{{< / highlight >}} + +**For specific domain names** + +{{< highlight bash >}} +mitmproxy --cert *.example.com=cert.pem +{{< / highlight >}} + +**Note:** `*.example.com` is for all the subdomains. You can also use +`www.example.com` for a particular subdomain. + +## Using a custom certificate authority + +By default, mitmproxy will use `~/.mitmproxy/mitmproxy-ca.pem` as the +certificate authority to generate certificates for all domains for which +no custom certificate is provided (see above). You can use your own +certificate authority by passing the `--cadir DIRECTORY` option to +mitmproxy. Mitmproxy will then look for `mitmproxy-ca.pem` in the +specified directory. If no such file exists, it will be generated +automatically. + +## Using a client side certificate + +You can use a client certificate by passing the `--client-certs DIRECTORY|FILE` +option to mitmproxy. Using a directory allows certs to be selected based on +hostname, while using a filename allows a single specific certificate to be used +for all SSL connections. Certificate files must be in the PEM format and should +contain both the unencrypted private key and the certificate. + +### Multiple certs by Hostname + +You can specify a directory to `--client-certs`, in which case the matching +certificate is looked up by filename. So, if you visit example.org, mitmproxy +looks for a file named `example.org.pem` in the specified directory and uses +this as the client cert. diff --git a/docs/src/content/concepts-commands.md b/docs/src/content/concepts-commands.md new file mode 100644 index 000000000..72f172524 --- /dev/null +++ b/docs/src/content/concepts-commands.md @@ -0,0 +1,72 @@ +--- +title: "Commands" +menu: + concepts: + weight: 6 +--- + +# Commands + +Commands are the mechanism that allows users to actively interact with addons. +Perhaps the most prominent example of this is the mitmproxy console user +interface - every interaction in this tool consists of commands bound to keys. +Commands also form a flexible and very powerful way to interact with mitmproxy +from the command prompt. In mitmproxy console you can enter the command prompt +with the `:` key. The prompt has intelligent tab completion for command names +and many of the built-in argument types - give it a try. + +The canonical reference for commands is the `--commands` flag, which is exposed +by each of the mitmproxy tools. Passing this flag will dump an annotated list of +all registered commands, their arguments and their return values to screen. In +mimtproxy console you can also view a palette of all commands in the command +browser (by default accessible with the `C` key binding). + + +# Working with flows + +Many of mitmproxy's commands take flows as arguments. For instance, the +signature for the client replay commands looks like this: + +{{< highlight none >}} +replay.client [flow] +{{< /highlight >}} + + +That means that it expects a sequence of one or more flows. This is where [flow +specifications]({{< relref concepts-filters >}}) come in - mitmproxy will +intelligently expand a flexible flow selection language to a list of flows when +invoking commands. + +Fire up mitmproxy console, and intercept some traffic so we have flows to work +with. Now type the following command: + +{{< highlight none >}} +:replay.client @focus +{{< /highlight >}} + +Make sure you try using tab completion for the command name and the flow +specification. The `@focus` specifiers expands to the currently focused flow, so +you should see this flow replay. However, replay can take any number of flows. +Try the following command: + +{{< highlight none >}} +:replay.client @all +{{< /highlight >}} + +Now you should see all flows replay one by one. We have the full power of the +mitmproxy filter language at our disposal here, so we could also, for example, +just replay flows for a specific domain: + +{{< highlight none >}} +:replay.client "~d google.com" +{{< /highlight >}} + + + + + + + + + + diff --git a/docs/src/content/concepts-filters.md b/docs/src/content/concepts-filters.md new file mode 100644 index 000000000..ada24e327 --- /dev/null +++ b/docs/src/content/concepts-filters.md @@ -0,0 +1,81 @@ +--- +title: "Filter expressions" +menu: + concepts: + weight: 4 +--- + +# Filter expressions + +Many commands in the mitmproxy tool make use of filter expressions. Filter +expressions consist of the following operators: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
~aMatch asset in response: CSS, Javascript, Flash, images.
~b regexBody
~bq regexRequest body
~bs regexResponse body
~c intHTTP response code
~d regexDomain
~dst regexMatch destination address
~eMatch error
~h regexHeader
~hq regexRequest header
~hs regexResponse header
~httpMatch HTTP flows
~m regexMethod
~markedMatch marked flows
~qMatch request with no response
~sMatch response
~src regexMatch source address
~t regexContent-type header
~tcpMatch TCP flows
~tq regexRequest Content-Type header
~ts regexResponse Content-Type header
~u regexURL
~websocketMatch WebSocket flows
!unary not
&and
|or
(...)grouping
+ + +- Regexes are Python-style +- Regexes can be specified as quoted strings +- Header matching (~h, ~hq, ~hs) is against a string of the form "name: value". +- Strings with no operators are matched against the request URL. +- The default binary operator is &. + + +## View flow selectors + +In interactive contexts, mitmproxy has a set of convenient flow selectors that +operate on the current view: + + + + + + + + +
@allAll flows
@focusThe currently focused flow
@shownAll flows currently shown
@hiddenAll flows currently hidden
@markedAll marked flows
@unmarkedAll unmarked flows
+ +These are frequently used in commands and key bindings. + + +## Examples + +URL containing "google.com": + + google\.com + +Requests whose body contains the string "test": + + ~q ~b test + +Anything but requests with a text/html content type: + + !(~q & ~t "text/html") diff --git a/docs/src/content/concepts-howmitmproxyworks.md b/docs/src/content/concepts-howmitmproxyworks.md new file mode 100644 index 000000000..d747d71c3 --- /dev/null +++ b/docs/src/content/concepts-howmitmproxyworks.md @@ -0,0 +1,252 @@ +--- +title: "How mitmproxy works" +menu: + concepts: + weight: 1 +--- + +# How mitmproxy works + +Mitmproxy is an enormously flexible tool. Knowing exactly how the proxying +process works will help you deploy it creatively, and take into account its +fundamental assumptions and how to work around them. This document explains +mitmproxy's proxy mechanism in detail, starting with the simplest unencrypted +explicit proxying, and working up to the most complicated interaction +-transparent proxying of TLS-protected traffic[^1] in the presence of [Server +Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication). + +## Explicit HTTP + +Configuring the client to use mitmproxy as an explicit proxy is the +simplest and most reliable way to intercept traffic. The proxy protocol +is codified in the [HTTP RFC](https://tools.ietf.org/html/rfc7230), so +the behaviour of both the client and the server is well defined, and +usually reliable. In the simplest possible interaction with mitmproxy, a +client connects directly to the proxy, and makes a request that looks +like this: + +{{< highlight http >}} +GET http://example.com/index.html HTTP/1.1 +{{< / highlight >}} + +This is a proxy GET request - an extended form of the vanilla HTTP GET +request that includes a schema and host specification, and it includes +all the information mitmproxy needs to proceed. + + +{{< figure src="/schematics/how-mitmproxy-works-explicit.png" title="Explicit" >}} + + +1. The client connects to the proxy and makes a request. +2. Mitmproxy connects to the upstream server and simply forwards the request on. + + +## Explicit HTTPS + +The process for an explicitly proxied HTTPS connection is quite +different. The client connects to the proxy and makes a request that +looks like this: + +{{< highlight http >}} +CONNECT example.com:443 HTTP/1.1 +{{< / highlight >}} + +A conventional proxy can neither view nor manipulate a TLS-encrypted +data stream, so a CONNECT request simply asks the proxy to open a pipe +between the client and server. The proxy here is just a facilitator - it +blindly forwards data in both directions without knowing anything about +the contents. The negotiation of the TLS connection happens over this +pipe, and the subsequent flow of requests and responses are completely +opaque to the proxy. + +### The MITM in mitmproxy + +This is where mitmproxy's fundamental trick comes into play. The MITM +in its name stands for Man-In-The-Middle - a reference to the process we +use to intercept and interfere with these theoretically opaque data +streams. The basic idea is to pretend to be the server to the client, +and pretend to be the client to the server, while we sit in the middle +decoding traffic from both sides. The tricky part is that the +[Certificate +Authority](https://en.wikipedia.org/wiki/Certificate_authority) system +is designed to prevent exactly this attack, by allowing a trusted +third-party to cryptographically sign a server's certificates to verify +that they are legit. If this signature doesn't match or is from a +non-trusted party, a secure client will simply drop the connection and +refuse to proceed. Despite the many shortcomings of the CA system as it +exists today, this is usually fatal to attempts to MITM a TLS connection +for analysis. Our answer to this conundrum is to become a trusted +Certificate Authority ourselves. Mitmproxy includes a full CA +implementation that generates interception certificates on the fly. To +get the client to trust these certificates, we +[register mitmproxy as a trusted CA with the device +manually]({{< relref concepts-certificates >}}). + +### Complication 1: What's the remote hostname? + +To proceed with this plan, we need to know the domain name to use in the +interception certificate - the client will verify that the certificate is for +the domain it's connecting to, and abort if this is not the case. At first +blush, it seems that the CONNECT request above gives us all we need - in this +example, both of these values are "example.com". But what if the client had +initiated the connection as follows: + +{{< highlight http >}} +CONNECT 10.1.1.1:443 HTTP/1.1 +{{< / highlight >}} + +Using the IP address is perfectly legitimate because it gives us enough +information to initiate the pipe, even though it doesn't reveal the +remote hostname. + +Mitmproxy has a cunning mechanism that smooths this over - [upstream certificate +sniffing]({{< relref "overview-features#upstream-certificates" >}}). As soon as +we see the CONNECT request, we pause the client part of the conversation, and +initiate a simultaneous connection to the server. We complete the TLS handshake +with the server, and inspect the certificates it used. Now, we use the Common +Name in the upstream certificates to generate the dummy certificate for the +client. Voila, we have the correct hostname to present to the client, even if it +was never specified. + +### Complication 2: Subject Alternative Name + +Enter the next complication. Sometimes, the certificate Common Name is +not, in fact, the hostname that the client is connecting to. This is +because of the optional [Subject Alternative +Name](https://en.wikipedia.org/wiki/SubjectAltName) field in the +certificate that allows an arbitrary number of alternative domains to be +specified. If the expected domain matches any of these, the client will +proceed, even though the domain doesn't match the certificate CN. The +answer here is simple: when we extract the CN from the upstream cert, we +also extract the SANs, and add them to the generated dummy certificate. + +### Complication 3: Server Name Indication + +One of the big limitations of vanilla TLS is that each certificate +requires its own IP address. This means that you couldn't do virtual +hosting where multiple domains with independent certificates share the +same IP address. In a world with a rapidly shrinking IPv4 address pool +this is a problem, and we have a solution in the form of the [Server +Name Indication](https://en.wikipedia.org/wiki/Server_Name_Indication) +extension to the TLS protocols. This lets the client specify the remote +server name at the start of the TLS handshake, which then lets the +server select the right certificate to complete the process. + +SNI breaks our upstream certificate sniffing process, because when we +connect without using SNI, we get served a default certificate that may +have nothing to do with the certificate expected by the client. The +solution is another tricky complication to the client connection +process. After the client connects, we allow the TLS handshake to +continue until just **after** the SNI value has been passed to us. Now +we can pause the conversation, and initiate an upstream connection using +the correct SNI value, which then serves us the correct upstream +certificate, from which we can extract the expected CN and SANs. + +### Putting it all together + +Lets put all of this together into the complete explicitly proxied HTTPS +flow. + +{{< figure src="/schematics/how-mitmproxy-works-explicit-https.png" title="Explicit HTTPS" >}} + +1. The client makes a connection to mitmproxy, and issues an HTTP CONNECT + request. +2. Mitmproxy responds with a `200 Connection Established`, as if it has set up + the CONNECT pipe. +3. The client believes it's talking to the remote server, and initiates the TLS + connection. It uses SNI to indicate the hostname it is connecting to. +4. Mitmproxy connects to the server, and establishes a TLS connection using the + SNI hostname indicated by the client. +5. The server responds with the matching certificate, which contains the CN and + SAN values needed to generate the interception certificate. +6. Mitmproxy generates the interception cert, and continues the client TLS + handshake paused in step 3. +7. The client sends the request over the established TLS connection. +8. Mitmproxy passes the request on to the server over the TLS connection + initiated in step 4. + +## Transparent HTTP + +When a transparent proxy is used, the connection is redirected into a +proxy at the network layer, without any client configuration being +required. This makes transparent proxying ideal for those situations +where you can't change client behaviour - proxy-oblivious Android +applications being a common example. + +To achieve this, we need to introduce two extra components. The first is +a redirection mechanism that transparently reroutes a TCP connection +destined for a server on the Internet to a listening proxy server. This +usually takes the form of a firewall on the same host as the proxy +server - [iptables](http://www.netfilter.org/) on Linux or +[pf](https://en.wikipedia.org/wiki/PF_\(firewall\)) on OSX. Once the +client has initiated the connection, it makes a vanilla HTTP request, +which might look something like this: + +{{< highlight http >}} +GET /index.html HTTP/1.1 +{{< / highlight >}} + +Note that this request differs from the explicit proxy variation, in +that it omits the scheme and hostname. How, then, do we know which +upstream host to forward the request to? The routing mechanism that has +performed the redirection keeps track of the original destination for +us. Each routing mechanism has a different way of exposing this data, so +this introduces the second component required for working transparent +proxying: a host module that knows how to retrieve the original +destination address from the router. In mitmproxy, this takes the form +of a built-in set of +[modules](https://github.com/mitmproxy/mitmproxy/tree/master/mitmproxy/platform) +that know how to talk to each platform's redirection mechanism. Once we +have this information, the process is fairly straight-forward. + +{{< figure src="/schematics/how-mitmproxy-works-transparent.png" title="Transparent" >}} + +1. The client makes a connection to the server. +2. The router redirects the connection to mitmproxy, which is typically + listening on a local port of the same host. Mitmproxy then consults + the routing mechanism to establish what the original destination + was. +3. Now, we simply read the client's request... +4. ... and forward it upstream. + +## Transparent HTTPS + +The first step is to determine whether we should treat an incoming +connection as HTTPS. The mechanism for doing this is simple - we use the +routing mechanism to find out what the original destination port is. All +incoming connections pass through different layers which can determine +the actual protocol to use. Automatic TLS detection works for SSLv3, TLS +1.0, TLS 1.1, and TLS 1.2 by looking for a *ClientHello* message at the +beginning of each connection. This works independently of the used TCP +port. + +From here, the process is a merger of the methods we've described for +transparently proxying HTTP, and explicitly proxying HTTPS. We use the +routing mechanism to establish the upstream server address, and then +proceed as for explicit HTTPS connections to establish the CN and SANs, +and cope with SNI. + +{{< figure src="/schematics/how-mitmproxy-works-transparent-https.png" title="Transparent HTTPS" >}} + +1. The client makes a connection to the server. +2. The router redirects the connection to mitmproxy, which is typically + listening on a local port of the same host. Mitmproxy then consults the + routing mechanism to establish what the original destination was. +3. The client believes it's talking to the remote server, and + initiates the TLS connection. It uses SNI to indicate the hostname + it is connecting to. +4. Mitmproxy connects to the server, and establishes a TLS connection + using the SNI hostname indicated by the client. +5. The server responds with the matching certificate, which contains + the CN and SAN values needed to generate the interception + certificate. +6. Mitmproxy generates the interception cert, and continues the client + TLS handshake paused in step 3. +7. The client sends the request over the established TLS connection. +8. Mitmproxy passes the request on to the server over the TLS + connection initiated in step 4. + +### Footnotes + +[^1]: The use of "TLS" refers to both SSL (outdated and insecure) and TLS (1.0 + and up) in the generic sense, unless otherwise specified. diff --git a/docs/src/content/concepts-modes.md b/docs/src/content/concepts-modes.md new file mode 100644 index 000000000..86bb7b0f9 --- /dev/null +++ b/docs/src/content/concepts-modes.md @@ -0,0 +1,215 @@ +--- +title: "Modes of operation" +menu: + concepts: + weight: 2 +--- + +# Modes of Operation + +- [Regular](#regular-proxy) (the default) +- [Transparent](#transparent-proxy) +- [Reverse Proxy](#reverse-proxy) +- [Upstream Proxy](#upstream-proxy) +- [SOCKS Proxy](#socks-proxy) + +Now, which one should you pick? Use this flow chart: + +{{< figure src="/schematics/proxy-modes-flowchart.png" >}} + + +## Regular Proxy + +Mitmproxy's regular mode is the simplest and the easiest to set up. + +1. Start mitmproxy. +2. Configure your client to use mitmproxy by explicitly setting an HTTP + proxy. +3. Quick Check: You should already be able to visit an unencrypted HTTP + site through the proxy. +4. Open the magic domain **mitm.it** and install the certificate for your + device. + +{{< note >}} +Unfortunately, some applications bypass the system HTTP proxy settings - +Android applications are a common example. In these cases, you need to +use mitmproxy's transparent mode. +{{< /note >}} + +If you are proxying an external device, your network will probably look +like this: + +{{< figure src="/schematics/proxy-modes-regular.png" >}} + +The square brackets signify the source and destination IP addresses. +Your client explicitly connects to mitmproxy and mitmproxy explicitly +connects to the target server. + +## Transparent Proxy + +In transparent mode, traffic is directed into a proxy at the network +layer, without any client configuration required. This makes transparent +proxying ideal for situations where you can't change client behaviour. +In the graphic below, a machine running mitmproxy has been inserted +between the router and the internet: + +{{< figure src="/schematics/proxy-modes-transparent-1.png" >}} + +The square brackets signify the source and destination IP addresses. +Round brackets mark the next hop on the *Ethernet/data link* layer. This +distinction is important: when the packet arrives at the mitmproxy +machine, it must still be addressed to the target server. This means +that Network Address Translation should not be applied before the +traffic reaches mitmproxy, since this would remove the target +information, leaving mitmproxy unable to determine the real destination. + +{{< figure src="/schematics/proxy-modes-transparent-wrong.png" title="Modes Transparent Wrong" >}} + +### Common Configurations + +There are many ways to configure your network for transparent proxying. +We'll look at two common scenarios: + +1. Configuring the client to use a custom gateway/router/"next hop" +2. Implementing custom routing on the router + +In most cases, the first option is recommended due to its ease of use. + +#### (a) Custom Gateway + +One simple way to get traffic to the mitmproxy machine with the +destination IP intact, is to simply configure the client with the +mitmproxy box as the default gateway. + +{{< figure src="/schematics/proxy-modes-transparent-2.png" >}} + +In this scenario, we would: + +1. Configure the proxy machine for transparent mode. You can find instructions + in the [transparent]({{< relref "howto-transparent" + >}}) section. +2. Configure the client to use the proxy machine's IP as the default gateway. +3. Quick Check: At this point, you should already be able to visit an + unencrypted HTTP site over the proxy. +4. Open the magic domain **mitm.it** and install the certificate for your + device. + +Setting the custom gateway on clients can be automated by serving the +settings out to clients over DHCP. This lets set up an interception +network where all clients are proxied automatically, which can save time +and effort. + +{{% note %}} + +### Troubleshooting Transparent Mode + +Incorrect transparent mode configurations are a frequent source of +error. If it doesn't work for you, try the following things: + +- Open mitmproxy's event log - do you see clientconnect messages? If not, the + packets are not arriving at the proxy. One common cause is the occurrence of + ICMP redirects, which means that your machine is telling the client that + there's a faster way to the internet by contacting your router directly (see + the [transparent]({{< relref "howto-transparent" + >}}) section on how to disable them). If in doubt, + [Wireshark](https://wireshark.org/) may help you to see whether something + arrives at your machine or not. +- Make sure you have not explicitly configured an HTTP proxy on the client. This + is not needed in transparent mode. +- Re-check the instructions in the [transparent]({{< relref "howto-transparent" + >}}) section. Anything you missed? + +If you encounter any other pitfalls that should be listed here, please +let us know! +{{% /note %}} + +#### (b) Custom Routing + +In some cases, you may need more fine-grained control of which traffic +reaches the mitmproxy instance, and which doesn't. You may, for +instance, choose only to divert traffic to some hosts into the +transparent proxy. There are a huge number of ways to accomplish this, +and much will depend on the router or packet filter you're using. In +most cases, the configuration will look like this: + +{{< figure src="/schematics/proxy-modes-transparent-3.png" >}} + +## Reverse Proxy + +mitmproxy is usually used with a client that uses the proxy to access +the Internet. Using reverse proxy mode, you can use mitmproxy to act +like a normal HTTP server: + +{{< figure src="/schematics/proxy-modes-reverse.png" >}} + +There are various use-cases: + +- Say you have an internal API running at . You could now + set up mitmproxy in reverse proxy mode at and + dynamically point clients to this new API endpoint, which provides them with + the same data and you with debug information. Similarly, you could move your + real server to a different IP/port and set up mitmproxy in the original + place to debug and or redirect all sessions. +- Say you're a web developer working on (with a + development version running on ). You can modify + your hosts file so that example.com points to 127.0.0.1 and then run + mitmproxy in reverse proxy mode on port 80. You can test your app on the + example.com domain and get all requests recorded in mitmproxy. +- Say you have some toy project that should get SSL support. Simply set up + mitmproxy as a reverse proxy on port 443 and you're done (`mitmdump -p 443 + -R http://localhost:80/`). Mitmproxy auto-detects TLS traffic and intercepts + it dynamically. There are better tools for this specific task, but mitmproxy + is very quick and simple way to set up an SSL-speaking server. +- Want to add a non-SSL-capable compression proxy in front of your server? You + could even spawn a mitmproxy instance that terminates SSL (`-R http://...`), + point it to the compression proxy and let the compression proxy point to a + SSL-initiating mitmproxy (`-R https://...`), which then points to the real + server. As you see, it's a fairly flexible thing. + +### Host Header + +In reverse proxy mode, mitmproxy automatically rewrites the Host header to match +the upstream server. This allows mitmproxy to easily connect to existing +endpoints on the open web (e.g. `mitmproxy -R https://example.com`). You can +disable this behaviour with the `keep_host_header` option. + +However, keep in mind that absolute URLs within the returned document or HTTP +redirects will NOT be rewritten by mitmproxy. This means that if you click on a +link for "" in the returned web page, you will be taken +directly to that URL, bypassing mitmproxy. + +One possible way to address this is to modify the hosts file of your OS so that +"example.com" resolves to your proxy's IP, and then access the proxy by going +directly to example.com. Make sure that your proxy can still resolve the +original IP, or specify an IP in mitmproxy. + + +{{% note %}} + +### Caveat: Interactive Use + +Reverse Proxy mode is usually not sufficient to create a copy of an +interactive website at different URL. The HTML served to the client +remains unchanged - as soon as the user clicks on an non-relative URL +(or downloads a non-relative image resource), traffic no longer passes +through mitmproxy. +{{% /note %}} + + +## Upstream Proxy + +If you want to chain proxies by adding mitmproxy in front of a different +proxy appliance, you can use mitmproxy's upstream mode. In upstream +mode, all requests are unconditionally transferred to an upstream proxy +of your choice. + +{{< figure src="/schematics/proxy-modes-upstream.png" >}} + +mitmproxy supports both explicit HTTP and explicit HTTPS in upstream +proxy mode. You could in theory chain multiple mitmproxy instances in a +row, but that doesn't make any sense in practice (i.e. outside of our +tests). + + +## SOCKS Proxy + diff --git a/docs/src/content/concepts-options.md b/docs/src/content/concepts-options.md new file mode 100644 index 000000000..187768410 --- /dev/null +++ b/docs/src/content/concepts-options.md @@ -0,0 +1,66 @@ +--- +title: "Options" +menu: + concepts: + weight: 5 +--- + +# Options + +The mitmproxy tools share a common [YAML](http://yaml.org/) configuration file +located at `~/.mitmproxy/config.yaml`. This file controls **options** - typed +values that determine the behaviour of mitmproxy. The options mechanism is very +comprehensive - in fact, options control all of mitmproxy's runtime behaviour. +Most command-line flags are simply aliases for underlying options, and +interactive settings changes made in **mitmproxy** and **mitmweb** just change +values in our runtime options store. This means that almost any facet of +mitmproxy's behaviour can be controlled through options. + +The canonical reference for options is the `--options` flag, which is exposed by +each of the mitmproxy tools. Passing this flag will dump an annotated YAML +configuration to console, which includes all options and their default values. + +The options mechanism is extensible - third-party addons can define options that +are treated exactly like mitmproxy's own. This means that addons can also be +configured through the central configuration file, and their options will appear +in the options editors in interactive tools. + + +## Tools + +Both **mitmproxy** and **mitmweb** have built-in editors that let you view and +manipulate the complete configuration state of mitmproxy. Values you change +interactively have immediate effect in the running instance, and can be made +persistent by saving the settings out to a YAML configuration file (please see +the specific tool's interactive help for details on how to do this). + +For all tools, options can be set directly by name using the `--set` command-line +option. Please see the command-line help (`--help`) for usage. + + +## Example + +Here is an excerpt showing the first few lines of the complete `--options` +output: + +{{< highlight yaml >}} + +# Add all certificates of the upstream server to the certificate chain +# that will be served to the proxy client, as extras. Type bool. +add_upstream_certs_to_client_chain: false + +# Allow remote clients to connect to proxy. If set to false, client will +# not be able to connect to proxy unless it is on the same network or +# the proxyauth option is set Type bool. +allow_remote: false + +# Strip out request headers that might cause the server to return +# 304-not-modified. Type bool. +anticache: false + +# Try to convince servers to send us un-compressed data. Type bool. +anticomp: false + +{{< /highlight >}} + + diff --git a/docs/src/content/howto-ignoredomains.md b/docs/src/content/howto-ignoredomains.md new file mode 100644 index 000000000..b1b148310 --- /dev/null +++ b/docs/src/content/howto-ignoredomains.md @@ -0,0 +1,102 @@ +--- +title: "Ignoring Domains" +menu: + howto: + weight: 2 +--- + +# Ignoring Domains + +There are two main reasons why you may want to exempt some traffic from +mitmproxy's interception mechanism: + +- **Certificate pinning:** Some traffic is is protected using [Certificate + Pinning](https://security.stackexchange.com/questions/29988/what-is-certificate-pinning) + and mitmproxy's interception leads to errors. For example, the Twitter app, + Windows Update or the Apple App Store fail to work if mitmproxy is active. +- **Convenience:** You really don't care about some parts of the traffic and + just want them to go away. Note that mitmproxy's "Limit" option is often the + better alternative here, as it is not affected by the limitations listed + below. + +If you want to peek into (SSL-protected) non-HTTP connections, check out the +**tcp_proxy** feature. If you want to ignore traffic from mitmproxy's processing +because of large response bodies, take a look at the [streaming]({{< relref "overview-features#streaming" >}}) feature. + + +## ignore_hosts + +The `ignore_hosts` option allows you to specify a regex which is matched against +a `host:port` string (e.g. "example.com:443") of a connection. Matching hosts +are excluded from interception, and passed on unmodified. + +| | | +| ------------------ | ------------------------------------------------------------------ | +| command-line alias | `--ignore-hosts regex` | +| mitmproxy option | `ignore_hosts` | + + +## Limitations + +There are two important quirks to consider: + +- **In transparent mode, the ignore pattern is matched against the IP and + ClientHello SNI host.** While we usually infer the hostname from the Host + header if the `ignore_hosts` option is set, we do not have access to this + information before the SSL handshake. If the client uses SNI however, then we + treat the SNI host as an ignore target. +- **In regular and upstream proxy mode, explicit HTTP requests are never + ignored.**\[1\] The ignore pattern is applied on CONNECT requests, which + initiate HTTPS or clear-text WebSocket connections. + +## Tutorial + +If you just want to ignore one specific domain, there's usually a bulletproof +method to do so: + +1. Run mitmproxy or mitmdump in verbose mode (`-v`) and observe the `host:port` + information in the serverconnect messages. mitmproxy will filter on these. +2. Take the `host:port` string, surround it with ^ and $, escape all dots (. + becomes \\.) and use this as your ignore pattern: + + +{{< highlight none >}} +>>> mitmdump -v +127.0.0.1:50588: clientconnect +127.0.0.1:50588: request + -> CONNECT example.com:443 HTTP/1.1 +127.0.0.1:50588: Set new server address: example.com:443 +127.0.0.1:50588: serverconnect + -> example.com:443 +^C +>>> mitmproxy --ignore-hosts ^example\.com:443$ +{{< /highlight >}} + +Here are some other examples for ignore +patterns: + +{{< highlight none >}} +# Exempt traffic from the iOS App Store (the regex is lax, but usually just works): +--ignore-hosts apple.com:443 +# "Correct" version without false-positives: +--ignore-hosts '^(.+\.)?apple\.com:443$' + +# Ignore example.com, but not its subdomains: +--ignore-hosts '^example.com:' + +# Ignore everything but example.com and mitmproxy.org: +--ignore-hosts '^(?!example\.com)(?!mitmproxy\.org)' + +# Transparent mode: +--ignore-hosts 17\.178\.96\.59:443 +# IP address range: +--ignore-hosts 17\.178\.\d+\.\d+:443 +{{< / highlight >}} + +**Footnotes** + +1. This stems from an limitation of explicit HTTP proxying: A single connection + can be re-used for multiple target domains - a `GET http://example.com/` + request may be followed by a `GET http://evil.com/` request on the same + connection. If we start to ignore the connection after the first request, we + would miss the relevant second one. diff --git a/docs/src/content/howto-transparent-vms.md b/docs/src/content/howto-transparent-vms.md new file mode 100644 index 000000000..b186fd39f --- /dev/null +++ b/docs/src/content/howto-transparent-vms.md @@ -0,0 +1,114 @@ +--- +title: "Transparently Proxying VMs" +menu: + howto: + weight: 3 +--- + +# Transparently proxify virtual machines + +This walkthrough illustrates how to set up transparent proxying with +mitmproxy. We use VirtualBox VMs with an Ubuntu proxy machine in this +example, but the general *Internet \<--\> Proxy VM \<--\> (Virtual) +Internal Network* setup can be applied to other setups. + +## 1. Configure Proxy VM + +On the proxy machine, **eth0** is connected to the internet. **eth1** is +connected to the internal network that will be proxified and configured +to use a static ip (192.168.3.1). + +### VirtualBox configuration + + +{{< figure src="/transparent-vms/step1_vbox_eth0.png" >}} + +{{< figure src="/transparent-vms/step1_vbox_eth1.png" >}} + + +### VM Network Configuration + +{{< figure src="/transparent-vms/step1_proxy.png" >}} + +## 2. Configure DHCP and DNS + +We use dnsmasq to provide DHCP and DNS in our internal network. Dnsmasq is a +lightweight server designed to provide DNS (and optionally DHCP and TFTP) +services to a small-scale network. Before we get to that, we need to fix some +Ubuntu quirks: **Ubuntu \>12.04** runs an internal dnsmasq instance (listening +on loopback only) by default +[\[1\]](https://www.stgraber.org/2012/02/24/dns-in-ubuntu-12-04/). For our use +case, this needs to be disabled by changing `dns=dnsmasq` to `#dns=dnsmasq` in +**/etc/NetworkManager/NetworkManager.conf** and if on Ubuntu 16.04 or newer +running: + +{{< highlight bash >}} +sudo systemctl restart NetworkManager +{{< / highlight >}} + +If on Ubuntu 12.04 or 14.04 running: + +{{< highlight bash >}} +sudo restart network-manager +{{< / highlight >}} + +afterwards. + +Now, dnsmasq can be be installed and configured: + +{{< highlight bash >}} +sudo apt-get install dnsmasq +{{< / highlight >}} + +Replace **/etc/dnsmasq.conf** with the following configuration: + +{{< highlight none >}} +# Listen for DNS requests on the internal network +interface=eth1 +# Act as a DHCP server, assign IP addresses to clients +dhcp-range=192.168.3.10,192.168.3.100,96h +# Broadcast gateway and dns server information +dhcp-option=option:router,192.168.3.1 +dhcp-option=option:dns-server,192.168.3.1 +{{< / highlight >}} + +Apply changes: + +If on Ubuntu 16.04 or newer: + +{{< highlight bash >}} +sudo systemctl restart dnsmasq +{{< / highlight >}} + +If on Ubuntu 12.04 or 14.04: + +{{< highlight bash >}} +sudo service dnsmasq restart +{{< / highlight >}} + +Your **proxied machine** in the internal virtual network should now receive an +IP address via DHCP: + +{{< figure src="/transparent-vms/step2_proxied_vm.png" >}} + +## 3. Redirect traffic to mitmproxy + +To redirect traffic to mitmproxy, we need to add two iptables +rules: + +{{< highlight bash >}} +sudo iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 8080 +sudo iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 443 -j REDIRECT --to-port 8080 +{{< / highlight >}} + +## 4. Run mitmproxy + +Finally, we can run mitmproxy in transparent mode with + +{{< highlight bash >}} +mitmproxy -T +{{< / highlight >}} + +The proxied machine cannot to leak any data outside of HTTP or DNS requests. If +required, you can now [install the mitmproxy certificates on the proxied +machine]({{< relref "concepts-certificates" >}}). diff --git a/docs/src/content/howto-transparent.md b/docs/src/content/howto-transparent.md new file mode 100644 index 000000000..224cb5ee1 --- /dev/null +++ b/docs/src/content/howto-transparent.md @@ -0,0 +1,269 @@ +--- +title: "Transparent Proxying" +menu: + howto: + weight: 1 +--- + +# Transparent Proxying + +When a transparent proxy is used, traffic is redirected into a proxy at the +network layer, without any client configuration being required. This makes +transparent proxying ideal for those situations where you can't change client +behaviour - proxy-oblivious mobile applications being a common example. + +To set up transparent proxying, we need two new components. The first is a +redirection mechanism that transparently reroutes a TCP connection destined for +a server on the Internet to a listening proxy server. This usually takes the +form of a firewall on the same host as the proxy server - +[iptables](http://www.netfilter.org/) on Linux or +[pf](https://en.wikipedia.org/wiki/PF_(firewall)) on OSX. When the proxy +receives a redirected connection, it sees a vanilla HTTP request, without a host +specification. This is where the second new component comes in - a host module +that allows us to query the redirector for the original destination of the TCP +connection. + +At the moment, mitmproxy supports transparent proxying on OSX Lion and above, +and all current flavors of Linux. + + +## Linux fully transparent mode + +By default mitmproxy will use its own local IP address for its server-side +connections. In case this isn't desired, the --spoof-source-address argument can +be used to use the client's IP address for server-side connections. The +following config is required for this mode to work: + +{{< highlight bash >}} +CLIENT_NET=192.168.1.0/24 +TABLE_ID=100 +MARK=1 + +echo "$TABLE_ID mitmproxy" >> /etc/iproute2/rt_tables +iptables -t mangle -A PREROUTING -d $CLIENT_NET -j MARK --set-mark $MARK +iptables -t nat \ + -A PREROUTING -p tcp -s $CLIENT_NET \ + --match multiport --dports 80,443 -j \ + REDIRECT --to-port 8080 + +ip rule add fwmark $MARK lookup $TABLE_ID +ip route add local $CLIENT_NET dev lo table $TABLE_ID +{{< / highlight >}} + +This mode does require root privileges though. There's a wrapper in the examples +directory called 'mitmproxy_shim.c', which will enable you to use this mode with +dropped privileges. It can be used as follows: + +{{< highlight bash >}} +gcc examples/complex/full_transparency_shim.c -o mitmproxy_shim -lcap +sudo chown root:root mitmproxy_shim +sudo chmod u+s mitmproxy_shim +./mitmproxy_shim $(which mitmproxy) -T --spoof-source-address +{{< / highlight >}} + + + +## Linux + +On Linux, mitmproxy integrates with the iptables redirection mechanism to +achieve transparent mode. + +### 1. [Install the mitmproxy certificate on the test device]({{< relref "concepts-certificates" >}}) + +### 2. Enable IP forwarding: + +{{< highlight bash >}} +sysctl -w net.ipv4.ip_forward=1 +sysctl -w net.ipv6.conf.all.forwarding=1 +{{< / highlight >}} + +You may also want to consider enabling this permanently in `/etc/sysctl.conf` or +newly created `/etc/sysctl.d/mitmproxy.conf`, see +[here](https://superuser.com/a/625852). + +### 3. If your target machine is on the same physical network and you configured it to use a custom gateway, disable ICMP redirects: + +{{< highlight bash >}} +sysctl -w net.ipv4.conf.all.send_redirects=0 +{{< / highlight >}} + +You may also want to consider enabling this permanently in `/etc/sysctl.conf` or +a newly created `/etc/sysctl.d/mitmproxy.conf`, see +[here](https://superuser.com/a/625852). + +### 4. Create an iptables ruleset that redirects the desired traffic to the mitmproxy port + +Details will differ according to your setup, but the ruleset should look +something like this: + +{{< highlight bash >}} + iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080 + iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8080 + ip6tables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080 + ip6tables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8080 +{{< / highlight >}} + +   You may also want to consider enabling this permanently with the +`iptables-persistent` package, see +[here](http://www.microhowto.info/howto/make_the_configuration_of_iptables_persistent_on_debian.html). + +### 5. Fire up mitmproxy + +You probably want a command like this: + +{{< highlight bash >}} +mitmproxy -T --host +{{< / highlight >}} + +The `-T` flag turns on transparent mode, and the `--host` argument tells + mitmproxy to use the value of the Host header for URL display. + +### 6. Finally, configure your test device + +Set the test device up to use the host on which mitmproxy is running as the +default gateway. For a detailed walkthrough, have a look at the [tutorial for +transparently proxying VMs]({{< relref "howto-transparent-vms" >}}). + + +## OpenBSD + +### 1 [Install the mitmproxy certificate on the test device]({{< relref "concepts-certificates" >}}) + +### 2. Enable IP forwarding + +{{< highlight bash >}} +sudo sysctl -w net.inet.ip.forwarding=1 +{{< / highlight >}} + +### 3. Place the following two lines in **/etc/pf.conf** + +{{< highlight none >}} +mitm_if = "re2" +pass in quick proto tcp from $mitm_if to port { 80, 443 } divert-to 127.0.0.1 port 8080 +{{< / highlight >}} + +These rules tell pf to divert all traffic from `$mitm_if` destined for port 80 +or 443 to the local mitmproxy instance running on port 8080. You should replace +`$mitm_if` value with the interface on which your test device will appear. + +### 4. Enable the pf ruleset and enable it + +{{< highlight bash >}} +doas pfctl -f /etc/pf.conf +{{< / highlight >}} + +And now enable it: + +{{< highlight bash >}} +doas pfctl -e +{{< / highlight >}} + +### 5. Fire up mitmproxy + +You probably want a command like this: + +{{< highlight bash >}} +mitmproxy -T --host +{{< / highlight >}} + +The `-T` flag turns on transparent mode, and the `--host` argument tells +mitmproxy to use the value of the Host header for URL display. + +### 6. Finally, configure your test device + +Set the test device up to use the host on which mitmproxy is running as the +default gateway. + + +{{% note %}} +Note that the **divert-to** rules in the pf.conf given above only apply +to inbound traffic. **This means that they will NOT redirect traffic +coming from the box running pf itself.** We can't distinguish between an +outbound connection from a non-mitmproxy app, and an outbound connection +from mitmproxy itself - if you want to intercept your traffic, you +should use an external host to run mitmproxy. Nonetheless, pf is +flexible to cater for a range of creative possibilities, like +intercepting traffic emanating from VMs. See the **pf.conf** man page +for more. +{{% /note %}} + + +## macOS + +OSX Lion integrated the [pf](https://en.wikipedia.org/wiki/PF_(firewall)) +packet filter from the OpenBSD project, which mitmproxy uses to implement +transparent mode on OSX. Note that this means we don't support transparent mode +for earlier versions of OSX. + +### 1. [Install the mitmproxy certificate on the test device]({{< relref "concepts-certificates" >}}) + +### 2. Enable IP forwarding + +{{< highlight bash >}} +sudo sysctl -w net.inet.ip.forwarding=1 +{{< / highlight >}} + +### 3. Place the following two lines in a file called, say, **pf.conf** + + +{{< highlight none >}} +rdr on en0 inet proto tcp to any port {80, 443} -> 127.0.0.1 port 8080 +{{< / highlight >}} + +These rules tell pf to redirect all traffic destined for port 80 or 443 +to the local mitmproxy instance running on port 8080. You should replace +`en2` with the interface on which your test device will appear. + +### 4. Configure pf with the rules + +{{< highlight bash >}} +sudo pfctl -f pf.conf +{{< / highlight >}} + +### 5. And now enable it + +{{< highlight bash >}} +sudo pfctl -e +{{< / highlight >}} + +### 6. Configure sudoers to allow mitmproxy to access pfctl + +Edit the file **/etc/sudoers** on your system as root. Add the following line to +the end of the file: + +{{< highlight none >}} +ALL ALL=NOPASSWD: /sbin/pfctl -s state +{{< / highlight >}} + +Note that this allows any user on the system to run the command `/sbin/pfctl -s +state` as root without a password. This only allows inspection of the state +table, so should not be an undue security risk. If you're special feel free to +tighten the restriction up to the user running mitmproxy. + +### 7. Fire up mitmproxy + +You probably want a command like this: + +{{< highlight bash >}} +mitmproxy -T --host +{{< / highlight >}} + +The `-T` flag turns on transparent mode, and the `--host` argument tells +mitmproxy to use the value of the Host header for URL display. + +### 6. Finally, configure your test device + +Set the test device up to use the host on which mitmproxy is running as the +default gateway. + +{{% note %}} +Note that the **rdr** rules in the pf.conf given above only apply to +inbound traffic. **This means that they will NOT redirect traffic coming +from the box running pf itself.** We can't distinguish between an +outbound connection from a non-mitmproxy app, and an outbound connection +from mitmproxy itself - if you want to intercept your OSX traffic, you +should use an external host to run mitmproxy. Nonetheless, pf is +flexible to cater for a range of creative possibilities, like +intercepting traffic emanating from VMs. See the **pf.conf** man page +for more. +{{% /note %}} diff --git a/docs/src/content/overview-features.md b/docs/src/content/overview-features.md new file mode 100644 index 000000000..9a4c5468d --- /dev/null +++ b/docs/src/content/overview-features.md @@ -0,0 +1,219 @@ +--- +title: "Features" +menu: "overview" +menu: + overview: + weight: 4 +--- + +# Mitmproxy Core Features + + +- [Anticache](#anticache) +- [Client-side replay](#client-side-replay) +- [Proxy Authentication](#proxy-authentication) +- [Replacements](#replacements) +- [Server-side replay](#server-side-replay) +- [Set Headers](#set-headers) +- [Sticky Auth](#sticky-auth) +- [Sticky Cookies](#sticky-cookies) +- [Streaming](#streaming) +- [Upstream Certificates](#upstream-certificates) + + +## Anticache + +When the `anticache` option is set, it removes headers (`if-none-match` and +`if-modified-since`) that might elicit a `304 not modified` response from the +server. This is useful when you want to make sure you capture an HTTP exchange +in its totality. It's also often used during client-side replay, when you want +to make sure the server responds with complete data. + + +## Client-side replay + +Client-side replay does what it says on the tin: you provide a previously saved +HTTP conversation, and mitmproxy replays the client requests one by one. Note +that mitmproxy serialises the requests, waiting for a response from the server +before starting the next request. This might differ from the recorded +conversation, where requests may have been made concurrently. + +You may want to use client-side replay in conjunction with the `anticache` +option, to make sure the server responds with complete data. + +## Proxy Authentication + +Asks the user for authentication before they are permitted to use the proxy. +Authentication headers are stripped from the flows, so they are not passed to +upstream servers. For now, only HTTP Basic authentication is supported. The +proxy auth options are not compatible with the transparent, socks or reverse +proxy mode. + + +## Replacements + +The `replacements` option lets you specify an arbitrary number of patterns that +define text replacements within flows. A replacement pattern looks like this: + +{{< highlight none >}} +/patt/regex/replacement +{{< / highlight >}} + +Here, **patt** is a mitmproxy filter expression that defines which flows a +replacement applies to, **regex** is a valid Python regular expression that +defines what gets replaced, and **replacement** is a string literal that is +substituted in. The separator is arbitrary, and defined by the first character. +If the replacement string literal starts with `@`, it is treated as a file path +from which the replacement is read. + +Replace hooks fire when either a client request or a server response is +received. Only the matching flow component is affected: so, for example, +if a replace hook is triggered on server response, the replacement is +only run on the Response object leaving the Request intact. You control +whether the hook triggers on the request, response or both using the +filter pattern. If you need finer-grained control than this, it's simple +to create a script using the replacement API on Flow components. + +### Examples + +Replace `foo` with `bar` in requests: + +{{< highlight none >}} +:~q:foo:bar +{{< / highlight >}} + +Replace `foo` with with the data read from `~/xss-exploit`: + +{{< highlight bash >}} +mitmdump --replacements :~q:foo:@~/xss-exploit +{{< / highlight >}} + + +## Server-side replay + +The `server_replay` option lets us replay server responses from saved HTTP +conversations. To do this, we use a set of heuristics to match incoming requests +with saved responses. By default, we exclude request headers when matching +incoming requests with responses from the replay file, and use only the URL and +request method for matching. This works in most circumstances, and makes it +possible to replay server responses in situations where request headers would +naturally vary, e.g. using a different user agent. + +There is a slew of ways to customise the matching heuristic, including +specifying headers to include, request parameters to exclude, etc. These options +are collected under the `server_replay` prefix - please see the built-in +documentation for details. + +### Response refreshing + +Simply replaying server responses without modification will often result in +unexpected behaviour. For example cookie timeouts that were in the future at the +time a conversation was recorded might be in the past at the time it is +replayed. By default, mitmproxy refreshes server responses before sending them +to the client. The **date**, **expires** and **last-modified** headers are all +updated to have the same relative time offset as they had at the time of +recording. So, if they were in the past at the time of recording, they will be +in the past at the time of replay, and vice versa. Cookie expiry times are +updated in a similar way. + +You can turn off this behaviour by setting the `server_replay_refresh` option to +`false`. + +### Replaying a session recorded in Reverse-proxy Mode + +If you have captured the session in reverse proxy mode, in order to replay it +you still have to specify the server URL, otherwise you may get the error: 'HTTP +protocol error in client request: Invalid HTTP request form (expected authority +or absolute...)'. + +During replay, when the client's requests match previously recorded requests, +then the respective recorded responses are simply replayed by mitmproxy. +Otherwise, the unmatched requests is forwarded to the upstream server. If +forwarding is not desired, you can use the --kill (-k) switch to prevent that. + +## Set Headers + +The `setheaders` option lets you specify a set of headers to be added to +requests or responses, based on a filter pattern. A `setheaders` expression +looks like this: + +{{< highlight none >}} +/patt/name/value +{{< / highlight >}} + +Here, **patt** is a mitmproxy filter expression that defines which flows to set +headers on, and **name** and **value** are the header name and the value to set +respectively. + +## Sticky auth + +The `stickyauth` option is analogous to the sticky cookie option, in that HTTP +**Authorization** headers are simply replayed to the server once they have been +seen. This is enough to allow you to access a server resource using HTTP Basic +authentication through the proxy. Note that mitmproxy doesn't (yet) support replay of HTTP Digest +authentication. + +## Sticky cookies + +When the `stickycookie` option is set, **mitmproxy** will add the cookie most +recently set by the server to any cookie-less request. Consider a service that +sets a cookie to track the session after authentication. Using sticky cookies, +you can fire up mitmproxy, and authenticate to a service as you usually would +using a browser. After authentication, you can request authenticated resources +through mitmproxy as if they were unauthenticated, because mitmproxy will +automatically add the session tracking cookie to requests. Among other things, +this lets you script interactions with authenticated resources (using tools like +wget or curl) without having to worry about authentication. + +Sticky cookies are especially powerful when used in conjunction with [client +replay]({{< relref "#client-side-replay" >}}) - you can record the +authentication process once, and simply replay it on startup every time you need +to interact with the secured resources. + +## Streaming + +By default, mitmproxy will read an entire request/response, perform any +indicated manipulations on it, and then send the message on to the other party. +This can be problematic when downloading or uploading large files. When +streaming is enabled, message bodies are not buffered on the proxy but instead +sent directly to the server/client. HTTP headers are still fully buffered before +being sent. + +Request/response streaming is enabled by specifying a size cutoff in the +`stream_large_bodies` option. + +### Customizing Streaming + +You can also use a script to customise exactly which requests or responses are +streamed. Requests/Responses that should be tagged for streaming by setting +their ``.stream`` attribute to ``True``: + +{{< example src="examples/complex/stream.py" lang="py" >}} + + +### Websockets + +The `stream_websockets` option enables an analogous behaviour for websockets. +When WebSocket streaming is enabled, portions of the code which may perform +changes to the WebSocket message payloads will not have any effect on the actual +payload sent to the server as the frames are immediately forwarded to the +server. In contrast to HTTP streaming, where the body is not stored, the message +payload will still be stored in the WebSocket flow. + +## Upstream Certificates + +When mitmproxy receives a connection destined for an SSL-protected service, it +freezes the connection before reading its request data, and makes a connection +to the upstream server to "sniff" the contents of its SSL certificate. The +information gained - the **Common Name** and **Subject Alternative Names** - is +then used to generate the interception certificate, which is sent to the client +so the connection can continue. + +This rather intricate little dance lets us seamlessly generate correct +certificates even if the client has specified only an IP address rather than the +hostname. It also means that we don't need to sniff additional data to generate +certs in transparent mode. + +Upstream cert sniffing is on by default, and can optionally be turned off with +the `upstream_cert` option. \ No newline at end of file diff --git a/docs/src/content/overview-installation.md b/docs/src/content/overview-installation.md new file mode 100644 index 000000000..00941cb8c --- /dev/null +++ b/docs/src/content/overview-installation.md @@ -0,0 +1,117 @@ +--- +title: "Installation" +menu: "overview" +menu: + overview: + weight: 2 +--- + +# Installation + +Please follow the steps for your operating system. + +## macOS + +The recommended way to install mitmproxy on macOS is to use +[Homebrew](https://brew.sh/): + +{{< highlight bash >}} +brew install mitmproxy +{{< / highlight >}} + +Alternatively you can download the binary-packages from our +[releases](https://github.com/mitmproxy/mitmproxy/releases/latest) page. + +## Linux + +The recommended way to install mitmproxy on Linux is to download the +binary-packages from our +[releases](https://github.com/mitmproxy/mitmproxy/releases/latest) page. + +Some Linux distributions provide community-supported mitmproxy packages through +their native package repositories (e.g., Arch Linux, Debian, Ubuntu, Kali Linux, +OpenSUSE, etc.). We are not involved in the maintenance of downstream packaging +efforts, and they often lag behind the current mitmproxy release. Please contact +the repository maintainers directly for issues with native packages. + +## Windows + +The recommended way to install mitmproxy on Windows is to download our +binary-packages from our +[releases](https://github.com/mitmproxy/mitmproxy/releases/latest) page. + +FIXME: WSL + +After installation, you'll find shortcuts for mitmweb and mitmdump in the start +menu. Both executables are added to your PATH and can be invoked from the +command line. The console interface is not supported on native Windows. + + +# Advanced Installation + +## Self-contained Pre-built Binary Packages + +For some platforms we provide pre-built binary packages containing +ready-to-run executables. This includes a self-contained Python 3 +environment, a recent OpenSSL that support ALPN and HTTP/2, and other +dependencies that would otherwise we cumbersome to compile and install. + +Please be advised that we do not update these binaries after the initial +release. This means we do not include security-related updates of our +dependencies in already released mitmproxy versions. If there is a +severe issue, we might consider releasing a bugfix release of mitmproxy +and corresponding binary packages. + +We only support the latest version of mitmproxy with bugfix and security +updates through regular minor releases. + +## Docker Images + +You can use the official mitmproxy images from +[DockerHub](https://hub.docker.com/r/mitmproxy/mitmproxy/). The same +security considerations apply as for our binary packages. + +## Installation on Linux via pip3 + +Please make sure to install Python 3.5 (or higher) and pip3 for your +distribution. If your distribution does not provide a suitable Python +version, you can use [pyenv](https://github.com/yyuu/pyenv) to get a +recent Python environment. + +{{< highlight bash >}} +sudo apt install python3-pip # Debian 8 or higher, Ubuntu 16.04 or higher +sudo dnf install python3-pip # Fedora 24 or higher +sudo pacman -S python-pip # Arch Linux +{{< / highlight >}} + +Please make sure to upgrade pip3 itself: + +{{< highlight bash >}} +sudo pip3 install -U pip +{{< / highlight >}} + +Now you can install mitmproxy via pip3: + +{{< highlight bash >}} +sudo pip3 install mitmproxy +{{< / highlight >}} + +## Installation on Windows via pip3 + +First, install the latest version of Python 3.5 or higher from the +[Python website](https://www.python.org/downloads/windows/). During +installation, make sure to select Add Python to PATH. There are no other +dependencies on Windows. + +Now you can install mitmproxy via pip3: + +{{< highlight bash >}} +pip3 install mitmproxy +{{< / highlight >}} + +## Installation from Source + +If you would like to install mitmproxy directly from source code or the +GitHub master branch, please see the our +[README](https://github.com/mitmproxy/mitmproxy/blob/master/README.rst) +on GitHub. diff --git a/docs/src/content/overview-tools.md b/docs/src/content/overview-tools.md new file mode 100644 index 000000000..7612383aa --- /dev/null +++ b/docs/src/content/overview-tools.md @@ -0,0 +1,105 @@ +--- +title: "Tools" +menu: "overview" +menu: + overview: + weight: 3 +--- + +# Overview + +You should thin of the mitmproxy project's tools as a set of front-ends that +expose the same underlying functionality. We aim to have feature parity across +all of our tooling, and all tools share a common configuration mechanism and +most command-line options. + +## mitmproxy + +{{< figure src="/screenshots/mitmproxy.png" >}} + +**mitmproxy** is a console tool that allows interactive examination and +modification of HTTP traffic. It differs from mitmdump in that all flows are +kept in memory, which means that it's intended for taking and manipulating +small-ish samples. Use the `?` shortcut key to view, context-sensitive +documentation from any **mitmproxy** screen. + + +## mitmweb + +{{< figure src="/screenshots/mitmweb.png" >}} + +**mitmweb** is mitmproxy's web-based user interface that allows +interactive examination and modification of HTTP traffic. Like +mitmproxy, it differs from mitmdump in that all flows are kept in +memory, which means that it's intended for taking and manipulating +small-ish samples. + +{{% note %}} +Mitmweb is currently in beta. We consider it stable for all features +currently exposed in the UI, but it still misses a lot of mitmproxy's +features. +{{% /note %}} + + +## mitmdump + +**mitmdump** is the command-line companion to mitmproxy. It provides +tcpdump-like functionality to let you view, record, and programmatically +transform HTTP traffic. See the `--help` flag output for complete +documentation. + + +### Example: Saving traffic + +{{< highlight bash >}} +mitmdump -w outfile +{{< / highlight >}} + +Start up mitmdump in proxy mode, and write all traffic to **outfile**. + +### Filtering saved traffic + +{{< highlight bash >}} +mitmdump -nr infile -w outfile "~m post" +{{< / highlight >}} + +Start mitmdump without binding to the proxy port (`-n`), read all flows +from infile, apply the specified filter expression (only match POSTs), +and write to outfile. + +### Client replay + +{{< highlight bash >}} +mitmdump -nc outfile +{{< / highlight >}} + +Start mitmdump without binding to the proxy port (`-n`), then replay all +requests from outfile (`-c filename`). Flags combine in the obvious way, +so you can replay requests from one file, and write the resulting flows +to another: + +{{< highlight bash >}} +mitmdump -nc srcfile -w dstfile +{{< / highlight >}} + +See the [client-side replay]({{< relref "overview-features#client-side-replay" +>}}) section for more information. + +### Running a script + +{{< highlight bash >}} +mitmdump -s examples/add_header.py +{{< / highlight >}} + +This runs the **add_header.py** example script, which simply adds a new +header to all responses. + +### Scripted data transformation + +{{< highlight bash >}} +mitmdump -ns examples/add_header.py -r srcfile -w dstfile +{{< / highlight >}} + +This command loads flows from **srcfile**, transforms it according to +the specified script, then writes it back to **dstfile**. + diff --git a/docs/src/content/tute-clientreplay.md b/docs/src/content/tute-clientreplay.md new file mode 100644 index 000000000..1bf690316 --- /dev/null +++ b/docs/src/content/tute-clientreplay.md @@ -0,0 +1,69 @@ +--- +title: "Client replay" +menu: + tutes: + weight: 1 +--- + +# Client playback: a 30 second example + +My local cafe is serviced by a rickety and unreliable wireless network, +generously sponsored with ratepayers' money by our city council. After +connecting, you are redirected to an SSL-protected page that prompts you for a +username and password. Once you've entered your details, you are free to enjoy +the intermittent dropouts, treacle-like speeds and incorrectly configured +transparent proxy. + +I tend to automate this kind of thing at the first opportunity, on the theory +that time spent now will be more than made up in the long run. In this case, I +might use [Firebug](https://getfirebug.com/) to ferret out the form post +parameters and target URL, then fire up an editor to write a little script using +Python's [urllib](https://docs.python.org/library/urllib.html) to simulate a +submission. That's a lot of futzing about. With mitmproxy we can do the job in +literally 30 seconds, without having to worry about any of the details. Here's +how. + +## 1. Run mitmdump to record our HTTP conversation to a file. + +{{< highlight bash >}} +mitmdump -w wireless-login +{{< / highlight >}} + +## 2. Point your browser at the mitmdump instance. + +I use a tiny Firefox addon called [Toggle +Proxy](https://addons.mozilla.org/en-us/firefox/addon/toggle-proxy-51740/) to +switch quickly to and from mitmproxy. I'm assuming you've already [configured +your browser with mitmproxy's SSL certificate authority]({{< relref +"concepts-certificates" >}}). + +## 3. Log in as usual + +And that's it\! You now have a serialised version of the login process +in the file wireless-login, and you can replay it at any time like this: + +{{< highlight bash >}} +mitmdump -c wireless-login +{{< / highlight >}} + +## Embellishments + +We're really done at this point, but there are a couple of embellishments we +could make if we wanted. I use [wicd](https://launchpad.net/wicd) to +automatically join wireless networks I frequent, and it lets me specify a +command to run after connecting. I used the client replay command above and +voila\! - totally hands-free wireless network startup. + +We might also want to prune requests that download CSS, JS, images and so forth. +These add only a few moments to the time it takes to replay, but they're not +really needed and I somehow feel compelled to trim them anyway. So, we fire up +the mitmproxy console tool on our serialised conversation, like so: + +{{< highlight bash >}} +mitmproxy -r wireless-login +{{< / highlight >}} + +We can now go through and manually delete (using the d keyboard shortcut) everything we want to trim. When +we're done, we use w to save the conversation back +to the file. diff --git a/docs/src/content/tute-highscores.md b/docs/src/content/tute-highscores.md new file mode 100644 index 000000000..f5cbd7bcf --- /dev/null +++ b/docs/src/content/tute-highscores.md @@ -0,0 +1,123 @@ +--- +title: "Setting highscores on Apple GameCenter" +menu: + tutes: + weight: 2 +--- + +# Setting highscores on Apple's GameCenter + +## The setup + +In this tutorial, I'm going to show you how simple it is to creatively interfere +with Apple Game Center traffic using mitmproxy. To set things up, [install the +mitmproxy root certificate]({{< relref concepts-certificates >}}). Then start +mitmproxy on your desktop, and configure the iPhone to use it as a proxy. + +## Taking a look at the Game Center traffic + +Lets take a first look at the Game Center traffic. The game I'll use in this +tutorial is [Super Mega +Worm](https://itunes.apple.com/us/app/super-mega-worm/id388541990?mt=8) - a +great little retro-apocalyptic sidescroller for the iPhone: + +{{< figure src="/tute-highscores/supermega.png" >}} + +After finishing a game (take your time), watch the traffic flowing through +mitmproxy: + +{{< figure src="/tute-highscores/one.png" >}} + +We see a bunch of things we might expect - initialisation, the retrieval +of leaderboards and so forth. Then, right at the end, there's a POST to +this tantalising +URL: + +{{< highlight none >}} +https://service.gc.apple.com/WebObjects/GKGameStatsService.woa/wa/submitScore +{{< / highlight >}} + +The contents of the submission are particularly interesting: + +{{< highlight xml >}} + + + scores + + + category + SMW_Adv_USA1 + context + 0 + score-value + 55 + timestamp + 1363515361321 + + + + +{{< / highlight >}} + +This is a [property list](https://en.wikipedia.org/wiki/Property_list), +containing an identifier for the game, a score (55, in this case), and a +timestamp. Looks pretty simple to mess with. + +## Modifying and replaying the score submission + +Lets edit the score submission. First, select it in mitmproxy, then +press enter to view it. Make sure you're +viewing the request, not the response -you can use +tab to flick between the two. Now press +e for edit. You'll be prompted for the part +of the request you want to change - press r +for raw body. Your preferred editor (taken from the EDITOR environment +variable) will now fire up. Lets bump the score up to something a bit +more ambitious: + +{{< highlight xml >}} + + + scores + + + category + SMW_Adv_USA1 + context + 0 + score-value + 2200272667 + timestamp + 1363515361321 + + + + +{{< / highlight >}} + +Save the file and exit your editor. + +The final step is to replay this modified request. Simply press +r for replay. + +## The glorious result and some intrigue + +{{< figure src="/tute-highscores/leaderboard.png" >}} + +And that's it - according to the records, I am the greatest Super Mega Worm +player of all time. + +There's a curious addendum to this tale. When I first wrote this tutorial, all +the top competitors' scores were the same: 2,147,483,647 (this is no longer the +case, because there are now so many fellow cheaters using this tutorial). If you +think that number seems familiar, you're right: it's 2^31-1, the maximum value +you can fit into a signed 32-bit int. Now let me tell you another peculiar thing +about Super Mega Worm - at the end of every game, it submits your highest +previous score to the Game Center, not your current score. This means that it +stores your highscore somewhere, and I'm guessing that it reads that stored +score back into a signed integer. So, if you **were** to cheat by the relatively +pedestrian means of modifying the saved score on your jailbroken phone, then +2^31-1 might well be the maximum score you could get. Then again, if the game +itself stores its score in a signed 32-bit int, you could get the same score +through perfect play, effectively beating the game. So, which is it in this +case? I'll leave that for you to decide. diff --git a/docs/src/examples b/docs/src/examples new file mode 120000 index 000000000..d15735c1d --- /dev/null +++ b/docs/src/examples @@ -0,0 +1 @@ +../../examples \ No newline at end of file diff --git a/docs/src/layouts/_default/single.html b/docs/src/layouts/_default/single.html new file mode 100644 index 000000000..4a8baf53f --- /dev/null +++ b/docs/src/layouts/_default/single.html @@ -0,0 +1,10 @@ +{{ partial "header.html" . }} +
+
+ {{ partial "sidebar.html" . }} +
+
+ {{.Content}} +
+
+{{ partial "footer.html" . }} diff --git a/docs/src/layouts/index.html b/docs/src/layouts/index.html new file mode 100644 index 000000000..4a8baf53f --- /dev/null +++ b/docs/src/layouts/index.html @@ -0,0 +1,10 @@ +{{ partial "header.html" . }} +
+
+ {{ partial "sidebar.html" . }} +
+
+ {{.Content}} +
+
+{{ partial "footer.html" . }} diff --git a/docs/src/layouts/partials/sidebar.html b/docs/src/layouts/partials/sidebar.html new file mode 100644 index 000000000..ef853fc6b --- /dev/null +++ b/docs/src/layouts/partials/sidebar.html @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/docs/src/layouts/partials/sidemenu.html b/docs/src/layouts/partials/sidemenu.html new file mode 100644 index 000000000..035cc59e8 --- /dev/null +++ b/docs/src/layouts/partials/sidemenu.html @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/docs/src/layouts/shortcodes/example.html b/docs/src/layouts/shortcodes/example.html new file mode 100644 index 000000000..d23cabb66 --- /dev/null +++ b/docs/src/layouts/shortcodes/example.html @@ -0,0 +1,5 @@ + +
+{{ highlight (trim (readFile (.Get "src")) "\n\r") (.Get "lang") "" }} +
{{ (.Get "src" )}}
+
\ No newline at end of file diff --git a/docs/src/layouts/shortcodes/note.html b/docs/src/layouts/shortcodes/note.html new file mode 100644 index 000000000..f3db72c4b --- /dev/null +++ b/docs/src/layouts/shortcodes/note.html @@ -0,0 +1 @@ +
{{.Inner}}
diff --git a/docs/certinstall-webapp.png b/docs/src/static/certinstall-webapp.png similarity index 100% rename from docs/certinstall-webapp.png rename to docs/src/static/certinstall-webapp.png diff --git a/docs/src/static/logo-docs.png b/docs/src/static/logo-docs.png new file mode 100644 index 000000000..b37dbd852 Binary files /dev/null and b/docs/src/static/logo-docs.png differ diff --git a/docs/schematics/how-mitmproxy-works-explicit-https.png b/docs/src/static/schematics/how-mitmproxy-works-explicit-https.png similarity index 100% rename from docs/schematics/how-mitmproxy-works-explicit-https.png rename to docs/src/static/schematics/how-mitmproxy-works-explicit-https.png diff --git a/docs/schematics/how-mitmproxy-works-explicit.png b/docs/src/static/schematics/how-mitmproxy-works-explicit.png similarity index 100% rename from docs/schematics/how-mitmproxy-works-explicit.png rename to docs/src/static/schematics/how-mitmproxy-works-explicit.png diff --git a/docs/schematics/how-mitmproxy-works-transparent-https.png b/docs/src/static/schematics/how-mitmproxy-works-transparent-https.png similarity index 100% rename from docs/schematics/how-mitmproxy-works-transparent-https.png rename to docs/src/static/schematics/how-mitmproxy-works-transparent-https.png diff --git a/docs/schematics/how-mitmproxy-works-transparent.png b/docs/src/static/schematics/how-mitmproxy-works-transparent.png similarity index 100% rename from docs/schematics/how-mitmproxy-works-transparent.png rename to docs/src/static/schematics/how-mitmproxy-works-transparent.png diff --git a/docs/schematics/proxy-modes-flowchart.png b/docs/src/static/schematics/proxy-modes-flowchart.png similarity index 100% rename from docs/schematics/proxy-modes-flowchart.png rename to docs/src/static/schematics/proxy-modes-flowchart.png diff --git a/docs/schematics/proxy-modes-regular.png b/docs/src/static/schematics/proxy-modes-regular.png similarity index 100% rename from docs/schematics/proxy-modes-regular.png rename to docs/src/static/schematics/proxy-modes-regular.png diff --git a/docs/schematics/proxy-modes-reverse.png b/docs/src/static/schematics/proxy-modes-reverse.png similarity index 100% rename from docs/schematics/proxy-modes-reverse.png rename to docs/src/static/schematics/proxy-modes-reverse.png diff --git a/docs/schematics/proxy-modes-transparent-1.png b/docs/src/static/schematics/proxy-modes-transparent-1.png similarity index 100% rename from docs/schematics/proxy-modes-transparent-1.png rename to docs/src/static/schematics/proxy-modes-transparent-1.png diff --git a/docs/schematics/proxy-modes-transparent-2.png b/docs/src/static/schematics/proxy-modes-transparent-2.png similarity index 100% rename from docs/schematics/proxy-modes-transparent-2.png rename to docs/src/static/schematics/proxy-modes-transparent-2.png diff --git a/docs/schematics/proxy-modes-transparent-3.png b/docs/src/static/schematics/proxy-modes-transparent-3.png similarity index 100% rename from docs/schematics/proxy-modes-transparent-3.png rename to docs/src/static/schematics/proxy-modes-transparent-3.png diff --git a/docs/schematics/proxy-modes-transparent-wrong.png b/docs/src/static/schematics/proxy-modes-transparent-wrong.png similarity index 100% rename from docs/schematics/proxy-modes-transparent-wrong.png rename to docs/src/static/schematics/proxy-modes-transparent-wrong.png diff --git a/docs/schematics/proxy-modes-upstream.png b/docs/src/static/schematics/proxy-modes-upstream.png similarity index 100% rename from docs/schematics/proxy-modes-upstream.png rename to docs/src/static/schematics/proxy-modes-upstream.png diff --git a/docs/schematics/proxy-modes.pdf b/docs/src/static/schematics/proxy-modes.pdf similarity index 100% rename from docs/schematics/proxy-modes.pdf rename to docs/src/static/schematics/proxy-modes.pdf diff --git a/docs/src/static/screenshots/mitmproxy.png b/docs/src/static/screenshots/mitmproxy.png new file mode 100644 index 000000000..3fd14b9d4 Binary files /dev/null and b/docs/src/static/screenshots/mitmproxy.png differ diff --git a/docs/screenshots/mitmweb.png b/docs/src/static/screenshots/mitmweb.png similarity index 100% rename from docs/screenshots/mitmweb.png rename to docs/src/static/screenshots/mitmweb.png diff --git a/docs/tutorials/transparent-dhcp/step1_proxy.png b/docs/src/static/transparent-vms/step1_proxy.png similarity index 100% rename from docs/tutorials/transparent-dhcp/step1_proxy.png rename to docs/src/static/transparent-vms/step1_proxy.png diff --git a/docs/tutorials/transparent-dhcp/step1_vbox_eth0.png b/docs/src/static/transparent-vms/step1_vbox_eth0.png similarity index 100% rename from docs/tutorials/transparent-dhcp/step1_vbox_eth0.png rename to docs/src/static/transparent-vms/step1_vbox_eth0.png diff --git a/docs/tutorials/transparent-dhcp/step1_vbox_eth1.png b/docs/src/static/transparent-vms/step1_vbox_eth1.png similarity index 100% rename from docs/tutorials/transparent-dhcp/step1_vbox_eth1.png rename to docs/src/static/transparent-vms/step1_vbox_eth1.png diff --git a/docs/tutorials/transparent-dhcp/step2_proxied_vm.png b/docs/src/static/transparent-vms/step2_proxied_vm.png similarity index 100% rename from docs/tutorials/transparent-dhcp/step2_proxied_vm.png rename to docs/src/static/transparent-vms/step2_proxied_vm.png diff --git a/docs/tutorials/leaderboard.png b/docs/src/static/tute-highscores/leaderboard.png similarity index 100% rename from docs/tutorials/leaderboard.png rename to docs/src/static/tute-highscores/leaderboard.png diff --git a/docs/tutorials/one.png b/docs/src/static/tute-highscores/one.png similarity index 100% rename from docs/tutorials/one.png rename to docs/src/static/tute-highscores/one.png diff --git a/docs/tutorials/supermega.png b/docs/src/static/tute-highscores/supermega.png similarity index 100% rename from docs/tutorials/supermega.png rename to docs/src/static/tute-highscores/supermega.png diff --git a/docs/src/themes/mitmproxydocs/archetypes/default.md b/docs/src/themes/mitmproxydocs/archetypes/default.md new file mode 100644 index 000000000..03855e356 --- /dev/null +++ b/docs/src/themes/mitmproxydocs/archetypes/default.md @@ -0,0 +1,4 @@ ++++ +title = "" +date = "" ++++ \ No newline at end of file diff --git a/docs/src/themes/mitmproxydocs/layouts/_default/list.html b/docs/src/themes/mitmproxydocs/layouts/_default/list.html new file mode 100644 index 000000000..b88c94ad7 --- /dev/null +++ b/docs/src/themes/mitmproxydocs/layouts/_default/list.html @@ -0,0 +1,25 @@ +{{ partial "header.html" . }} +
+
+
+
+
+
+ {{ range .Data.Pages.GroupByDate "2006" }} +

{{ .Key }}

+
    + {{ range .Pages }} +
  • + - + {{ .Title }} +
  • + {{ end }} +
+ {{ end }} +
+
+
+
+
+
+{{ partial "footer.html" . }} diff --git a/docs/src/themes/mitmproxydocs/layouts/_default/single.html b/docs/src/themes/mitmproxydocs/layouts/_default/single.html new file mode 100644 index 000000000..830164500 --- /dev/null +++ b/docs/src/themes/mitmproxydocs/layouts/_default/single.html @@ -0,0 +1,7 @@ +{{ partial "header.html" . }} +
+
+ {{ .Content }} +
+
+{{ partial "footer.html" . }} diff --git a/docs/src/themes/mitmproxydocs/layouts/partials/footer.html b/docs/src/themes/mitmproxydocs/layouts/partials/footer.html new file mode 100644 index 000000000..308b1d01b --- /dev/null +++ b/docs/src/themes/mitmproxydocs/layouts/partials/footer.html @@ -0,0 +1,2 @@ + + diff --git a/docs/src/themes/mitmproxydocs/layouts/partials/header.html b/docs/src/themes/mitmproxydocs/layouts/partials/header.html new file mode 100644 index 000000000..559115f0a --- /dev/null +++ b/docs/src/themes/mitmproxydocs/layouts/partials/header.html @@ -0,0 +1,22 @@ + + + + + + + + + {{ .Title }} + {{ with .Site.Params.description }} + + {{ end }} + {{ with .Site.Params.author }} + + {{ end }} + + {{ if .RSSLink -}} + + {{- end }} + {{ .Hugo.Generator }} + + diff --git a/docs/src/themes/mitmproxydocs/static/css/style.css b/docs/src/themes/mitmproxydocs/static/css/style.css new file mode 100644 index 000000000..14823447a --- /dev/null +++ b/docs/src/themes/mitmproxydocs/static/css/style.css @@ -0,0 +1,6756 @@ +/* Background */ +.chroma { + color: #f8f8f2; + background-color: #272822; } + +/* Error */ +.chroma .err { + color: #960050; + background-color: #1e0010; } + +/* LineTableTD */ +.chroma .lntd { + vertical-align: top; + padding: 0; + margin: 0; + border: 0; } + +/* LineTable */ +.chroma .lntable { + border-spacing: 0; + padding: 0; + margin: 0; + border: 0; + width: 100%; + overflow: auto; + display: block; } + +/* LineHighlight */ +.chroma .hl { + display: block; + width: 100%; + background-color: #ffffcc; } + +/* LineNumbersTable */ +.chroma .lnt { + margin-right: 0.4em; + padding: 0 0.4em 0 0.4em; + display: block; } + +/* LineNumbers */ +.chroma .ln { + margin-right: 0.4em; + padding: 0 0.4em 0 0.4em; } + +/* Keyword */ +.chroma .k { + color: #66d9ef; } + +/* KeywordConstant */ +.chroma .kc { + color: #66d9ef; } + +/* KeywordDeclaration */ +.chroma .kd { + color: #66d9ef; } + +/* KeywordNamespace */ +.chroma .kn { + color: #f92672; } + +/* KeywordPseudo */ +.chroma .kp { + color: #66d9ef; } + +/* KeywordReserved */ +.chroma .kr { + color: #66d9ef; } + +/* KeywordType */ +.chroma .kt { + color: #66d9ef; } + +/* NameAttribute */ +.chroma .na { + color: #a6e22e; } + +/* NameClass */ +.chroma .nc { + color: #a6e22e; } + +/* NameConstant */ +.chroma .no { + color: #66d9ef; } + +/* NameDecorator */ +.chroma .nd { + color: #a6e22e; } + +/* NameException */ +.chroma .ne { + color: #a6e22e; } + +/* NameFunction */ +.chroma .nf { + color: #a6e22e; } + +/* NameOther */ +.chroma .nx { + color: #a6e22e; } + +/* NameTag */ +.chroma .nt { + color: #f92672; } + +/* Literal */ +.chroma .l { + color: #ae81ff; } + +/* LiteralDate */ +.chroma .ld { + color: #e6db74; } + +/* LiteralString */ +.chroma .s { + color: #e6db74; } + +/* LiteralStringAffix */ +.chroma .sa { + color: #e6db74; } + +/* LiteralStringBacktick */ +.chroma .sb { + color: #e6db74; } + +/* LiteralStringChar */ +.chroma .sc { + color: #e6db74; } + +/* LiteralStringDelimiter */ +.chroma .dl { + color: #e6db74; } + +/* LiteralStringDoc */ +.chroma .sd { + color: #e6db74; } + +/* LiteralStringDouble */ +.chroma .s2 { + color: #e6db74; } + +/* LiteralStringEscape */ +.chroma .se { + color: #ae81ff; } + +/* LiteralStringHeredoc */ +.chroma .sh { + color: #e6db74; } + +/* LiteralStringInterpol */ +.chroma .si { + color: #e6db74; } + +/* LiteralStringOther */ +.chroma .sx { + color: #e6db74; } + +/* LiteralStringRegex */ +.chroma .sr { + color: #e6db74; } + +/* LiteralStringSingle */ +.chroma .s1 { + color: #e6db74; } + +/* LiteralStringSymbol */ +.chroma .ss { + color: #e6db74; } + +/* LiteralNumber */ +.chroma .m { + color: #ae81ff; } + +/* LiteralNumberBin */ +.chroma .mb { + color: #ae81ff; } + +/* LiteralNumberFloat */ +.chroma .mf { + color: #ae81ff; } + +/* LiteralNumberHex */ +.chroma .mh { + color: #ae81ff; } + +/* LiteralNumberInteger */ +.chroma .mi { + color: #ae81ff; } + +/* LiteralNumberIntegerLong */ +.chroma .il { + color: #ae81ff; } + +/* LiteralNumberOct */ +.chroma .mo { + color: #ae81ff; } + +/* Operator */ +.chroma .o { + color: #f92672; } + +/* OperatorWord */ +.chroma .ow { + color: #f92672; } + +/* Comment */ +.chroma .c { + color: #75715e; } + +/* CommentHashbang */ +.chroma .ch { + color: #75715e; } + +/* CommentMultiline */ +.chroma .cm { + color: #75715e; } + +/* CommentSingle */ +.chroma .c1 { + color: #75715e; } + +/* CommentSpecial */ +.chroma .cs { + color: #75715e; } + +/* CommentPreproc */ +.chroma .cp { + color: #75715e; } + +/* CommentPreprocFile */ +.chroma .cpf { + color: #75715e; } + +/* GenericDeleted */ +.chroma .gd { + color: #f92672; } + +/* GenericEmph */ +.chroma .ge { + font-style: italic; } + +/* GenericInserted */ +.chroma .gi { + color: #a6e22e; } + +/* GenericStrong */ +.chroma .gs { + font-weight: bold; } + +/* GenericSubheading */ +.chroma .gu { + color: #75715e; } + +@keyframes spinAround { + from { + transform: rotate(0deg); } + to { + transform: rotate(359deg); } } + +/*! minireset.css v0.0.2 | MIT License | github.com/jgthms/minireset.css */ +html, +body, +p, +ol, +ul, +li, +dl, +dt, +dd, +blockquote, +figure, +fieldset, +legend, +textarea, +pre, +iframe, +hr, +h1, +h2, +h3, +h4, +h5, +h6 { + margin: 0; + padding: 0; } + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: 100%; + font-weight: normal; } + +ul { + list-style: none; } + +button, +input, +select, +textarea { + margin: 0; } + +html { + box-sizing: border-box; } + +* { + box-sizing: inherit; } + *:before, *:after { + box-sizing: inherit; } + +img, +embed, +object, +audio, +video { + max-width: 100%; } + +iframe { + border: 0; } + +table { + border-collapse: collapse; + border-spacing: 0; } + +td, +th { + padding: 0; + text-align: left; } + +html { + background-color: white; + font-size: 16px; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + min-width: 300px; + overflow-x: hidden; + overflow-y: scroll; + text-rendering: optimizeLegibility; + text-size-adjust: 100%; } + +article, +aside, +figure, +footer, +header, +hgroup, +section { + display: block; } + +body, +button, +input, +select, +textarea { + font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif, "Font Awesome 5 Free", "Font Awesome 5 Brands"; } + +code, +pre { + -moz-osx-font-smoothing: auto; + -webkit-font-smoothing: auto; + font-family: monospace; } + +body { + color: #4a4a4a; + font-size: 1rem; + font-weight: 400; + line-height: 1.5; } + +a { + color: #3273dc; + cursor: pointer; + text-decoration: none; } + a strong { + color: currentColor; } + a:hover { + color: #363636; } + +code { + background-color: whitesmoke; + color: #ff3860; + font-size: 0.875em; + font-weight: normal; + padding: 0.25em 0.5em 0.25em; } + +hr { + background-color: #dbdbdb; + border: none; + display: block; + height: 1px; + margin: 1.5rem 0; } + +img { + height: auto; + max-width: 100%; } + +input[type="checkbox"], +input[type="radio"] { + vertical-align: baseline; } + +small { + font-size: 0.875em; } + +span { + font-style: inherit; + font-weight: inherit; } + +strong { + color: #363636; + font-weight: 700; } + +pre { + -webkit-overflow-scrolling: touch; + background-color: whitesmoke; + color: #4a4a4a; + font-size: 0.875em; + overflow-x: auto; + padding: 1.25rem 1.5rem; + white-space: pre; + word-wrap: normal; } + pre code { + background-color: transparent; + color: currentColor; + font-size: 1em; + padding: 0; } + +table td, +table th { + text-align: left; + vertical-align: top; } + +table th { + color: #363636; } + +.is-clearfix:after { + clear: both; + content: " "; + display: table; } + +.is-pulled-left { + float: left !important; } + +.is-pulled-right { + float: right !important; } + +.is-clipped { + overflow: hidden !important; } + +.is-overlay { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; } + +.is-size-1 { + font-size: 3rem !important; } + +.is-size-2 { + font-size: 2.5rem !important; } + +.is-size-3 { + font-size: 2rem !important; } + +.is-size-4 { + font-size: 1.5rem !important; } + +.is-size-5 { + font-size: 1.25rem !important; } + +.is-size-6 { + font-size: 1rem !important; } + +.is-size-7 { + font-size: 0.75rem !important; } + +@media screen and (max-width: 768px) { + .is-size-1-mobile { + font-size: 3rem !important; } + .is-size-2-mobile { + font-size: 2.5rem !important; } + .is-size-3-mobile { + font-size: 2rem !important; } + .is-size-4-mobile { + font-size: 1.5rem !important; } + .is-size-5-mobile { + font-size: 1.25rem !important; } + .is-size-6-mobile { + font-size: 1rem !important; } + .is-size-7-mobile { + font-size: 0.75rem !important; } } + +@media screen and (min-width: 769px), print { + .is-size-1-tablet { + font-size: 3rem !important; } + .is-size-2-tablet { + font-size: 2.5rem !important; } + .is-size-3-tablet { + font-size: 2rem !important; } + .is-size-4-tablet { + font-size: 1.5rem !important; } + .is-size-5-tablet { + font-size: 1.25rem !important; } + .is-size-6-tablet { + font-size: 1rem !important; } + .is-size-7-tablet { + font-size: 0.75rem !important; } } + +@media screen and (max-width: 1023px) { + .is-size-1-touch { + font-size: 3rem !important; } + .is-size-2-touch { + font-size: 2.5rem !important; } + .is-size-3-touch { + font-size: 2rem !important; } + .is-size-4-touch { + font-size: 1.5rem !important; } + .is-size-5-touch { + font-size: 1.25rem !important; } + .is-size-6-touch { + font-size: 1rem !important; } + .is-size-7-touch { + font-size: 0.75rem !important; } } + +@media screen and (min-width: 1024px) { + .is-size-1-desktop { + font-size: 3rem !important; } + .is-size-2-desktop { + font-size: 2.5rem !important; } + .is-size-3-desktop { + font-size: 2rem !important; } + .is-size-4-desktop { + font-size: 1.5rem !important; } + .is-size-5-desktop { + font-size: 1.25rem !important; } + .is-size-6-desktop { + font-size: 1rem !important; } + .is-size-7-desktop { + font-size: 0.75rem !important; } } + +@media screen and (min-width: 1216px) { + .is-size-1-widescreen { + font-size: 3rem !important; } + .is-size-2-widescreen { + font-size: 2.5rem !important; } + .is-size-3-widescreen { + font-size: 2rem !important; } + .is-size-4-widescreen { + font-size: 1.5rem !important; } + .is-size-5-widescreen { + font-size: 1.25rem !important; } + .is-size-6-widescreen { + font-size: 1rem !important; } + .is-size-7-widescreen { + font-size: 0.75rem !important; } } + +@media screen and (min-width: 1408px) { + .is-size-1-fullhd { + font-size: 3rem !important; } + .is-size-2-fullhd { + font-size: 2.5rem !important; } + .is-size-3-fullhd { + font-size: 2rem !important; } + .is-size-4-fullhd { + font-size: 1.5rem !important; } + .is-size-5-fullhd { + font-size: 1.25rem !important; } + .is-size-6-fullhd { + font-size: 1rem !important; } + .is-size-7-fullhd { + font-size: 0.75rem !important; } } + +.has-text-centered { + text-align: center !important; } + +@media screen and (max-width: 768px) { + .has-text-centered-mobile { + text-align: center !important; } } + +@media screen and (min-width: 769px), print { + .has-text-centered-tablet { + text-align: center !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .has-text-centered-tablet-only { + text-align: center !important; } } + +@media screen and (max-width: 1023px) { + .has-text-centered-touch { + text-align: center !important; } } + +@media screen and (min-width: 1024px) { + .has-text-centered-desktop { + text-align: center !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .has-text-centered-desktop-only { + text-align: center !important; } } + +@media screen and (min-width: 1216px) { + .has-text-centered-widescreen { + text-align: center !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .has-text-centered-widescreen-only { + text-align: center !important; } } + +@media screen and (min-width: 1408px) { + .has-text-centered-fullhd { + text-align: center !important; } } + +.has-text-justified { + text-align: justify !important; } + +@media screen and (max-width: 768px) { + .has-text-justified-mobile { + text-align: justify !important; } } + +@media screen and (min-width: 769px), print { + .has-text-justified-tablet { + text-align: justify !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .has-text-justified-tablet-only { + text-align: justify !important; } } + +@media screen and (max-width: 1023px) { + .has-text-justified-touch { + text-align: justify !important; } } + +@media screen and (min-width: 1024px) { + .has-text-justified-desktop { + text-align: justify !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .has-text-justified-desktop-only { + text-align: justify !important; } } + +@media screen and (min-width: 1216px) { + .has-text-justified-widescreen { + text-align: justify !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .has-text-justified-widescreen-only { + text-align: justify !important; } } + +@media screen and (min-width: 1408px) { + .has-text-justified-fullhd { + text-align: justify !important; } } + +.has-text-left { + text-align: left !important; } + +@media screen and (max-width: 768px) { + .has-text-left-mobile { + text-align: left !important; } } + +@media screen and (min-width: 769px), print { + .has-text-left-tablet { + text-align: left !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .has-text-left-tablet-only { + text-align: left !important; } } + +@media screen and (max-width: 1023px) { + .has-text-left-touch { + text-align: left !important; } } + +@media screen and (min-width: 1024px) { + .has-text-left-desktop { + text-align: left !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .has-text-left-desktop-only { + text-align: left !important; } } + +@media screen and (min-width: 1216px) { + .has-text-left-widescreen { + text-align: left !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .has-text-left-widescreen-only { + text-align: left !important; } } + +@media screen and (min-width: 1408px) { + .has-text-left-fullhd { + text-align: left !important; } } + +.has-text-right { + text-align: right !important; } + +@media screen and (max-width: 768px) { + .has-text-right-mobile { + text-align: right !important; } } + +@media screen and (min-width: 769px), print { + .has-text-right-tablet { + text-align: right !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .has-text-right-tablet-only { + text-align: right !important; } } + +@media screen and (max-width: 1023px) { + .has-text-right-touch { + text-align: right !important; } } + +@media screen and (min-width: 1024px) { + .has-text-right-desktop { + text-align: right !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .has-text-right-desktop-only { + text-align: right !important; } } + +@media screen and (min-width: 1216px) { + .has-text-right-widescreen { + text-align: right !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .has-text-right-widescreen-only { + text-align: right !important; } } + +@media screen and (min-width: 1408px) { + .has-text-right-fullhd { + text-align: right !important; } } + +.is-capitalized { + text-transform: capitalize !important; } + +.is-lowercase { + text-transform: lowercase !important; } + +.is-uppercase { + text-transform: uppercase !important; } + +.has-text-white { + color: white !important; } + +a.has-text-white:hover, a.has-text-white:focus { + color: #e6e6e6 !important; } + +.has-text-black { + color: #0a0a0a !important; } + +a.has-text-black:hover, a.has-text-black:focus { + color: black !important; } + +.has-text-light { + color: whitesmoke !important; } + +a.has-text-light:hover, a.has-text-light:focus { + color: #dbdbdb !important; } + +.has-text-dark { + color: #363636 !important; } + +a.has-text-dark:hover, a.has-text-dark:focus { + color: #1c1c1c !important; } + +.has-text-primary { + color: #C93312 !important; } + +a.has-text-primary:hover, a.has-text-primary:focus { + color: #9a270e !important; } + +.has-text-link { + color: #3273dc !important; } + +a.has-text-link:hover, a.has-text-link:focus { + color: #205bbc !important; } + +.has-text-info { + color: #209cee !important; } + +a.has-text-info:hover, a.has-text-info:focus { + color: #0f81cc !important; } + +.has-text-success { + color: #23d160 !important; } + +a.has-text-success:hover, a.has-text-success:focus { + color: #1ca64c !important; } + +.has-text-warning { + color: #ffdd57 !important; } + +a.has-text-warning:hover, a.has-text-warning:focus { + color: #ffd324 !important; } + +.has-text-danger { + color: #ff3860 !important; } + +a.has-text-danger:hover, a.has-text-danger:focus { + color: #ff0537 !important; } + +.has-text-black-bis { + color: #121212 !important; } + +.has-text-black-ter { + color: #242424 !important; } + +.has-text-grey-darker { + color: #363636 !important; } + +.has-text-grey-dark { + color: #4a4a4a !important; } + +.has-text-grey { + color: #7a7a7a !important; } + +.has-text-grey-light { + color: #b5b5b5 !important; } + +.has-text-grey-lighter { + color: #dbdbdb !important; } + +.has-text-white-ter { + color: whitesmoke !important; } + +.has-text-white-bis { + color: #fafafa !important; } + +.has-text-weight-light { + font-weight: 300 !important; } + +.has-text-weight-normal { + font-weight: 400 !important; } + +.has-text-weight-semibold { + font-weight: 600 !important; } + +.has-text-weight-bold { + font-weight: 700 !important; } + +.is-block { + display: block !important; } + +@media screen and (max-width: 768px) { + .is-block-mobile { + display: block !important; } } + +@media screen and (min-width: 769px), print { + .is-block-tablet { + display: block !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .is-block-tablet-only { + display: block !important; } } + +@media screen and (max-width: 1023px) { + .is-block-touch { + display: block !important; } } + +@media screen and (min-width: 1024px) { + .is-block-desktop { + display: block !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .is-block-desktop-only { + display: block !important; } } + +@media screen and (min-width: 1216px) { + .is-block-widescreen { + display: block !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .is-block-widescreen-only { + display: block !important; } } + +@media screen and (min-width: 1408px) { + .is-block-fullhd { + display: block !important; } } + +.is-flex { + display: flex !important; } + +@media screen and (max-width: 768px) { + .is-flex-mobile { + display: flex !important; } } + +@media screen and (min-width: 769px), print { + .is-flex-tablet { + display: flex !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .is-flex-tablet-only { + display: flex !important; } } + +@media screen and (max-width: 1023px) { + .is-flex-touch { + display: flex !important; } } + +@media screen and (min-width: 1024px) { + .is-flex-desktop { + display: flex !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .is-flex-desktop-only { + display: flex !important; } } + +@media screen and (min-width: 1216px) { + .is-flex-widescreen { + display: flex !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .is-flex-widescreen-only { + display: flex !important; } } + +@media screen and (min-width: 1408px) { + .is-flex-fullhd { + display: flex !important; } } + +.is-inline { + display: inline !important; } + +@media screen and (max-width: 768px) { + .is-inline-mobile { + display: inline !important; } } + +@media screen and (min-width: 769px), print { + .is-inline-tablet { + display: inline !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .is-inline-tablet-only { + display: inline !important; } } + +@media screen and (max-width: 1023px) { + .is-inline-touch { + display: inline !important; } } + +@media screen and (min-width: 1024px) { + .is-inline-desktop { + display: inline !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .is-inline-desktop-only { + display: inline !important; } } + +@media screen and (min-width: 1216px) { + .is-inline-widescreen { + display: inline !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .is-inline-widescreen-only { + display: inline !important; } } + +@media screen and (min-width: 1408px) { + .is-inline-fullhd { + display: inline !important; } } + +.is-inline-block { + display: inline-block !important; } + +@media screen and (max-width: 768px) { + .is-inline-block-mobile { + display: inline-block !important; } } + +@media screen and (min-width: 769px), print { + .is-inline-block-tablet { + display: inline-block !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .is-inline-block-tablet-only { + display: inline-block !important; } } + +@media screen and (max-width: 1023px) { + .is-inline-block-touch { + display: inline-block !important; } } + +@media screen and (min-width: 1024px) { + .is-inline-block-desktop { + display: inline-block !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .is-inline-block-desktop-only { + display: inline-block !important; } } + +@media screen and (min-width: 1216px) { + .is-inline-block-widescreen { + display: inline-block !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .is-inline-block-widescreen-only { + display: inline-block !important; } } + +@media screen and (min-width: 1408px) { + .is-inline-block-fullhd { + display: inline-block !important; } } + +.is-inline-flex { + display: inline-flex !important; } + +@media screen and (max-width: 768px) { + .is-inline-flex-mobile { + display: inline-flex !important; } } + +@media screen and (min-width: 769px), print { + .is-inline-flex-tablet { + display: inline-flex !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .is-inline-flex-tablet-only { + display: inline-flex !important; } } + +@media screen and (max-width: 1023px) { + .is-inline-flex-touch { + display: inline-flex !important; } } + +@media screen and (min-width: 1024px) { + .is-inline-flex-desktop { + display: inline-flex !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .is-inline-flex-desktop-only { + display: inline-flex !important; } } + +@media screen and (min-width: 1216px) { + .is-inline-flex-widescreen { + display: inline-flex !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .is-inline-flex-widescreen-only { + display: inline-flex !important; } } + +@media screen and (min-width: 1408px) { + .is-inline-flex-fullhd { + display: inline-flex !important; } } + +.is-hidden { + display: none !important; } + +@media screen and (max-width: 768px) { + .is-hidden-mobile { + display: none !important; } } + +@media screen and (min-width: 769px), print { + .is-hidden-tablet { + display: none !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .is-hidden-tablet-only { + display: none !important; } } + +@media screen and (max-width: 1023px) { + .is-hidden-touch { + display: none !important; } } + +@media screen and (min-width: 1024px) { + .is-hidden-desktop { + display: none !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .is-hidden-desktop-only { + display: none !important; } } + +@media screen and (min-width: 1216px) { + .is-hidden-widescreen { + display: none !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .is-hidden-widescreen-only { + display: none !important; } } + +@media screen and (min-width: 1408px) { + .is-hidden-fullhd { + display: none !important; } } + +.is-invisible { + visibility: hidden !important; } + +@media screen and (max-width: 768px) { + .is-invisible-mobile { + visibility: hidden !important; } } + +@media screen and (min-width: 769px), print { + .is-invisible-tablet { + visibility: hidden !important; } } + +@media screen and (min-width: 769px) and (max-width: 1023px) { + .is-invisible-tablet-only { + visibility: hidden !important; } } + +@media screen and (max-width: 1023px) { + .is-invisible-touch { + visibility: hidden !important; } } + +@media screen and (min-width: 1024px) { + .is-invisible-desktop { + visibility: hidden !important; } } + +@media screen and (min-width: 1024px) and (max-width: 1215px) { + .is-invisible-desktop-only { + visibility: hidden !important; } } + +@media screen and (min-width: 1216px) { + .is-invisible-widescreen { + visibility: hidden !important; } } + +@media screen and (min-width: 1216px) and (max-width: 1407px) { + .is-invisible-widescreen-only { + visibility: hidden !important; } } + +@media screen and (min-width: 1408px) { + .is-invisible-fullhd { + visibility: hidden !important; } } + +.is-marginless { + margin: 0 !important; } + +.is-paddingless { + padding: 0 !important; } + +.is-radiusless { + border-radius: 0 !important; } + +.is-shadowless { + box-shadow: none !important; } + +.is-unselectable { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + +.column { + display: block; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + padding: 0.75rem; } + .columns.is-mobile > .column.is-narrow { + flex: none; } + .columns.is-mobile > .column.is-full { + flex: none; + width: 100%; } + .columns.is-mobile > .column.is-three-quarters { + flex: none; + width: 75%; } + .columns.is-mobile > .column.is-two-thirds { + flex: none; + width: 66.6666%; } + .columns.is-mobile > .column.is-half { + flex: none; + width: 50%; } + .columns.is-mobile > .column.is-one-third { + flex: none; + width: 33.3333%; } + .columns.is-mobile > .column.is-one-quarter { + flex: none; + width: 25%; } + .columns.is-mobile > .column.is-one-fifth { + flex: none; + width: 20%; } + .columns.is-mobile > .column.is-two-fifths { + flex: none; + width: 40%; } + .columns.is-mobile > .column.is-three-fifths { + flex: none; + width: 60%; } + .columns.is-mobile > .column.is-four-fifths { + flex: none; + width: 80%; } + .columns.is-mobile > .column.is-offset-three-quarters { + margin-left: 75%; } + .columns.is-mobile > .column.is-offset-two-thirds { + margin-left: 66.6666%; } + .columns.is-mobile > .column.is-offset-half { + margin-left: 50%; } + .columns.is-mobile > .column.is-offset-one-third { + margin-left: 33.3333%; } + .columns.is-mobile > .column.is-offset-one-quarter { + margin-left: 25%; } + .columns.is-mobile > .column.is-offset-one-fifth { + margin-left: 20%; } + .columns.is-mobile > .column.is-offset-two-fifths { + margin-left: 40%; } + .columns.is-mobile > .column.is-offset-three-fifths { + margin-left: 60%; } + .columns.is-mobile > .column.is-offset-four-fifths { + margin-left: 80%; } + .columns.is-mobile > .column.is-1 { + flex: none; + width: 8.33333%; } + .columns.is-mobile > .column.is-offset-1 { + margin-left: 8.33333%; } + .columns.is-mobile > .column.is-2 { + flex: none; + width: 16.66667%; } + .columns.is-mobile > .column.is-offset-2 { + margin-left: 16.66667%; } + .columns.is-mobile > .column.is-3 { + flex: none; + width: 25%; } + .columns.is-mobile > .column.is-offset-3 { + margin-left: 25%; } + .columns.is-mobile > .column.is-4 { + flex: none; + width: 33.33333%; } + .columns.is-mobile > .column.is-offset-4 { + margin-left: 33.33333%; } + .columns.is-mobile > .column.is-5 { + flex: none; + width: 41.66667%; } + .columns.is-mobile > .column.is-offset-5 { + margin-left: 41.66667%; } + .columns.is-mobile > .column.is-6 { + flex: none; + width: 50%; } + .columns.is-mobile > .column.is-offset-6 { + margin-left: 50%; } + .columns.is-mobile > .column.is-7 { + flex: none; + width: 58.33333%; } + .columns.is-mobile > .column.is-offset-7 { + margin-left: 58.33333%; } + .columns.is-mobile > .column.is-8 { + flex: none; + width: 66.66667%; } + .columns.is-mobile > .column.is-offset-8 { + margin-left: 66.66667%; } + .columns.is-mobile > .column.is-9 { + flex: none; + width: 75%; } + .columns.is-mobile > .column.is-offset-9 { + margin-left: 75%; } + .columns.is-mobile > .column.is-10 { + flex: none; + width: 83.33333%; } + .columns.is-mobile > .column.is-offset-10 { + margin-left: 83.33333%; } + .columns.is-mobile > .column.is-11 { + flex: none; + width: 91.66667%; } + .columns.is-mobile > .column.is-offset-11 { + margin-left: 91.66667%; } + .columns.is-mobile > .column.is-12 { + flex: none; + width: 100%; } + .columns.is-mobile > .column.is-offset-12 { + margin-left: 100%; } + @media screen and (max-width: 768px) { + .column.is-narrow-mobile { + flex: none; } + .column.is-full-mobile { + flex: none; + width: 100%; } + .column.is-three-quarters-mobile { + flex: none; + width: 75%; } + .column.is-two-thirds-mobile { + flex: none; + width: 66.6666%; } + .column.is-half-mobile { + flex: none; + width: 50%; } + .column.is-one-third-mobile { + flex: none; + width: 33.3333%; } + .column.is-one-quarter-mobile { + flex: none; + width: 25%; } + .column.is-one-fifth-mobile { + flex: none; + width: 20%; } + .column.is-two-fifths-mobile { + flex: none; + width: 40%; } + .column.is-three-fifths-mobile { + flex: none; + width: 60%; } + .column.is-four-fifths-mobile { + flex: none; + width: 80%; } + .column.is-offset-three-quarters-mobile { + margin-left: 75%; } + .column.is-offset-two-thirds-mobile { + margin-left: 66.6666%; } + .column.is-offset-half-mobile { + margin-left: 50%; } + .column.is-offset-one-third-mobile { + margin-left: 33.3333%; } + .column.is-offset-one-quarter-mobile { + margin-left: 25%; } + .column.is-offset-one-fifth-mobile { + margin-left: 20%; } + .column.is-offset-two-fifths-mobile { + margin-left: 40%; } + .column.is-offset-three-fifths-mobile { + margin-left: 60%; } + .column.is-offset-four-fifths-mobile { + margin-left: 80%; } + .column.is-1-mobile { + flex: none; + width: 8.33333%; } + .column.is-offset-1-mobile { + margin-left: 8.33333%; } + .column.is-2-mobile { + flex: none; + width: 16.66667%; } + .column.is-offset-2-mobile { + margin-left: 16.66667%; } + .column.is-3-mobile { + flex: none; + width: 25%; } + .column.is-offset-3-mobile { + margin-left: 25%; } + .column.is-4-mobile { + flex: none; + width: 33.33333%; } + .column.is-offset-4-mobile { + margin-left: 33.33333%; } + .column.is-5-mobile { + flex: none; + width: 41.66667%; } + .column.is-offset-5-mobile { + margin-left: 41.66667%; } + .column.is-6-mobile { + flex: none; + width: 50%; } + .column.is-offset-6-mobile { + margin-left: 50%; } + .column.is-7-mobile { + flex: none; + width: 58.33333%; } + .column.is-offset-7-mobile { + margin-left: 58.33333%; } + .column.is-8-mobile { + flex: none; + width: 66.66667%; } + .column.is-offset-8-mobile { + margin-left: 66.66667%; } + .column.is-9-mobile { + flex: none; + width: 75%; } + .column.is-offset-9-mobile { + margin-left: 75%; } + .column.is-10-mobile { + flex: none; + width: 83.33333%; } + .column.is-offset-10-mobile { + margin-left: 83.33333%; } + .column.is-11-mobile { + flex: none; + width: 91.66667%; } + .column.is-offset-11-mobile { + margin-left: 91.66667%; } + .column.is-12-mobile { + flex: none; + width: 100%; } + .column.is-offset-12-mobile { + margin-left: 100%; } } + @media screen and (min-width: 769px), print { + .column.is-narrow, .column.is-narrow-tablet { + flex: none; } + .column.is-full, .column.is-full-tablet { + flex: none; + width: 100%; } + .column.is-three-quarters, .column.is-three-quarters-tablet { + flex: none; + width: 75%; } + .column.is-two-thirds, .column.is-two-thirds-tablet { + flex: none; + width: 66.6666%; } + .column.is-half, .column.is-half-tablet { + flex: none; + width: 50%; } + .column.is-one-third, .column.is-one-third-tablet { + flex: none; + width: 33.3333%; } + .column.is-one-quarter, .column.is-one-quarter-tablet { + flex: none; + width: 25%; } + .column.is-one-fifth, .column.is-one-fifth-tablet { + flex: none; + width: 20%; } + .column.is-two-fifths, .column.is-two-fifths-tablet { + flex: none; + width: 40%; } + .column.is-three-fifths, .column.is-three-fifths-tablet { + flex: none; + width: 60%; } + .column.is-four-fifths, .column.is-four-fifths-tablet { + flex: none; + width: 80%; } + .column.is-offset-three-quarters, .column.is-offset-three-quarters-tablet { + margin-left: 75%; } + .column.is-offset-two-thirds, .column.is-offset-two-thirds-tablet { + margin-left: 66.6666%; } + .column.is-offset-half, .column.is-offset-half-tablet { + margin-left: 50%; } + .column.is-offset-one-third, .column.is-offset-one-third-tablet { + margin-left: 33.3333%; } + .column.is-offset-one-quarter, .column.is-offset-one-quarter-tablet { + margin-left: 25%; } + .column.is-offset-one-fifth, .column.is-offset-one-fifth-tablet { + margin-left: 20%; } + .column.is-offset-two-fifths, .column.is-offset-two-fifths-tablet { + margin-left: 40%; } + .column.is-offset-three-fifths, .column.is-offset-three-fifths-tablet { + margin-left: 60%; } + .column.is-offset-four-fifths, .column.is-offset-four-fifths-tablet { + margin-left: 80%; } + .column.is-1, .column.is-1-tablet { + flex: none; + width: 8.33333%; } + .column.is-offset-1, .column.is-offset-1-tablet { + margin-left: 8.33333%; } + .column.is-2, .column.is-2-tablet { + flex: none; + width: 16.66667%; } + .column.is-offset-2, .column.is-offset-2-tablet { + margin-left: 16.66667%; } + .column.is-3, .column.is-3-tablet { + flex: none; + width: 25%; } + .column.is-offset-3, .column.is-offset-3-tablet { + margin-left: 25%; } + .column.is-4, .column.is-4-tablet { + flex: none; + width: 33.33333%; } + .column.is-offset-4, .column.is-offset-4-tablet { + margin-left: 33.33333%; } + .column.is-5, .column.is-5-tablet { + flex: none; + width: 41.66667%; } + .column.is-offset-5, .column.is-offset-5-tablet { + margin-left: 41.66667%; } + .column.is-6, .column.is-6-tablet { + flex: none; + width: 50%; } + .column.is-offset-6, .column.is-offset-6-tablet { + margin-left: 50%; } + .column.is-7, .column.is-7-tablet { + flex: none; + width: 58.33333%; } + .column.is-offset-7, .column.is-offset-7-tablet { + margin-left: 58.33333%; } + .column.is-8, .column.is-8-tablet { + flex: none; + width: 66.66667%; } + .column.is-offset-8, .column.is-offset-8-tablet { + margin-left: 66.66667%; } + .column.is-9, .column.is-9-tablet { + flex: none; + width: 75%; } + .column.is-offset-9, .column.is-offset-9-tablet { + margin-left: 75%; } + .column.is-10, .column.is-10-tablet { + flex: none; + width: 83.33333%; } + .column.is-offset-10, .column.is-offset-10-tablet { + margin-left: 83.33333%; } + .column.is-11, .column.is-11-tablet { + flex: none; + width: 91.66667%; } + .column.is-offset-11, .column.is-offset-11-tablet { + margin-left: 91.66667%; } + .column.is-12, .column.is-12-tablet { + flex: none; + width: 100%; } + .column.is-offset-12, .column.is-offset-12-tablet { + margin-left: 100%; } } + @media screen and (max-width: 1023px) { + .column.is-narrow-touch { + flex: none; } + .column.is-full-touch { + flex: none; + width: 100%; } + .column.is-three-quarters-touch { + flex: none; + width: 75%; } + .column.is-two-thirds-touch { + flex: none; + width: 66.6666%; } + .column.is-half-touch { + flex: none; + width: 50%; } + .column.is-one-third-touch { + flex: none; + width: 33.3333%; } + .column.is-one-quarter-touch { + flex: none; + width: 25%; } + .column.is-one-fifth-touch { + flex: none; + width: 20%; } + .column.is-two-fifths-touch { + flex: none; + width: 40%; } + .column.is-three-fifths-touch { + flex: none; + width: 60%; } + .column.is-four-fifths-touch { + flex: none; + width: 80%; } + .column.is-offset-three-quarters-touch { + margin-left: 75%; } + .column.is-offset-two-thirds-touch { + margin-left: 66.6666%; } + .column.is-offset-half-touch { + margin-left: 50%; } + .column.is-offset-one-third-touch { + margin-left: 33.3333%; } + .column.is-offset-one-quarter-touch { + margin-left: 25%; } + .column.is-offset-one-fifth-touch { + margin-left: 20%; } + .column.is-offset-two-fifths-touch { + margin-left: 40%; } + .column.is-offset-three-fifths-touch { + margin-left: 60%; } + .column.is-offset-four-fifths-touch { + margin-left: 80%; } + .column.is-1-touch { + flex: none; + width: 8.33333%; } + .column.is-offset-1-touch { + margin-left: 8.33333%; } + .column.is-2-touch { + flex: none; + width: 16.66667%; } + .column.is-offset-2-touch { + margin-left: 16.66667%; } + .column.is-3-touch { + flex: none; + width: 25%; } + .column.is-offset-3-touch { + margin-left: 25%; } + .column.is-4-touch { + flex: none; + width: 33.33333%; } + .column.is-offset-4-touch { + margin-left: 33.33333%; } + .column.is-5-touch { + flex: none; + width: 41.66667%; } + .column.is-offset-5-touch { + margin-left: 41.66667%; } + .column.is-6-touch { + flex: none; + width: 50%; } + .column.is-offset-6-touch { + margin-left: 50%; } + .column.is-7-touch { + flex: none; + width: 58.33333%; } + .column.is-offset-7-touch { + margin-left: 58.33333%; } + .column.is-8-touch { + flex: none; + width: 66.66667%; } + .column.is-offset-8-touch { + margin-left: 66.66667%; } + .column.is-9-touch { + flex: none; + width: 75%; } + .column.is-offset-9-touch { + margin-left: 75%; } + .column.is-10-touch { + flex: none; + width: 83.33333%; } + .column.is-offset-10-touch { + margin-left: 83.33333%; } + .column.is-11-touch { + flex: none; + width: 91.66667%; } + .column.is-offset-11-touch { + margin-left: 91.66667%; } + .column.is-12-touch { + flex: none; + width: 100%; } + .column.is-offset-12-touch { + margin-left: 100%; } } + @media screen and (min-width: 1024px) { + .column.is-narrow-desktop { + flex: none; } + .column.is-full-desktop { + flex: none; + width: 100%; } + .column.is-three-quarters-desktop { + flex: none; + width: 75%; } + .column.is-two-thirds-desktop { + flex: none; + width: 66.6666%; } + .column.is-half-desktop { + flex: none; + width: 50%; } + .column.is-one-third-desktop { + flex: none; + width: 33.3333%; } + .column.is-one-quarter-desktop { + flex: none; + width: 25%; } + .column.is-one-fifth-desktop { + flex: none; + width: 20%; } + .column.is-two-fifths-desktop { + flex: none; + width: 40%; } + .column.is-three-fifths-desktop { + flex: none; + width: 60%; } + .column.is-four-fifths-desktop { + flex: none; + width: 80%; } + .column.is-offset-three-quarters-desktop { + margin-left: 75%; } + .column.is-offset-two-thirds-desktop { + margin-left: 66.6666%; } + .column.is-offset-half-desktop { + margin-left: 50%; } + .column.is-offset-one-third-desktop { + margin-left: 33.3333%; } + .column.is-offset-one-quarter-desktop { + margin-left: 25%; } + .column.is-offset-one-fifth-desktop { + margin-left: 20%; } + .column.is-offset-two-fifths-desktop { + margin-left: 40%; } + .column.is-offset-three-fifths-desktop { + margin-left: 60%; } + .column.is-offset-four-fifths-desktop { + margin-left: 80%; } + .column.is-1-desktop { + flex: none; + width: 8.33333%; } + .column.is-offset-1-desktop { + margin-left: 8.33333%; } + .column.is-2-desktop { + flex: none; + width: 16.66667%; } + .column.is-offset-2-desktop { + margin-left: 16.66667%; } + .column.is-3-desktop { + flex: none; + width: 25%; } + .column.is-offset-3-desktop { + margin-left: 25%; } + .column.is-4-desktop { + flex: none; + width: 33.33333%; } + .column.is-offset-4-desktop { + margin-left: 33.33333%; } + .column.is-5-desktop { + flex: none; + width: 41.66667%; } + .column.is-offset-5-desktop { + margin-left: 41.66667%; } + .column.is-6-desktop { + flex: none; + width: 50%; } + .column.is-offset-6-desktop { + margin-left: 50%; } + .column.is-7-desktop { + flex: none; + width: 58.33333%; } + .column.is-offset-7-desktop { + margin-left: 58.33333%; } + .column.is-8-desktop { + flex: none; + width: 66.66667%; } + .column.is-offset-8-desktop { + margin-left: 66.66667%; } + .column.is-9-desktop { + flex: none; + width: 75%; } + .column.is-offset-9-desktop { + margin-left: 75%; } + .column.is-10-desktop { + flex: none; + width: 83.33333%; } + .column.is-offset-10-desktop { + margin-left: 83.33333%; } + .column.is-11-desktop { + flex: none; + width: 91.66667%; } + .column.is-offset-11-desktop { + margin-left: 91.66667%; } + .column.is-12-desktop { + flex: none; + width: 100%; } + .column.is-offset-12-desktop { + margin-left: 100%; } } + @media screen and (min-width: 1216px) { + .column.is-narrow-widescreen { + flex: none; } + .column.is-full-widescreen { + flex: none; + width: 100%; } + .column.is-three-quarters-widescreen { + flex: none; + width: 75%; } + .column.is-two-thirds-widescreen { + flex: none; + width: 66.6666%; } + .column.is-half-widescreen { + flex: none; + width: 50%; } + .column.is-one-third-widescreen { + flex: none; + width: 33.3333%; } + .column.is-one-quarter-widescreen { + flex: none; + width: 25%; } + .column.is-one-fifth-widescreen { + flex: none; + width: 20%; } + .column.is-two-fifths-widescreen { + flex: none; + width: 40%; } + .column.is-three-fifths-widescreen { + flex: none; + width: 60%; } + .column.is-four-fifths-widescreen { + flex: none; + width: 80%; } + .column.is-offset-three-quarters-widescreen { + margin-left: 75%; } + .column.is-offset-two-thirds-widescreen { + margin-left: 66.6666%; } + .column.is-offset-half-widescreen { + margin-left: 50%; } + .column.is-offset-one-third-widescreen { + margin-left: 33.3333%; } + .column.is-offset-one-quarter-widescreen { + margin-left: 25%; } + .column.is-offset-one-fifth-widescreen { + margin-left: 20%; } + .column.is-offset-two-fifths-widescreen { + margin-left: 40%; } + .column.is-offset-three-fifths-widescreen { + margin-left: 60%; } + .column.is-offset-four-fifths-widescreen { + margin-left: 80%; } + .column.is-1-widescreen { + flex: none; + width: 8.33333%; } + .column.is-offset-1-widescreen { + margin-left: 8.33333%; } + .column.is-2-widescreen { + flex: none; + width: 16.66667%; } + .column.is-offset-2-widescreen { + margin-left: 16.66667%; } + .column.is-3-widescreen { + flex: none; + width: 25%; } + .column.is-offset-3-widescreen { + margin-left: 25%; } + .column.is-4-widescreen { + flex: none; + width: 33.33333%; } + .column.is-offset-4-widescreen { + margin-left: 33.33333%; } + .column.is-5-widescreen { + flex: none; + width: 41.66667%; } + .column.is-offset-5-widescreen { + margin-left: 41.66667%; } + .column.is-6-widescreen { + flex: none; + width: 50%; } + .column.is-offset-6-widescreen { + margin-left: 50%; } + .column.is-7-widescreen { + flex: none; + width: 58.33333%; } + .column.is-offset-7-widescreen { + margin-left: 58.33333%; } + .column.is-8-widescreen { + flex: none; + width: 66.66667%; } + .column.is-offset-8-widescreen { + margin-left: 66.66667%; } + .column.is-9-widescreen { + flex: none; + width: 75%; } + .column.is-offset-9-widescreen { + margin-left: 75%; } + .column.is-10-widescreen { + flex: none; + width: 83.33333%; } + .column.is-offset-10-widescreen { + margin-left: 83.33333%; } + .column.is-11-widescreen { + flex: none; + width: 91.66667%; } + .column.is-offset-11-widescreen { + margin-left: 91.66667%; } + .column.is-12-widescreen { + flex: none; + width: 100%; } + .column.is-offset-12-widescreen { + margin-left: 100%; } } + @media screen and (min-width: 1408px) { + .column.is-narrow-fullhd { + flex: none; } + .column.is-full-fullhd { + flex: none; + width: 100%; } + .column.is-three-quarters-fullhd { + flex: none; + width: 75%; } + .column.is-two-thirds-fullhd { + flex: none; + width: 66.6666%; } + .column.is-half-fullhd { + flex: none; + width: 50%; } + .column.is-one-third-fullhd { + flex: none; + width: 33.3333%; } + .column.is-one-quarter-fullhd { + flex: none; + width: 25%; } + .column.is-one-fifth-fullhd { + flex: none; + width: 20%; } + .column.is-two-fifths-fullhd { + flex: none; + width: 40%; } + .column.is-three-fifths-fullhd { + flex: none; + width: 60%; } + .column.is-four-fifths-fullhd { + flex: none; + width: 80%; } + .column.is-offset-three-quarters-fullhd { + margin-left: 75%; } + .column.is-offset-two-thirds-fullhd { + margin-left: 66.6666%; } + .column.is-offset-half-fullhd { + margin-left: 50%; } + .column.is-offset-one-third-fullhd { + margin-left: 33.3333%; } + .column.is-offset-one-quarter-fullhd { + margin-left: 25%; } + .column.is-offset-one-fifth-fullhd { + margin-left: 20%; } + .column.is-offset-two-fifths-fullhd { + margin-left: 40%; } + .column.is-offset-three-fifths-fullhd { + margin-left: 60%; } + .column.is-offset-four-fifths-fullhd { + margin-left: 80%; } + .column.is-1-fullhd { + flex: none; + width: 8.33333%; } + .column.is-offset-1-fullhd { + margin-left: 8.33333%; } + .column.is-2-fullhd { + flex: none; + width: 16.66667%; } + .column.is-offset-2-fullhd { + margin-left: 16.66667%; } + .column.is-3-fullhd { + flex: none; + width: 25%; } + .column.is-offset-3-fullhd { + margin-left: 25%; } + .column.is-4-fullhd { + flex: none; + width: 33.33333%; } + .column.is-offset-4-fullhd { + margin-left: 33.33333%; } + .column.is-5-fullhd { + flex: none; + width: 41.66667%; } + .column.is-offset-5-fullhd { + margin-left: 41.66667%; } + .column.is-6-fullhd { + flex: none; + width: 50%; } + .column.is-offset-6-fullhd { + margin-left: 50%; } + .column.is-7-fullhd { + flex: none; + width: 58.33333%; } + .column.is-offset-7-fullhd { + margin-left: 58.33333%; } + .column.is-8-fullhd { + flex: none; + width: 66.66667%; } + .column.is-offset-8-fullhd { + margin-left: 66.66667%; } + .column.is-9-fullhd { + flex: none; + width: 75%; } + .column.is-offset-9-fullhd { + margin-left: 75%; } + .column.is-10-fullhd { + flex: none; + width: 83.33333%; } + .column.is-offset-10-fullhd { + margin-left: 83.33333%; } + .column.is-11-fullhd { + flex: none; + width: 91.66667%; } + .column.is-offset-11-fullhd { + margin-left: 91.66667%; } + .column.is-12-fullhd { + flex: none; + width: 100%; } + .column.is-offset-12-fullhd { + margin-left: 100%; } } + +.columns { + margin-left: -0.75rem; + margin-right: -0.75rem; + margin-top: -0.75rem; } + .columns:last-child { + margin-bottom: -0.75rem; } + .columns:not(:last-child) { + margin-bottom: calc(1.5rem - 0.75rem); } + .columns.is-centered { + justify-content: center; } + .columns.is-gapless { + margin-left: 0; + margin-right: 0; + margin-top: 0; } + .columns.is-gapless > .column { + margin: 0; + padding: 0 !important; } + .columns.is-gapless:not(:last-child) { + margin-bottom: 1.5rem; } + .columns.is-gapless:last-child { + margin-bottom: 0; } + .columns.is-mobile { + display: flex; } + .columns.is-multiline { + flex-wrap: wrap; } + .columns.is-vcentered { + align-items: center; } + @media screen and (min-width: 769px), print { + .columns:not(.is-desktop) { + display: flex; } } + @media screen and (min-width: 1024px) { + .columns.is-desktop { + display: flex; } } + +.columns.is-variable { + --columnGap: 0.75rem; + margin-left: calc(-1 * var(--columnGap)); + margin-right: calc(-1 * var(--columnGap)); } + .columns.is-variable .column { + padding-left: var(--columnGap); + padding-right: var(--columnGap); } + .columns.is-variable.is-0 { + --columnGap: 0rem; } + .columns.is-variable.is-1 { + --columnGap: 0.25rem; } + .columns.is-variable.is-2 { + --columnGap: 0.5rem; } + .columns.is-variable.is-3 { + --columnGap: 0.75rem; } + .columns.is-variable.is-4 { + --columnGap: 1rem; } + .columns.is-variable.is-5 { + --columnGap: 1.25rem; } + .columns.is-variable.is-6 { + --columnGap: 1.5rem; } + .columns.is-variable.is-7 { + --columnGap: 1.75rem; } + .columns.is-variable.is-8 { + --columnGap: 2rem; } + +.tile { + align-items: stretch; + display: block; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 1; + min-height: min-content; } + .tile.is-ancestor { + margin-left: -0.75rem; + margin-right: -0.75rem; + margin-top: -0.75rem; } + .tile.is-ancestor:last-child { + margin-bottom: -0.75rem; } + .tile.is-ancestor:not(:last-child) { + margin-bottom: 0.75rem; } + .tile.is-child { + margin: 0 !important; } + .tile.is-parent { + padding: 0.75rem; } + .tile.is-vertical { + flex-direction: column; } + .tile.is-vertical > .tile.is-child:not(:last-child) { + margin-bottom: 1.5rem !important; } + @media screen and (min-width: 769px), print { + .tile:not(.is-child) { + display: flex; } + .tile.is-1 { + flex: none; + width: 8.33333%; } + .tile.is-2 { + flex: none; + width: 16.66667%; } + .tile.is-3 { + flex: none; + width: 25%; } + .tile.is-4 { + flex: none; + width: 33.33333%; } + .tile.is-5 { + flex: none; + width: 41.66667%; } + .tile.is-6 { + flex: none; + width: 50%; } + .tile.is-7 { + flex: none; + width: 58.33333%; } + .tile.is-8 { + flex: none; + width: 66.66667%; } + .tile.is-9 { + flex: none; + width: 75%; } + .tile.is-10 { + flex: none; + width: 83.33333%; } + .tile.is-11 { + flex: none; + width: 91.66667%; } + .tile.is-12 { + flex: none; + width: 100%; } } + +.box { + background-color: white; + border-radius: 5px; + box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); + color: #4a4a4a; + display: block; + padding: 1.25rem; } + .box:not(:last-child) { + margin-bottom: 1.5rem; } + +a.box:hover, a.box:focus { + box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px #3273dc; } + +a.box:active { + box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2), 0 0 0 1px #3273dc; } + +.button { + -moz-appearance: none; + -webkit-appearance: none; + align-items: center; + border: 1px solid transparent; + border-radius: 3px; + box-shadow: none; + display: inline-flex; + font-size: 1rem; + height: 2.25em; + justify-content: flex-start; + line-height: 1.5; + padding-bottom: calc(0.375em - 1px); + padding-left: calc(0.625em - 1px); + padding-right: calc(0.625em - 1px); + padding-top: calc(0.375em - 1px); + position: relative; + vertical-align: top; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + background-color: white; + border-color: #dbdbdb; + color: #363636; + cursor: pointer; + justify-content: center; + padding-left: 0.75em; + padding-right: 0.75em; + text-align: center; + white-space: nowrap; } + .button:focus, .button.is-focused, .button:active, .button.is-active { + outline: none; } + .button[disabled] { + cursor: not-allowed; } + .button strong { + color: inherit; } + .button .icon, .button .icon.is-small, .button .icon.is-medium, .button .icon.is-large { + height: 1.5em; + width: 1.5em; } + .button .icon:first-child:not(:last-child) { + margin-left: calc(-0.375em - 1px); + margin-right: 0.1875em; } + .button .icon:last-child:not(:first-child) { + margin-left: 0.1875em; + margin-right: calc(-0.375em - 1px); } + .button .icon:first-child:last-child { + margin-left: calc(-0.375em - 1px); + margin-right: calc(-0.375em - 1px); } + .button:hover, .button.is-hovered { + border-color: #b5b5b5; + color: #363636; } + .button:focus, .button.is-focused { + border-color: #3273dc; + color: #363636; } + .button:focus:not(:active), .button.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } + .button:active, .button.is-active { + border-color: #4a4a4a; + color: #363636; } + .button.is-text { + background-color: transparent; + border-color: transparent; + color: #4a4a4a; + text-decoration: underline; } + .button.is-text:hover, .button.is-text.is-hovered, .button.is-text:focus, .button.is-text.is-focused { + background-color: whitesmoke; + color: #363636; } + .button.is-text:active, .button.is-text.is-active { + background-color: #e8e8e8; + color: #363636; } + .button.is-text[disabled] { + background-color: transparent; + border-color: transparent; + box-shadow: none; } + .button.is-white { + background-color: white; + border-color: transparent; + color: #0a0a0a; } + .button.is-white:hover, .button.is-white.is-hovered { + background-color: #f9f9f9; + border-color: transparent; + color: #0a0a0a; } + .button.is-white:focus, .button.is-white.is-focused { + border-color: transparent; + color: #0a0a0a; } + .button.is-white:focus:not(:active), .button.is-white.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); } + .button.is-white:active, .button.is-white.is-active { + background-color: #f2f2f2; + border-color: transparent; + color: #0a0a0a; } + .button.is-white[disabled] { + background-color: white; + border-color: transparent; + box-shadow: none; } + .button.is-white.is-inverted { + background-color: #0a0a0a; + color: white; } + .button.is-white.is-inverted:hover { + background-color: black; } + .button.is-white.is-inverted[disabled] { + background-color: #0a0a0a; + border-color: transparent; + box-shadow: none; + color: white; } + .button.is-white.is-loading:after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; } + .button.is-white.is-outlined { + background-color: transparent; + border-color: white; + color: white; } + .button.is-white.is-outlined:hover, .button.is-white.is-outlined:focus { + background-color: white; + border-color: white; + color: #0a0a0a; } + .button.is-white.is-outlined.is-loading:after { + border-color: transparent transparent white white !important; } + .button.is-white.is-outlined[disabled] { + background-color: transparent; + border-color: white; + box-shadow: none; + color: white; } + .button.is-white.is-inverted.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + color: #0a0a0a; } + .button.is-white.is-inverted.is-outlined:hover, .button.is-white.is-inverted.is-outlined:focus { + background-color: #0a0a0a; + color: white; } + .button.is-white.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: #0a0a0a; + box-shadow: none; + color: #0a0a0a; } + .button.is-black { + background-color: #0a0a0a; + border-color: transparent; + color: white; } + .button.is-black:hover, .button.is-black.is-hovered { + background-color: #040404; + border-color: transparent; + color: white; } + .button.is-black:focus, .button.is-black.is-focused { + border-color: transparent; + color: white; } + .button.is-black:focus:not(:active), .button.is-black.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); } + .button.is-black:active, .button.is-black.is-active { + background-color: black; + border-color: transparent; + color: white; } + .button.is-black[disabled] { + background-color: #0a0a0a; + border-color: transparent; + box-shadow: none; } + .button.is-black.is-inverted { + background-color: white; + color: #0a0a0a; } + .button.is-black.is-inverted:hover { + background-color: #f2f2f2; } + .button.is-black.is-inverted[disabled] { + background-color: white; + border-color: transparent; + box-shadow: none; + color: #0a0a0a; } + .button.is-black.is-loading:after { + border-color: transparent transparent white white !important; } + .button.is-black.is-outlined { + background-color: transparent; + border-color: #0a0a0a; + color: #0a0a0a; } + .button.is-black.is-outlined:hover, .button.is-black.is-outlined:focus { + background-color: #0a0a0a; + border-color: #0a0a0a; + color: white; } + .button.is-black.is-outlined.is-loading:after { + border-color: transparent transparent #0a0a0a #0a0a0a !important; } + .button.is-black.is-outlined[disabled] { + background-color: transparent; + border-color: #0a0a0a; + box-shadow: none; + color: #0a0a0a; } + .button.is-black.is-inverted.is-outlined { + background-color: transparent; + border-color: white; + color: white; } + .button.is-black.is-inverted.is-outlined:hover, .button.is-black.is-inverted.is-outlined:focus { + background-color: white; + color: #0a0a0a; } + .button.is-black.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: white; + box-shadow: none; + color: white; } + .button.is-light { + background-color: whitesmoke; + border-color: transparent; + color: #363636; } + .button.is-light:hover, .button.is-light.is-hovered { + background-color: #eeeeee; + border-color: transparent; + color: #363636; } + .button.is-light:focus, .button.is-light.is-focused { + border-color: transparent; + color: #363636; } + .button.is-light:focus:not(:active), .button.is-light.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); } + .button.is-light:active, .button.is-light.is-active { + background-color: #e8e8e8; + border-color: transparent; + color: #363636; } + .button.is-light[disabled] { + background-color: whitesmoke; + border-color: transparent; + box-shadow: none; } + .button.is-light.is-inverted { + background-color: #363636; + color: whitesmoke; } + .button.is-light.is-inverted:hover { + background-color: #292929; } + .button.is-light.is-inverted[disabled] { + background-color: #363636; + border-color: transparent; + box-shadow: none; + color: whitesmoke; } + .button.is-light.is-loading:after { + border-color: transparent transparent #363636 #363636 !important; } + .button.is-light.is-outlined { + background-color: transparent; + border-color: whitesmoke; + color: whitesmoke; } + .button.is-light.is-outlined:hover, .button.is-light.is-outlined:focus { + background-color: whitesmoke; + border-color: whitesmoke; + color: #363636; } + .button.is-light.is-outlined.is-loading:after { + border-color: transparent transparent whitesmoke whitesmoke !important; } + .button.is-light.is-outlined[disabled] { + background-color: transparent; + border-color: whitesmoke; + box-shadow: none; + color: whitesmoke; } + .button.is-light.is-inverted.is-outlined { + background-color: transparent; + border-color: #363636; + color: #363636; } + .button.is-light.is-inverted.is-outlined:hover, .button.is-light.is-inverted.is-outlined:focus { + background-color: #363636; + color: whitesmoke; } + .button.is-light.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: #363636; + box-shadow: none; + color: #363636; } + .button.is-dark { + background-color: #363636; + border-color: transparent; + color: whitesmoke; } + .button.is-dark:hover, .button.is-dark.is-hovered { + background-color: #2f2f2f; + border-color: transparent; + color: whitesmoke; } + .button.is-dark:focus, .button.is-dark.is-focused { + border-color: transparent; + color: whitesmoke; } + .button.is-dark:focus:not(:active), .button.is-dark.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); } + .button.is-dark:active, .button.is-dark.is-active { + background-color: #292929; + border-color: transparent; + color: whitesmoke; } + .button.is-dark[disabled] { + background-color: #363636; + border-color: transparent; + box-shadow: none; } + .button.is-dark.is-inverted { + background-color: whitesmoke; + color: #363636; } + .button.is-dark.is-inverted:hover { + background-color: #e8e8e8; } + .button.is-dark.is-inverted[disabled] { + background-color: whitesmoke; + border-color: transparent; + box-shadow: none; + color: #363636; } + .button.is-dark.is-loading:after { + border-color: transparent transparent whitesmoke whitesmoke !important; } + .button.is-dark.is-outlined { + background-color: transparent; + border-color: #363636; + color: #363636; } + .button.is-dark.is-outlined:hover, .button.is-dark.is-outlined:focus { + background-color: #363636; + border-color: #363636; + color: whitesmoke; } + .button.is-dark.is-outlined.is-loading:after { + border-color: transparent transparent #363636 #363636 !important; } + .button.is-dark.is-outlined[disabled] { + background-color: transparent; + border-color: #363636; + box-shadow: none; + color: #363636; } + .button.is-dark.is-inverted.is-outlined { + background-color: transparent; + border-color: whitesmoke; + color: whitesmoke; } + .button.is-dark.is-inverted.is-outlined:hover, .button.is-dark.is-inverted.is-outlined:focus { + background-color: whitesmoke; + color: #363636; } + .button.is-dark.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: whitesmoke; + box-shadow: none; + color: whitesmoke; } + .button.is-primary { + background-color: #C93312; + border-color: transparent; + color: #fff; } + .button.is-primary:hover, .button.is-primary.is-hovered { + background-color: #bd3011; + border-color: transparent; + color: #fff; } + .button.is-primary:focus, .button.is-primary.is-focused { + border-color: transparent; + color: #fff; } + .button.is-primary:focus:not(:active), .button.is-primary.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(201, 51, 18, 0.25); } + .button.is-primary:active, .button.is-primary.is-active { + background-color: #b22d10; + border-color: transparent; + color: #fff; } + .button.is-primary[disabled] { + background-color: #C93312; + border-color: transparent; + box-shadow: none; } + .button.is-primary.is-inverted { + background-color: #fff; + color: #C93312; } + .button.is-primary.is-inverted:hover { + background-color: #f2f2f2; } + .button.is-primary.is-inverted[disabled] { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #C93312; } + .button.is-primary.is-loading:after { + border-color: transparent transparent #fff #fff !important; } + .button.is-primary.is-outlined { + background-color: transparent; + border-color: #C93312; + color: #C93312; } + .button.is-primary.is-outlined:hover, .button.is-primary.is-outlined:focus { + background-color: #C93312; + border-color: #C93312; + color: #fff; } + .button.is-primary.is-outlined.is-loading:after { + border-color: transparent transparent #C93312 #C93312 !important; } + .button.is-primary.is-outlined[disabled] { + background-color: transparent; + border-color: #C93312; + box-shadow: none; + color: #C93312; } + .button.is-primary.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; } + .button.is-primary.is-inverted.is-outlined:hover, .button.is-primary.is-inverted.is-outlined:focus { + background-color: #fff; + color: #C93312; } + .button.is-primary.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; } + .button.is-link { + background-color: #3273dc; + border-color: transparent; + color: #fff; } + .button.is-link:hover, .button.is-link.is-hovered { + background-color: #276cda; + border-color: transparent; + color: #fff; } + .button.is-link:focus, .button.is-link.is-focused { + border-color: transparent; + color: #fff; } + .button.is-link:focus:not(:active), .button.is-link.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } + .button.is-link:active, .button.is-link.is-active { + background-color: #2366d1; + border-color: transparent; + color: #fff; } + .button.is-link[disabled] { + background-color: #3273dc; + border-color: transparent; + box-shadow: none; } + .button.is-link.is-inverted { + background-color: #fff; + color: #3273dc; } + .button.is-link.is-inverted:hover { + background-color: #f2f2f2; } + .button.is-link.is-inverted[disabled] { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #3273dc; } + .button.is-link.is-loading:after { + border-color: transparent transparent #fff #fff !important; } + .button.is-link.is-outlined { + background-color: transparent; + border-color: #3273dc; + color: #3273dc; } + .button.is-link.is-outlined:hover, .button.is-link.is-outlined:focus { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; } + .button.is-link.is-outlined.is-loading:after { + border-color: transparent transparent #3273dc #3273dc !important; } + .button.is-link.is-outlined[disabled] { + background-color: transparent; + border-color: #3273dc; + box-shadow: none; + color: #3273dc; } + .button.is-link.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; } + .button.is-link.is-inverted.is-outlined:hover, .button.is-link.is-inverted.is-outlined:focus { + background-color: #fff; + color: #3273dc; } + .button.is-link.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; } + .button.is-info { + background-color: #209cee; + border-color: transparent; + color: #fff; } + .button.is-info:hover, .button.is-info.is-hovered { + background-color: #1496ed; + border-color: transparent; + color: #fff; } + .button.is-info:focus, .button.is-info.is-focused { + border-color: transparent; + color: #fff; } + .button.is-info:focus:not(:active), .button.is-info.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); } + .button.is-info:active, .button.is-info.is-active { + background-color: #118fe4; + border-color: transparent; + color: #fff; } + .button.is-info[disabled] { + background-color: #209cee; + border-color: transparent; + box-shadow: none; } + .button.is-info.is-inverted { + background-color: #fff; + color: #209cee; } + .button.is-info.is-inverted:hover { + background-color: #f2f2f2; } + .button.is-info.is-inverted[disabled] { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #209cee; } + .button.is-info.is-loading:after { + border-color: transparent transparent #fff #fff !important; } + .button.is-info.is-outlined { + background-color: transparent; + border-color: #209cee; + color: #209cee; } + .button.is-info.is-outlined:hover, .button.is-info.is-outlined:focus { + background-color: #209cee; + border-color: #209cee; + color: #fff; } + .button.is-info.is-outlined.is-loading:after { + border-color: transparent transparent #209cee #209cee !important; } + .button.is-info.is-outlined[disabled] { + background-color: transparent; + border-color: #209cee; + box-shadow: none; + color: #209cee; } + .button.is-info.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; } + .button.is-info.is-inverted.is-outlined:hover, .button.is-info.is-inverted.is-outlined:focus { + background-color: #fff; + color: #209cee; } + .button.is-info.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; } + .button.is-success { + background-color: #23d160; + border-color: transparent; + color: #fff; } + .button.is-success:hover, .button.is-success.is-hovered { + background-color: #22c65b; + border-color: transparent; + color: #fff; } + .button.is-success:focus, .button.is-success.is-focused { + border-color: transparent; + color: #fff; } + .button.is-success:focus:not(:active), .button.is-success.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); } + .button.is-success:active, .button.is-success.is-active { + background-color: #20bc56; + border-color: transparent; + color: #fff; } + .button.is-success[disabled] { + background-color: #23d160; + border-color: transparent; + box-shadow: none; } + .button.is-success.is-inverted { + background-color: #fff; + color: #23d160; } + .button.is-success.is-inverted:hover { + background-color: #f2f2f2; } + .button.is-success.is-inverted[disabled] { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #23d160; } + .button.is-success.is-loading:after { + border-color: transparent transparent #fff #fff !important; } + .button.is-success.is-outlined { + background-color: transparent; + border-color: #23d160; + color: #23d160; } + .button.is-success.is-outlined:hover, .button.is-success.is-outlined:focus { + background-color: #23d160; + border-color: #23d160; + color: #fff; } + .button.is-success.is-outlined.is-loading:after { + border-color: transparent transparent #23d160 #23d160 !important; } + .button.is-success.is-outlined[disabled] { + background-color: transparent; + border-color: #23d160; + box-shadow: none; + color: #23d160; } + .button.is-success.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; } + .button.is-success.is-inverted.is-outlined:hover, .button.is-success.is-inverted.is-outlined:focus { + background-color: #fff; + color: #23d160; } + .button.is-success.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; } + .button.is-warning { + background-color: #ffdd57; + border-color: transparent; + color: #FFFFFF; } + .button.is-warning:hover, .button.is-warning.is-hovered { + background-color: #ffdb4a; + border-color: transparent; + color: #FFFFFF; } + .button.is-warning:focus, .button.is-warning.is-focused { + border-color: transparent; + color: #FFFFFF; } + .button.is-warning:focus:not(:active), .button.is-warning.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); } + .button.is-warning:active, .button.is-warning.is-active { + background-color: #ffd83d; + border-color: transparent; + color: #FFFFFF; } + .button.is-warning[disabled] { + background-color: #ffdd57; + border-color: transparent; + box-shadow: none; } + .button.is-warning.is-inverted { + background-color: #FFFFFF; + color: #ffdd57; } + .button.is-warning.is-inverted:hover { + background-color: #f2f2f2; } + .button.is-warning.is-inverted[disabled] { + background-color: #FFFFFF; + border-color: transparent; + box-shadow: none; + color: #ffdd57; } + .button.is-warning.is-loading:after { + border-color: transparent transparent #FFFFFF #FFFFFF !important; } + .button.is-warning.is-outlined { + background-color: transparent; + border-color: #ffdd57; + color: #ffdd57; } + .button.is-warning.is-outlined:hover, .button.is-warning.is-outlined:focus { + background-color: #ffdd57; + border-color: #ffdd57; + color: #FFFFFF; } + .button.is-warning.is-outlined.is-loading:after { + border-color: transparent transparent #ffdd57 #ffdd57 !important; } + .button.is-warning.is-outlined[disabled] { + background-color: transparent; + border-color: #ffdd57; + box-shadow: none; + color: #ffdd57; } + .button.is-warning.is-inverted.is-outlined { + background-color: transparent; + border-color: #FFFFFF; + color: #FFFFFF; } + .button.is-warning.is-inverted.is-outlined:hover, .button.is-warning.is-inverted.is-outlined:focus { + background-color: #FFFFFF; + color: #ffdd57; } + .button.is-warning.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: #FFFFFF; + box-shadow: none; + color: #FFFFFF; } + .button.is-danger { + background-color: #ff3860; + border-color: transparent; + color: #fff; } + .button.is-danger:hover, .button.is-danger.is-hovered { + background-color: #ff2b56; + border-color: transparent; + color: #fff; } + .button.is-danger:focus, .button.is-danger.is-focused { + border-color: transparent; + color: #fff; } + .button.is-danger:focus:not(:active), .button.is-danger.is-focused:not(:active) { + box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); } + .button.is-danger:active, .button.is-danger.is-active { + background-color: #ff1f4b; + border-color: transparent; + color: #fff; } + .button.is-danger[disabled] { + background-color: #ff3860; + border-color: transparent; + box-shadow: none; } + .button.is-danger.is-inverted { + background-color: #fff; + color: #ff3860; } + .button.is-danger.is-inverted:hover { + background-color: #f2f2f2; } + .button.is-danger.is-inverted[disabled] { + background-color: #fff; + border-color: transparent; + box-shadow: none; + color: #ff3860; } + .button.is-danger.is-loading:after { + border-color: transparent transparent #fff #fff !important; } + .button.is-danger.is-outlined { + background-color: transparent; + border-color: #ff3860; + color: #ff3860; } + .button.is-danger.is-outlined:hover, .button.is-danger.is-outlined:focus { + background-color: #ff3860; + border-color: #ff3860; + color: #fff; } + .button.is-danger.is-outlined.is-loading:after { + border-color: transparent transparent #ff3860 #ff3860 !important; } + .button.is-danger.is-outlined[disabled] { + background-color: transparent; + border-color: #ff3860; + box-shadow: none; + color: #ff3860; } + .button.is-danger.is-inverted.is-outlined { + background-color: transparent; + border-color: #fff; + color: #fff; } + .button.is-danger.is-inverted.is-outlined:hover, .button.is-danger.is-inverted.is-outlined:focus { + background-color: #fff; + color: #ff3860; } + .button.is-danger.is-inverted.is-outlined[disabled] { + background-color: transparent; + border-color: #fff; + box-shadow: none; + color: #fff; } + .button.is-small { + border-radius: 2px; + font-size: 0.75rem; } + .button.is-medium { + font-size: 1.25rem; } + .button.is-large { + font-size: 1.5rem; } + .button[disabled] { + background-color: white; + border-color: #dbdbdb; + box-shadow: none; + opacity: 0.5; } + .button.is-fullwidth { + display: flex; + width: 100%; } + .button.is-loading { + color: transparent !important; + pointer-events: none; } + .button.is-loading:after { + animation: spinAround 500ms infinite linear; + border: 2px solid #dbdbdb; + border-radius: 290486px; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + height: 1em; + position: relative; + width: 1em; + position: absolute; + left: calc(50% - (1em / 2)); + top: calc(50% - (1em / 2)); + position: absolute !important; } + .button.is-static { + background-color: whitesmoke; + border-color: #dbdbdb; + color: #7a7a7a; + box-shadow: none; + pointer-events: none; } + +.buttons { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; } + .buttons .button { + margin-bottom: 0.5rem; } + .buttons .button:not(:last-child) { + margin-right: 0.5rem; } + .buttons:last-child { + margin-bottom: -0.5rem; } + .buttons:not(:last-child) { + margin-bottom: 1rem; } + .buttons.has-addons .button:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; } + .buttons.has-addons .button:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; + margin-right: -1px; } + .buttons.has-addons .button:last-child { + margin-right: 0; } + .buttons.has-addons .button:hover, .buttons.has-addons .button.is-hovered { + z-index: 2; } + .buttons.has-addons .button:focus, .buttons.has-addons .button.is-focused, .buttons.has-addons .button:active, .buttons.has-addons .button.is-active, .buttons.has-addons .button.is-selected { + z-index: 3; } + .buttons.has-addons .button:focus:hover, .buttons.has-addons .button.is-focused:hover, .buttons.has-addons .button:active:hover, .buttons.has-addons .button.is-active:hover, .buttons.has-addons .button.is-selected:hover { + z-index: 4; } + .buttons.is-centered { + justify-content: center; } + .buttons.is-right { + justify-content: flex-end; } + +.container { + margin: 0 auto; + position: relative; } + @media screen and (min-width: 1024px) { + .container { + max-width: 960px; + width: 960px; } + .container.is-fluid { + margin-left: 32px; + margin-right: 32px; + max-width: none; + width: auto; } } + @media screen and (max-width: 1215px) { + .container.is-widescreen { + max-width: 1152px; + width: auto; } } + @media screen and (max-width: 1407px) { + .container.is-fullhd { + max-width: 1344px; + width: auto; } } + @media screen and (min-width: 1216px) { + .container { + max-width: 1152px; + width: 1152px; } } + @media screen and (min-width: 1408px) { + .container { + max-width: 1344px; + width: 1344px; } } + +.content:not(:last-child) { + margin-bottom: 1.5rem; } + +.content li + li { + margin-top: 0.25em; } + +.content p:not(:last-child), +.content dl:not(:last-child), +.content ol:not(:last-child), +.content ul:not(:last-child), +.content blockquote:not(:last-child), +.content pre:not(:last-child), +.content table:not(:last-child) { + margin-bottom: 1em; } + +.content h1, +.content h2, +.content h3, +.content h4, +.content h5, +.content h6 { + color: #363636; + font-weight: 400; + line-height: 1.125; } + +.content h1 { + font-size: 2em; + margin-bottom: 0.5em; } + .content h1:not(:first-child) { + margin-top: 1em; } + +.content h2 { + font-size: 1.75em; + margin-bottom: 0.5714em; } + .content h2:not(:first-child) { + margin-top: 1.1428em; } + +.content h3 { + font-size: 1.5em; + margin-bottom: 0.6666em; } + .content h3:not(:first-child) { + margin-top: 1.3333em; } + +.content h4 { + font-size: 1.25em; + margin-bottom: 0.8em; } + +.content h5 { + font-size: 1.125em; + margin-bottom: 0.8888em; } + +.content h6 { + font-size: 1em; + margin-bottom: 1em; } + +.content blockquote { + background-color: whitesmoke; + border-left: 5px solid #dbdbdb; + padding: 1.25em 1.5em; } + +.content ol { + list-style: decimal outside; + margin-left: 2em; + margin-top: 1em; } + +.content ul { + list-style: disc outside; + margin-left: 2em; + margin-top: 1em; } + .content ul ul { + list-style-type: circle; + margin-top: 0.5em; } + .content ul ul ul { + list-style-type: square; } + +.content dd { + margin-left: 2em; } + +.content figure { + margin-left: 2em; + margin-right: 2em; + text-align: center; } + .content figure:not(:first-child) { + margin-top: 2em; } + .content figure:not(:last-child) { + margin-bottom: 2em; } + .content figure img { + display: inline-block; } + .content figure figcaption { + font-style: italic; } + +.content pre { + -webkit-overflow-scrolling: touch; + overflow-x: auto; + padding: 1.25em 1.5em; + white-space: pre; + word-wrap: normal; } + +.content sup, +.content sub { + font-size: 75%; } + +.content table { + width: 100%; } + .content table td, + .content table th { + border: 1px solid #dbdbdb; + border-width: 0 0 1px; + padding: 0.5em 0.75em; + vertical-align: top; } + .content table th { + color: #363636; + text-align: left; } + .content table tr:hover { + background-color: whitesmoke; } + .content table thead td, + .content table thead th { + border-width: 0 0 2px; + color: #363636; } + .content table tfoot td, + .content table tfoot th { + border-width: 2px 0 0; + color: #363636; } + .content table tbody tr:last-child td, + .content table tbody tr:last-child th { + border-bottom-width: 0; } + +.content.is-small { + font-size: 0.75rem; } + +.content.is-medium { + font-size: 1.25rem; } + +.content.is-large { + font-size: 1.5rem; } + +.input, +.textarea { + -moz-appearance: none; + -webkit-appearance: none; + align-items: center; + border: 1px solid transparent; + border-radius: 3px; + box-shadow: none; + display: inline-flex; + font-size: 1rem; + height: 2.25em; + justify-content: flex-start; + line-height: 1.5; + padding-bottom: calc(0.375em - 1px); + padding-left: calc(0.625em - 1px); + padding-right: calc(0.625em - 1px); + padding-top: calc(0.375em - 1px); + position: relative; + vertical-align: top; + background-color: white; + border-color: #dbdbdb; + color: #363636; + box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1); + max-width: 100%; + width: 100%; } + .input:focus, .input.is-focused, .input:active, .input.is-active, + .textarea:focus, + .textarea.is-focused, + .textarea:active, + .textarea.is-active { + outline: none; } + .input[disabled], + .textarea[disabled] { + cursor: not-allowed; } + .input::-moz-placeholder, + .textarea::-moz-placeholder { + color: rgba(54, 54, 54, 0.3); } + .input::-webkit-input-placeholder, + .textarea::-webkit-input-placeholder { + color: rgba(54, 54, 54, 0.3); } + .input:-moz-placeholder, + .textarea:-moz-placeholder { + color: rgba(54, 54, 54, 0.3); } + .input:-ms-input-placeholder, + .textarea:-ms-input-placeholder { + color: rgba(54, 54, 54, 0.3); } + .input:hover, .input.is-hovered, + .textarea:hover, + .textarea.is-hovered { + border-color: #b5b5b5; } + .input:focus, .input.is-focused, .input:active, .input.is-active, + .textarea:focus, + .textarea.is-focused, + .textarea:active, + .textarea.is-active { + border-color: #3273dc; + box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } + .input[disabled], + .textarea[disabled] { + background-color: whitesmoke; + border-color: whitesmoke; + box-shadow: none; + color: #7a7a7a; } + .input[disabled]::-moz-placeholder, + .textarea[disabled]::-moz-placeholder { + color: rgba(122, 122, 122, 0.3); } + .input[disabled]::-webkit-input-placeholder, + .textarea[disabled]::-webkit-input-placeholder { + color: rgba(122, 122, 122, 0.3); } + .input[disabled]:-moz-placeholder, + .textarea[disabled]:-moz-placeholder { + color: rgba(122, 122, 122, 0.3); } + .input[disabled]:-ms-input-placeholder, + .textarea[disabled]:-ms-input-placeholder { + color: rgba(122, 122, 122, 0.3); } + .input[type="search"], + .textarea[type="search"] { + border-radius: 290486px; } + .input[readonly], + .textarea[readonly] { + box-shadow: none; } + .input.is-white, + .textarea.is-white { + border-color: white; } + .input.is-white:focus, .input.is-white.is-focused, .input.is-white:active, .input.is-white.is-active, + .textarea.is-white:focus, + .textarea.is-white.is-focused, + .textarea.is-white:active, + .textarea.is-white.is-active { + box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); } + .input.is-black, + .textarea.is-black { + border-color: #0a0a0a; } + .input.is-black:focus, .input.is-black.is-focused, .input.is-black:active, .input.is-black.is-active, + .textarea.is-black:focus, + .textarea.is-black.is-focused, + .textarea.is-black:active, + .textarea.is-black.is-active { + box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); } + .input.is-light, + .textarea.is-light { + border-color: whitesmoke; } + .input.is-light:focus, .input.is-light.is-focused, .input.is-light:active, .input.is-light.is-active, + .textarea.is-light:focus, + .textarea.is-light.is-focused, + .textarea.is-light:active, + .textarea.is-light.is-active { + box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); } + .input.is-dark, + .textarea.is-dark { + border-color: #363636; } + .input.is-dark:focus, .input.is-dark.is-focused, .input.is-dark:active, .input.is-dark.is-active, + .textarea.is-dark:focus, + .textarea.is-dark.is-focused, + .textarea.is-dark:active, + .textarea.is-dark.is-active { + box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); } + .input.is-primary, + .textarea.is-primary { + border-color: #C93312; } + .input.is-primary:focus, .input.is-primary.is-focused, .input.is-primary:active, .input.is-primary.is-active, + .textarea.is-primary:focus, + .textarea.is-primary.is-focused, + .textarea.is-primary:active, + .textarea.is-primary.is-active { + box-shadow: 0 0 0 0.125em rgba(201, 51, 18, 0.25); } + .input.is-link, + .textarea.is-link { + border-color: #3273dc; } + .input.is-link:focus, .input.is-link.is-focused, .input.is-link:active, .input.is-link.is-active, + .textarea.is-link:focus, + .textarea.is-link.is-focused, + .textarea.is-link:active, + .textarea.is-link.is-active { + box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } + .input.is-info, + .textarea.is-info { + border-color: #209cee; } + .input.is-info:focus, .input.is-info.is-focused, .input.is-info:active, .input.is-info.is-active, + .textarea.is-info:focus, + .textarea.is-info.is-focused, + .textarea.is-info:active, + .textarea.is-info.is-active { + box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); } + .input.is-success, + .textarea.is-success { + border-color: #23d160; } + .input.is-success:focus, .input.is-success.is-focused, .input.is-success:active, .input.is-success.is-active, + .textarea.is-success:focus, + .textarea.is-success.is-focused, + .textarea.is-success:active, + .textarea.is-success.is-active { + box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); } + .input.is-warning, + .textarea.is-warning { + border-color: #ffdd57; } + .input.is-warning:focus, .input.is-warning.is-focused, .input.is-warning:active, .input.is-warning.is-active, + .textarea.is-warning:focus, + .textarea.is-warning.is-focused, + .textarea.is-warning:active, + .textarea.is-warning.is-active { + box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); } + .input.is-danger, + .textarea.is-danger { + border-color: #ff3860; } + .input.is-danger:focus, .input.is-danger.is-focused, .input.is-danger:active, .input.is-danger.is-active, + .textarea.is-danger:focus, + .textarea.is-danger.is-focused, + .textarea.is-danger:active, + .textarea.is-danger.is-active { + box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); } + .input.is-small, + .textarea.is-small { + border-radius: 2px; + font-size: 0.75rem; } + .input.is-medium, + .textarea.is-medium { + font-size: 1.25rem; } + .input.is-large, + .textarea.is-large { + font-size: 1.5rem; } + .input.is-fullwidth, + .textarea.is-fullwidth { + display: block; + width: 100%; } + .input.is-inline, + .textarea.is-inline { + display: inline; + width: auto; } + +.input.is-static { + background-color: transparent; + border-color: transparent; + box-shadow: none; + padding-left: 0; + padding-right: 0; } + +.textarea { + display: block; + max-width: 100%; + min-width: 100%; + padding: 0.625em; + resize: vertical; } + .textarea:not([rows]) { + max-height: 600px; + min-height: 120px; } + .textarea[rows] { + height: unset; } + .textarea.has-fixed-size { + resize: none; } + +.checkbox, +.radio { + cursor: pointer; + display: inline-block; + line-height: 1.25; + position: relative; } + .checkbox input, + .radio input { + cursor: pointer; } + .checkbox:hover, + .radio:hover { + color: #363636; } + .checkbox[disabled], + .radio[disabled] { + color: #7a7a7a; + cursor: not-allowed; } + +.radio + .radio { + margin-left: 0.5em; } + +.select { + display: inline-block; + max-width: 100%; + position: relative; + vertical-align: top; } + .select:not(.is-multiple) { + height: 2.25em; } + .select:not(.is-multiple)::after { + border: 1px solid #3273dc; + border-right: 0; + border-top: 0; + content: " "; + display: block; + height: 0.5em; + pointer-events: none; + position: absolute; + transform: rotate(-45deg); + transform-origin: center; + width: 0.5em; + margin-top: -0.375em; + right: 1.125em; + top: 50%; + z-index: 4; } + .select select { + -moz-appearance: none; + -webkit-appearance: none; + align-items: center; + border: 1px solid transparent; + border-radius: 3px; + box-shadow: none; + display: inline-flex; + font-size: 1rem; + height: 2.25em; + justify-content: flex-start; + line-height: 1.5; + padding-bottom: calc(0.375em - 1px); + padding-left: calc(0.625em - 1px); + padding-right: calc(0.625em - 1px); + padding-top: calc(0.375em - 1px); + position: relative; + vertical-align: top; + background-color: white; + border-color: #dbdbdb; + color: #363636; + cursor: pointer; + display: block; + font-size: 1em; + max-width: 100%; + outline: none; } + .select select:focus, .select select.is-focused, .select select:active, .select select.is-active { + outline: none; } + .select select[disabled] { + cursor: not-allowed; } + .select select::-moz-placeholder { + color: rgba(54, 54, 54, 0.3); } + .select select::-webkit-input-placeholder { + color: rgba(54, 54, 54, 0.3); } + .select select:-moz-placeholder { + color: rgba(54, 54, 54, 0.3); } + .select select:-ms-input-placeholder { + color: rgba(54, 54, 54, 0.3); } + .select select:hover, .select select.is-hovered { + border-color: #b5b5b5; } + .select select:focus, .select select.is-focused, .select select:active, .select select.is-active { + border-color: #3273dc; + box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } + .select select[disabled] { + background-color: whitesmoke; + border-color: whitesmoke; + box-shadow: none; + color: #7a7a7a; } + .select select[disabled]::-moz-placeholder { + color: rgba(122, 122, 122, 0.3); } + .select select[disabled]::-webkit-input-placeholder { + color: rgba(122, 122, 122, 0.3); } + .select select[disabled]:-moz-placeholder { + color: rgba(122, 122, 122, 0.3); } + .select select[disabled]:-ms-input-placeholder { + color: rgba(122, 122, 122, 0.3); } + .select select::-ms-expand { + display: none; } + .select select[disabled]:hover { + border-color: whitesmoke; } + .select select:not([multiple]) { + padding-right: 2.5em; } + .select select[multiple] { + height: unset; + padding: 0; } + .select select[multiple] option { + padding: 0.5em 1em; } + .select:hover::after { + border-color: #363636; } + .select.is-white select { + border-color: white; } + .select.is-white select:focus, .select.is-white select.is-focused, .select.is-white select:active, .select.is-white select.is-active { + box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); } + .select.is-black select { + border-color: #0a0a0a; } + .select.is-black select:focus, .select.is-black select.is-focused, .select.is-black select:active, .select.is-black select.is-active { + box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); } + .select.is-light select { + border-color: whitesmoke; } + .select.is-light select:focus, .select.is-light select.is-focused, .select.is-light select:active, .select.is-light select.is-active { + box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); } + .select.is-dark select { + border-color: #363636; } + .select.is-dark select:focus, .select.is-dark select.is-focused, .select.is-dark select:active, .select.is-dark select.is-active { + box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); } + .select.is-primary select { + border-color: #C93312; } + .select.is-primary select:focus, .select.is-primary select.is-focused, .select.is-primary select:active, .select.is-primary select.is-active { + box-shadow: 0 0 0 0.125em rgba(201, 51, 18, 0.25); } + .select.is-link select { + border-color: #3273dc; } + .select.is-link select:focus, .select.is-link select.is-focused, .select.is-link select:active, .select.is-link select.is-active { + box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } + .select.is-info select { + border-color: #209cee; } + .select.is-info select:focus, .select.is-info select.is-focused, .select.is-info select:active, .select.is-info select.is-active { + box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); } + .select.is-success select { + border-color: #23d160; } + .select.is-success select:focus, .select.is-success select.is-focused, .select.is-success select:active, .select.is-success select.is-active { + box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); } + .select.is-warning select { + border-color: #ffdd57; } + .select.is-warning select:focus, .select.is-warning select.is-focused, .select.is-warning select:active, .select.is-warning select.is-active { + box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); } + .select.is-danger select { + border-color: #ff3860; } + .select.is-danger select:focus, .select.is-danger select.is-focused, .select.is-danger select:active, .select.is-danger select.is-active { + box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); } + .select.is-small { + border-radius: 2px; + font-size: 0.75rem; } + .select.is-medium { + font-size: 1.25rem; } + .select.is-large { + font-size: 1.5rem; } + .select.is-disabled::after { + border-color: #7a7a7a; } + .select.is-fullwidth { + width: 100%; } + .select.is-fullwidth select { + width: 100%; } + .select.is-loading::after { + animation: spinAround 500ms infinite linear; + border: 2px solid #dbdbdb; + border-radius: 290486px; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + height: 1em; + position: relative; + width: 1em; + margin-top: 0; + position: absolute; + right: 0.625em; + top: 0.625em; + transform: none; } + .select.is-loading.is-small:after { + font-size: 0.75rem; } + .select.is-loading.is-medium:after { + font-size: 1.25rem; } + .select.is-loading.is-large:after { + font-size: 1.5rem; } + +.file { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + align-items: stretch; + display: flex; + justify-content: flex-start; + position: relative; } + .file.is-white .file-cta { + background-color: white; + border-color: transparent; + color: #0a0a0a; } + .file.is-white:hover .file-cta, .file.is-white.is-hovered .file-cta { + background-color: #f9f9f9; + border-color: transparent; + color: #0a0a0a; } + .file.is-white:focus .file-cta, .file.is-white.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255, 255, 255, 0.25); + color: #0a0a0a; } + .file.is-white:active .file-cta, .file.is-white.is-active .file-cta { + background-color: #f2f2f2; + border-color: transparent; + color: #0a0a0a; } + .file.is-black .file-cta { + background-color: #0a0a0a; + border-color: transparent; + color: white; } + .file.is-black:hover .file-cta, .file.is-black.is-hovered .file-cta { + background-color: #040404; + border-color: transparent; + color: white; } + .file.is-black:focus .file-cta, .file.is-black.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(10, 10, 10, 0.25); + color: white; } + .file.is-black:active .file-cta, .file.is-black.is-active .file-cta { + background-color: black; + border-color: transparent; + color: white; } + .file.is-light .file-cta { + background-color: whitesmoke; + border-color: transparent; + color: #363636; } + .file.is-light:hover .file-cta, .file.is-light.is-hovered .file-cta { + background-color: #eeeeee; + border-color: transparent; + color: #363636; } + .file.is-light:focus .file-cta, .file.is-light.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(245, 245, 245, 0.25); + color: #363636; } + .file.is-light:active .file-cta, .file.is-light.is-active .file-cta { + background-color: #e8e8e8; + border-color: transparent; + color: #363636; } + .file.is-dark .file-cta { + background-color: #363636; + border-color: transparent; + color: whitesmoke; } + .file.is-dark:hover .file-cta, .file.is-dark.is-hovered .file-cta { + background-color: #2f2f2f; + border-color: transparent; + color: whitesmoke; } + .file.is-dark:focus .file-cta, .file.is-dark.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(54, 54, 54, 0.25); + color: whitesmoke; } + .file.is-dark:active .file-cta, .file.is-dark.is-active .file-cta { + background-color: #292929; + border-color: transparent; + color: whitesmoke; } + .file.is-primary .file-cta { + background-color: #C93312; + border-color: transparent; + color: #fff; } + .file.is-primary:hover .file-cta, .file.is-primary.is-hovered .file-cta { + background-color: #bd3011; + border-color: transparent; + color: #fff; } + .file.is-primary:focus .file-cta, .file.is-primary.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(201, 51, 18, 0.25); + color: #fff; } + .file.is-primary:active .file-cta, .file.is-primary.is-active .file-cta { + background-color: #b22d10; + border-color: transparent; + color: #fff; } + .file.is-link .file-cta { + background-color: #3273dc; + border-color: transparent; + color: #fff; } + .file.is-link:hover .file-cta, .file.is-link.is-hovered .file-cta { + background-color: #276cda; + border-color: transparent; + color: #fff; } + .file.is-link:focus .file-cta, .file.is-link.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(50, 115, 220, 0.25); + color: #fff; } + .file.is-link:active .file-cta, .file.is-link.is-active .file-cta { + background-color: #2366d1; + border-color: transparent; + color: #fff; } + .file.is-info .file-cta { + background-color: #209cee; + border-color: transparent; + color: #fff; } + .file.is-info:hover .file-cta, .file.is-info.is-hovered .file-cta { + background-color: #1496ed; + border-color: transparent; + color: #fff; } + .file.is-info:focus .file-cta, .file.is-info.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(32, 156, 238, 0.25); + color: #fff; } + .file.is-info:active .file-cta, .file.is-info.is-active .file-cta { + background-color: #118fe4; + border-color: transparent; + color: #fff; } + .file.is-success .file-cta { + background-color: #23d160; + border-color: transparent; + color: #fff; } + .file.is-success:hover .file-cta, .file.is-success.is-hovered .file-cta { + background-color: #22c65b; + border-color: transparent; + color: #fff; } + .file.is-success:focus .file-cta, .file.is-success.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(35, 209, 96, 0.25); + color: #fff; } + .file.is-success:active .file-cta, .file.is-success.is-active .file-cta { + background-color: #20bc56; + border-color: transparent; + color: #fff; } + .file.is-warning .file-cta { + background-color: #ffdd57; + border-color: transparent; + color: #FFFFFF; } + .file.is-warning:hover .file-cta, .file.is-warning.is-hovered .file-cta { + background-color: #ffdb4a; + border-color: transparent; + color: #FFFFFF; } + .file.is-warning:focus .file-cta, .file.is-warning.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255, 221, 87, 0.25); + color: #FFFFFF; } + .file.is-warning:active .file-cta, .file.is-warning.is-active .file-cta { + background-color: #ffd83d; + border-color: transparent; + color: #FFFFFF; } + .file.is-danger .file-cta { + background-color: #ff3860; + border-color: transparent; + color: #fff; } + .file.is-danger:hover .file-cta, .file.is-danger.is-hovered .file-cta { + background-color: #ff2b56; + border-color: transparent; + color: #fff; } + .file.is-danger:focus .file-cta, .file.is-danger.is-focused .file-cta { + border-color: transparent; + box-shadow: 0 0 0.5em rgba(255, 56, 96, 0.25); + color: #fff; } + .file.is-danger:active .file-cta, .file.is-danger.is-active .file-cta { + background-color: #ff1f4b; + border-color: transparent; + color: #fff; } + .file.is-small { + font-size: 0.75rem; } + .file.is-medium { + font-size: 1.25rem; } + .file.is-medium .file-icon .fa { + font-size: 21px; } + .file.is-large { + font-size: 1.5rem; } + .file.is-large .file-icon .fa { + font-size: 28px; } + .file.has-name .file-cta { + border-bottom-right-radius: 0; + border-top-right-radius: 0; } + .file.has-name .file-name { + border-bottom-left-radius: 0; + border-top-left-radius: 0; } + .file.has-name.is-empty .file-cta { + border-radius: 3px; } + .file.has-name.is-empty .file-name { + display: none; } + .file.is-centered { + justify-content: center; } + .file.is-right { + justify-content: flex-end; } + .file.is-boxed .file-label { + flex-direction: column; } + .file.is-boxed .file-cta { + flex-direction: column; + height: auto; + padding: 1em 3em; } + .file.is-boxed .file-name { + border-width: 0 1px 1px; } + .file.is-boxed .file-icon { + height: 1.5em; + width: 1.5em; } + .file.is-boxed .file-icon .fa { + font-size: 21px; } + .file.is-boxed.is-small .file-icon .fa { + font-size: 14px; } + .file.is-boxed.is-medium .file-icon .fa { + font-size: 28px; } + .file.is-boxed.is-large .file-icon .fa { + font-size: 35px; } + .file.is-boxed.has-name .file-cta { + border-radius: 3px 3px 0 0; } + .file.is-boxed.has-name .file-name { + border-radius: 0 0 3px 3px; + border-width: 0 1px 1px; } + .file.is-right .file-cta { + border-radius: 0 3px 3px 0; } + .file.is-right .file-name { + border-radius: 3px 0 0 3px; + border-width: 1px 0 1px 1px; + order: -1; } + .file.is-fullwidth .file-label { + width: 100%; } + .file.is-fullwidth .file-name { + flex-grow: 1; + max-width: none; } + +.file-label { + align-items: stretch; + display: flex; + cursor: pointer; + justify-content: flex-start; + overflow: hidden; + position: relative; } + .file-label:hover .file-cta { + background-color: #eeeeee; + color: #363636; } + .file-label:hover .file-name { + border-color: #d5d5d5; } + .file-label:active .file-cta { + background-color: #e8e8e8; + color: #363636; } + .file-label:active .file-name { + border-color: #cfcfcf; } + +.file-input { + height: 0.01em; + left: 0; + outline: none; + position: absolute; + top: 0; + width: 0.01em; } + +.file-cta, +.file-name { + -moz-appearance: none; + -webkit-appearance: none; + align-items: center; + border: 1px solid transparent; + border-radius: 3px; + box-shadow: none; + display: inline-flex; + font-size: 1rem; + height: 2.25em; + justify-content: flex-start; + line-height: 1.5; + padding-bottom: calc(0.375em - 1px); + padding-left: calc(0.625em - 1px); + padding-right: calc(0.625em - 1px); + padding-top: calc(0.375em - 1px); + position: relative; + vertical-align: top; + border-color: #dbdbdb; + border-radius: 3px; + font-size: 1em; + padding-left: 1em; + padding-right: 1em; + white-space: nowrap; } + .file-cta:focus, .file-cta.is-focused, .file-cta:active, .file-cta.is-active, + .file-name:focus, + .file-name.is-focused, + .file-name:active, + .file-name.is-active { + outline: none; } + .file-cta[disabled], + .file-name[disabled] { + cursor: not-allowed; } + +.file-cta { + background-color: whitesmoke; + color: #4a4a4a; } + +.file-name { + border-color: #dbdbdb; + border-style: solid; + border-width: 1px 1px 1px 0; + display: block; + max-width: 16em; + overflow: hidden; + text-align: left; + text-overflow: ellipsis; } + +.file-icon { + align-items: center; + display: flex; + height: 1em; + justify-content: center; + margin-right: 0.5em; + width: 1em; } + .file-icon .fa { + font-size: 14px; } + +.label { + color: #363636; + display: block; + font-size: 1rem; + font-weight: 700; } + .label:not(:last-child) { + margin-bottom: 0.5em; } + .label.is-small { + font-size: 0.75rem; } + .label.is-medium { + font-size: 1.25rem; } + .label.is-large { + font-size: 1.5rem; } + +.help { + display: block; + font-size: 0.75rem; + margin-top: 0.25rem; } + .help.is-white { + color: white; } + .help.is-black { + color: #0a0a0a; } + .help.is-light { + color: whitesmoke; } + .help.is-dark { + color: #363636; } + .help.is-primary { + color: #C93312; } + .help.is-link { + color: #3273dc; } + .help.is-info { + color: #209cee; } + .help.is-success { + color: #23d160; } + .help.is-warning { + color: #ffdd57; } + .help.is-danger { + color: #ff3860; } + +.field:not(:last-child) { + margin-bottom: 0.75rem; } + +.field.has-addons { + display: flex; + justify-content: flex-start; } + .field.has-addons .control:not(:last-child) { + margin-right: -1px; } + .field.has-addons .control:first-child .button, + .field.has-addons .control:first-child .input, + .field.has-addons .control:first-child .select select { + border-bottom-left-radius: 3px; + border-top-left-radius: 3px; } + .field.has-addons .control:last-child .button, + .field.has-addons .control:last-child .input, + .field.has-addons .control:last-child .select select { + border-bottom-right-radius: 3px; + border-top-right-radius: 3px; } + .field.has-addons .control .button, + .field.has-addons .control .input, + .field.has-addons .control .select select { + border-radius: 0; } + .field.has-addons .control .button:hover, .field.has-addons .control .button.is-hovered, + .field.has-addons .control .input:hover, + .field.has-addons .control .input.is-hovered, + .field.has-addons .control .select select:hover, + .field.has-addons .control .select select.is-hovered { + z-index: 2; } + .field.has-addons .control .button:focus, .field.has-addons .control .button.is-focused, .field.has-addons .control .button:active, .field.has-addons .control .button.is-active, + .field.has-addons .control .input:focus, + .field.has-addons .control .input.is-focused, + .field.has-addons .control .input:active, + .field.has-addons .control .input.is-active, + .field.has-addons .control .select select:focus, + .field.has-addons .control .select select.is-focused, + .field.has-addons .control .select select:active, + .field.has-addons .control .select select.is-active { + z-index: 3; } + .field.has-addons .control .button:focus:hover, .field.has-addons .control .button.is-focused:hover, .field.has-addons .control .button:active:hover, .field.has-addons .control .button.is-active:hover, + .field.has-addons .control .input:focus:hover, + .field.has-addons .control .input.is-focused:hover, + .field.has-addons .control .input:active:hover, + .field.has-addons .control .input.is-active:hover, + .field.has-addons .control .select select:focus:hover, + .field.has-addons .control .select select.is-focused:hover, + .field.has-addons .control .select select:active:hover, + .field.has-addons .control .select select.is-active:hover { + z-index: 4; } + .field.has-addons .control.is-expanded { + flex-grow: 1; } + .field.has-addons.has-addons-centered { + justify-content: center; } + .field.has-addons.has-addons-right { + justify-content: flex-end; } + .field.has-addons.has-addons-fullwidth .control { + flex-grow: 1; + flex-shrink: 0; } + +.field.is-grouped { + display: flex; + justify-content: flex-start; } + .field.is-grouped > .control { + flex-shrink: 0; } + .field.is-grouped > .control:not(:last-child) { + margin-bottom: 0; + margin-right: 0.75rem; } + .field.is-grouped > .control.is-expanded { + flex-grow: 1; + flex-shrink: 1; } + .field.is-grouped.is-grouped-centered { + justify-content: center; } + .field.is-grouped.is-grouped-right { + justify-content: flex-end; } + .field.is-grouped.is-grouped-multiline { + flex-wrap: wrap; } + .field.is-grouped.is-grouped-multiline > .control:last-child, .field.is-grouped.is-grouped-multiline > .control:not(:last-child) { + margin-bottom: 0.75rem; } + .field.is-grouped.is-grouped-multiline:last-child { + margin-bottom: -0.75rem; } + .field.is-grouped.is-grouped-multiline:not(:last-child) { + margin-bottom: 0; } + +@media screen and (min-width: 769px), print { + .field.is-horizontal { + display: flex; } } + +.field-label .label { + font-size: inherit; } + +@media screen and (max-width: 768px) { + .field-label { + margin-bottom: 0.5rem; } } + +@media screen and (min-width: 769px), print { + .field-label { + flex-basis: 0; + flex-grow: 1; + flex-shrink: 0; + margin-right: 1.5rem; + text-align: right; } + .field-label.is-small { + font-size: 0.75rem; + padding-top: 0.375em; } + .field-label.is-normal { + padding-top: 0.375em; } + .field-label.is-medium { + font-size: 1.25rem; + padding-top: 0.375em; } + .field-label.is-large { + font-size: 1.5rem; + padding-top: 0.375em; } } + +.field-body .field .field { + margin-bottom: 0; } + +@media screen and (min-width: 769px), print { + .field-body { + display: flex; + flex-basis: 0; + flex-grow: 5; + flex-shrink: 1; } + .field-body .field { + margin-bottom: 0; } + .field-body > .field { + flex-shrink: 1; } + .field-body > .field:not(.is-narrow) { + flex-grow: 1; } + .field-body > .field:not(:last-child) { + margin-right: 0.75rem; } } + +.control { + font-size: 1rem; + position: relative; + text-align: left; } + .control.has-icon .icon { + color: #dbdbdb; + height: 2.25em; + pointer-events: none; + position: absolute; + top: 0; + width: 2.25em; + z-index: 4; } + .control.has-icon .input:focus + .icon { + color: #7a7a7a; } + .control.has-icon .input.is-small + .icon { + font-size: 0.75rem; } + .control.has-icon .input.is-medium + .icon { + font-size: 1.25rem; } + .control.has-icon .input.is-large + .icon { + font-size: 1.5rem; } + .control.has-icon:not(.has-icon-right) .icon { + left: 0; } + .control.has-icon:not(.has-icon-right) .input { + padding-left: 2.25em; } + .control.has-icon.has-icon-right .icon { + right: 0; } + .control.has-icon.has-icon-right .input { + padding-right: 2.25em; } + .control.has-icons-left .input:focus ~ .icon, + .control.has-icons-left .select:focus ~ .icon, .control.has-icons-right .input:focus ~ .icon, + .control.has-icons-right .select:focus ~ .icon { + color: #7a7a7a; } + .control.has-icons-left .input.is-small ~ .icon, + .control.has-icons-left .select.is-small ~ .icon, .control.has-icons-right .input.is-small ~ .icon, + .control.has-icons-right .select.is-small ~ .icon { + font-size: 0.75rem; } + .control.has-icons-left .input.is-medium ~ .icon, + .control.has-icons-left .select.is-medium ~ .icon, .control.has-icons-right .input.is-medium ~ .icon, + .control.has-icons-right .select.is-medium ~ .icon { + font-size: 1.25rem; } + .control.has-icons-left .input.is-large ~ .icon, + .control.has-icons-left .select.is-large ~ .icon, .control.has-icons-right .input.is-large ~ .icon, + .control.has-icons-right .select.is-large ~ .icon { + font-size: 1.5rem; } + .control.has-icons-left .icon, .control.has-icons-right .icon { + color: #dbdbdb; + height: 2.25em; + pointer-events: none; + position: absolute; + top: 0; + width: 2.25em; + z-index: 4; } + .control.has-icons-left .input, + .control.has-icons-left .select select { + padding-left: 2.25em; } + .control.has-icons-left .icon.is-left { + left: 0; } + .control.has-icons-right .input, + .control.has-icons-right .select select { + padding-right: 2.25em; } + .control.has-icons-right .icon.is-right { + right: 0; } + .control.is-loading::after { + animation: spinAround 500ms infinite linear; + border: 2px solid #dbdbdb; + border-radius: 290486px; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + height: 1em; + position: relative; + width: 1em; + position: absolute !important; + right: 0.625em; + top: 0.625em; } + .control.is-loading.is-small:after { + font-size: 0.75rem; } + .control.is-loading.is-medium:after { + font-size: 1.25rem; } + .control.is-loading.is-large:after { + font-size: 1.5rem; } + +.icon { + align-items: center; + display: inline-flex; + justify-content: center; + height: 1.5rem; + width: 1.5rem; } + .icon.is-small { + height: 1rem; + width: 1rem; } + .icon.is-medium { + height: 2rem; + width: 2rem; } + .icon.is-large { + height: 3rem; + width: 3rem; } + +.image { + display: block; + position: relative; } + .image img { + display: block; + height: auto; + width: 100%; } + .image.is-square img, .image.is-1by1 img, .image.is-4by3 img, .image.is-3by2 img, .image.is-16by9 img, .image.is-2by1 img { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + height: 100%; + width: 100%; } + .image.is-square, .image.is-1by1 { + padding-top: 100%; } + .image.is-4by3 { + padding-top: 75%; } + .image.is-3by2 { + padding-top: 66.6666%; } + .image.is-16by9 { + padding-top: 56.25%; } + .image.is-2by1 { + padding-top: 50%; } + .image.is-16x16 { + height: 16px; + width: 16px; } + .image.is-24x24 { + height: 24px; + width: 24px; } + .image.is-32x32 { + height: 32px; + width: 32px; } + .image.is-48x48 { + height: 48px; + width: 48px; } + .image.is-64x64 { + height: 64px; + width: 64px; } + .image.is-96x96 { + height: 96px; + width: 96px; } + .image.is-128x128 { + height: 128px; + width: 128px; } + +.notification { + background-color: whitesmoke; + border-radius: 3px; + padding: 1.25rem 2.5rem 1.25rem 1.5rem; + position: relative; } + .notification:not(:last-child) { + margin-bottom: 1.5rem; } + .notification a:not(.button) { + color: currentColor; + text-decoration: underline; } + .notification strong { + color: currentColor; } + .notification code, + .notification pre { + background: white; } + .notification pre code { + background: transparent; } + .notification > .delete { + position: absolute; + right: 0.5em; + top: 0.5em; } + .notification .title, + .notification .subtitle, + .notification .content { + color: currentColor; } + .notification.is-white { + background-color: white; + color: #0a0a0a; } + .notification.is-black { + background-color: #0a0a0a; + color: white; } + .notification.is-light { + background-color: whitesmoke; + color: #363636; } + .notification.is-dark { + background-color: #363636; + color: whitesmoke; } + .notification.is-primary { + background-color: #C93312; + color: #fff; } + .notification.is-link { + background-color: #3273dc; + color: #fff; } + .notification.is-info { + background-color: #209cee; + color: #fff; } + .notification.is-success { + background-color: #23d160; + color: #fff; } + .notification.is-warning { + background-color: #ffdd57; + color: #FFFFFF; } + .notification.is-danger { + background-color: #ff3860; + color: #fff; } + +.progress { + -moz-appearance: none; + -webkit-appearance: none; + border: none; + border-radius: 290486px; + display: block; + height: 1rem; + overflow: hidden; + padding: 0; + width: 100%; } + .progress:not(:last-child) { + margin-bottom: 1.5rem; } + .progress::-webkit-progress-bar { + background-color: #dbdbdb; } + .progress::-webkit-progress-value { + background-color: #4a4a4a; } + .progress::-moz-progress-bar { + background-color: #4a4a4a; } + .progress::-ms-fill { + background-color: #4a4a4a; + border: none; } + .progress.is-white::-webkit-progress-value { + background-color: white; } + .progress.is-white::-moz-progress-bar { + background-color: white; } + .progress.is-white::-ms-fill { + background-color: white; } + .progress.is-black::-webkit-progress-value { + background-color: #0a0a0a; } + .progress.is-black::-moz-progress-bar { + background-color: #0a0a0a; } + .progress.is-black::-ms-fill { + background-color: #0a0a0a; } + .progress.is-light::-webkit-progress-value { + background-color: whitesmoke; } + .progress.is-light::-moz-progress-bar { + background-color: whitesmoke; } + .progress.is-light::-ms-fill { + background-color: whitesmoke; } + .progress.is-dark::-webkit-progress-value { + background-color: #363636; } + .progress.is-dark::-moz-progress-bar { + background-color: #363636; } + .progress.is-dark::-ms-fill { + background-color: #363636; } + .progress.is-primary::-webkit-progress-value { + background-color: #C93312; } + .progress.is-primary::-moz-progress-bar { + background-color: #C93312; } + .progress.is-primary::-ms-fill { + background-color: #C93312; } + .progress.is-link::-webkit-progress-value { + background-color: #3273dc; } + .progress.is-link::-moz-progress-bar { + background-color: #3273dc; } + .progress.is-link::-ms-fill { + background-color: #3273dc; } + .progress.is-info::-webkit-progress-value { + background-color: #209cee; } + .progress.is-info::-moz-progress-bar { + background-color: #209cee; } + .progress.is-info::-ms-fill { + background-color: #209cee; } + .progress.is-success::-webkit-progress-value { + background-color: #23d160; } + .progress.is-success::-moz-progress-bar { + background-color: #23d160; } + .progress.is-success::-ms-fill { + background-color: #23d160; } + .progress.is-warning::-webkit-progress-value { + background-color: #ffdd57; } + .progress.is-warning::-moz-progress-bar { + background-color: #ffdd57; } + .progress.is-warning::-ms-fill { + background-color: #ffdd57; } + .progress.is-danger::-webkit-progress-value { + background-color: #ff3860; } + .progress.is-danger::-moz-progress-bar { + background-color: #ff3860; } + .progress.is-danger::-ms-fill { + background-color: #ff3860; } + .progress.is-small { + height: 0.75rem; } + .progress.is-medium { + height: 1.25rem; } + .progress.is-large { + height: 1.5rem; } + +.table { + background-color: white; + color: #363636; + margin-bottom: 1.5rem; } + .table td, + .table th { + border: 1px solid #dbdbdb; + border-width: 0 0 1px; + padding: 0.5em 0.75em; + vertical-align: top; } + .table td.is-white, + .table th.is-white { + background-color: white; + border-color: white; + color: #0a0a0a; } + .table td.is-black, + .table th.is-black { + background-color: #0a0a0a; + border-color: #0a0a0a; + color: white; } + .table td.is-light, + .table th.is-light { + background-color: whitesmoke; + border-color: whitesmoke; + color: #363636; } + .table td.is-dark, + .table th.is-dark { + background-color: #363636; + border-color: #363636; + color: whitesmoke; } + .table td.is-primary, + .table th.is-primary { + background-color: #C93312; + border-color: #C93312; + color: #fff; } + .table td.is-link, + .table th.is-link { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; } + .table td.is-info, + .table th.is-info { + background-color: #209cee; + border-color: #209cee; + color: #fff; } + .table td.is-success, + .table th.is-success { + background-color: #23d160; + border-color: #23d160; + color: #fff; } + .table td.is-warning, + .table th.is-warning { + background-color: #ffdd57; + border-color: #ffdd57; + color: #FFFFFF; } + .table td.is-danger, + .table th.is-danger { + background-color: #ff3860; + border-color: #ff3860; + color: #fff; } + .table td.is-narrow, + .table th.is-narrow { + white-space: nowrap; + width: 1%; } + .table td.is-selected, + .table th.is-selected { + background-color: #C93312; + color: #fff; } + .table td.is-selected a, + .table td.is-selected strong, + .table th.is-selected a, + .table th.is-selected strong { + color: currentColor; } + .table th { + color: #363636; + text-align: left; } + .table tr.is-selected { + background-color: #C93312; + color: #fff; } + .table tr.is-selected a, + .table tr.is-selected strong { + color: currentColor; } + .table tr.is-selected td, + .table tr.is-selected th { + border-color: #fff; + color: currentColor; } + .table thead td, + .table thead th { + border-width: 0 0 2px; + color: #363636; } + .table tfoot td, + .table tfoot th { + border-width: 2px 0 0; + color: #363636; } + .table tbody tr:last-child td, + .table tbody tr:last-child th { + border-bottom-width: 0; } + .table.is-bordered td, + .table.is-bordered th { + border-width: 1px; } + .table.is-bordered tr:last-child td, + .table.is-bordered tr:last-child th { + border-bottom-width: 1px; } + .table.is-fullwidth { + width: 100%; } + .table.is-hoverable tbody tr:not(.is-selected):hover { + background-color: #fafafa; } + .table.is-hoverable.is-striped tbody tr:not(.is-selected):hover { + background-color: whitesmoke; } + .table.is-narrow td, + .table.is-narrow th { + padding: 0.25em 0.5em; } + .table.is-striped tbody tr:not(.is-selected):nth-child(even) { + background-color: #fafafa; } + +.tags { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; } + .tags .tag { + margin-bottom: 0.5rem; } + .tags .tag:not(:last-child) { + margin-right: 0.5rem; } + .tags:last-child { + margin-bottom: -0.5rem; } + .tags:not(:last-child) { + margin-bottom: 1rem; } + .tags.has-addons .tag { + margin-right: 0; } + .tags.has-addons .tag:not(:first-child) { + border-bottom-left-radius: 0; + border-top-left-radius: 0; } + .tags.has-addons .tag:not(:last-child) { + border-bottom-right-radius: 0; + border-top-right-radius: 0; } + .tags.is-centered { + justify-content: center; } + .tags.is-centered .tag { + margin-right: 0.25rem; + margin-left: 0.25rem; } + .tags.is-right { + justify-content: flex-end; } + .tags.is-right .tag:not(:first-child) { + margin-left: 0.5rem; } + .tags.is-right .tag:not(:last-child) { + margin-right: 0; } + +.tag:not(body) { + align-items: center; + background-color: whitesmoke; + border-radius: 3px; + color: #4a4a4a; + display: inline-flex; + font-size: 0.75rem; + height: 2em; + justify-content: center; + line-height: 1.5; + padding-left: 0.75em; + padding-right: 0.75em; + white-space: nowrap; } + .tag:not(body) .delete { + margin-left: 0.25em; + margin-right: -0.375em; } + .tag:not(body).is-white { + background-color: white; + color: #0a0a0a; } + .tag:not(body).is-black { + background-color: #0a0a0a; + color: white; } + .tag:not(body).is-light { + background-color: whitesmoke; + color: #363636; } + .tag:not(body).is-dark { + background-color: #363636; + color: whitesmoke; } + .tag:not(body).is-primary { + background-color: #C93312; + color: #fff; } + .tag:not(body).is-link { + background-color: #3273dc; + color: #fff; } + .tag:not(body).is-info { + background-color: #209cee; + color: #fff; } + .tag:not(body).is-success { + background-color: #23d160; + color: #fff; } + .tag:not(body).is-warning { + background-color: #ffdd57; + color: #FFFFFF; } + .tag:not(body).is-danger { + background-color: #ff3860; + color: #fff; } + .tag:not(body).is-medium { + font-size: 1rem; } + .tag:not(body).is-large { + font-size: 1.25rem; } + .tag:not(body) .icon:first-child:not(:last-child) { + margin-left: -0.375em; + margin-right: 0.1875em; } + .tag:not(body) .icon:last-child:not(:first-child) { + margin-left: 0.1875em; + margin-right: -0.375em; } + .tag:not(body) .icon:first-child:last-child { + margin-left: -0.375em; + margin-right: -0.375em; } + .tag:not(body).is-delete { + margin-left: 1px; + padding: 0; + position: relative; + width: 2em; } + .tag:not(body).is-delete:before, .tag:not(body).is-delete:after { + background-color: currentColor; + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; } + .tag:not(body).is-delete:before { + height: 1px; + width: 50%; } + .tag:not(body).is-delete:after { + height: 50%; + width: 1px; } + .tag:not(body).is-delete:hover, .tag:not(body).is-delete:focus { + background-color: #e8e8e8; } + .tag:not(body).is-delete:active { + background-color: #dbdbdb; } + .tag:not(body).is-rounded { + border-radius: 290486px; } + +a.tag:hover { + text-decoration: underline; } + +.title, +.subtitle { + word-break: break-word; } + .title:not(:last-child), + .subtitle:not(:last-child) { + margin-bottom: 1.5rem; } + .title em, + .title span, + .subtitle em, + .subtitle span { + font-weight: inherit; } + .title .tag, + .subtitle .tag { + vertical-align: middle; } + +.title { + color: #363636; + font-size: 2rem; + font-weight: 600; + line-height: 1.125; } + .title strong { + color: inherit; + font-weight: inherit; } + .title + .highlight { + margin-top: -0.75rem; } + .title:not(.is-spaced) + .subtitle { + margin-top: -1.5rem; } + .title.is-1 { + font-size: 3rem; } + .title.is-2 { + font-size: 2.5rem; } + .title.is-3 { + font-size: 2rem; } + .title.is-4 { + font-size: 1.5rem; } + .title.is-5 { + font-size: 1.25rem; } + .title.is-6 { + font-size: 1rem; } + .title.is-7 { + font-size: 0.75rem; } + +.subtitle { + color: #4a4a4a; + font-size: 1.25rem; + font-weight: 400; + line-height: 1.25; } + .subtitle strong { + color: #363636; + font-weight: 600; } + .subtitle:not(.is-spaced) + .title { + margin-top: -1.5rem; } + .subtitle.is-1 { + font-size: 3rem; } + .subtitle.is-2 { + font-size: 2.5rem; } + .subtitle.is-3 { + font-size: 2rem; } + .subtitle.is-4 { + font-size: 1.5rem; } + .subtitle.is-5 { + font-size: 1.25rem; } + .subtitle.is-6 { + font-size: 1rem; } + .subtitle.is-7 { + font-size: 0.75rem; } + +.block:not(:last-child) { + margin-bottom: 1.5rem; } + +.delete { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -moz-appearance: none; + -webkit-appearance: none; + background-color: rgba(10, 10, 10, 0.2); + border: none; + border-radius: 290486px; + cursor: pointer; + display: inline-block; + flex-grow: 0; + flex-shrink: 0; + font-size: 0; + height: 20px; + max-height: 20px; + max-width: 20px; + min-height: 20px; + min-width: 20px; + outline: none; + position: relative; + vertical-align: top; + width: 20px; } + .delete:before, .delete:after { + background-color: white; + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; } + .delete:before { + height: 2px; + width: 50%; } + .delete:after { + height: 50%; + width: 2px; } + .delete:hover, .delete:focus { + background-color: rgba(10, 10, 10, 0.3); } + .delete:active { + background-color: rgba(10, 10, 10, 0.4); } + .delete.is-small { + height: 16px; + max-height: 16px; + max-width: 16px; + min-height: 16px; + min-width: 16px; + width: 16px; } + .delete.is-medium { + height: 24px; + max-height: 24px; + max-width: 24px; + min-height: 24px; + min-width: 24px; + width: 24px; } + .delete.is-large { + height: 32px; + max-height: 32px; + max-width: 32px; + min-height: 32px; + min-width: 32px; + width: 32px; } + +.heading { + display: block; + font-size: 11px; + letter-spacing: 1px; + margin-bottom: 5px; + text-transform: uppercase; } + +.highlight { + font-weight: 400; + max-width: 100%; + overflow: hidden; + padding: 0; } + .highlight:not(:last-child) { + margin-bottom: 1.5rem; } + .highlight pre { + overflow: auto; + max-width: 100%; } + +.loader { + animation: spinAround 500ms infinite linear; + border: 2px solid #dbdbdb; + border-radius: 290486px; + border-right-color: transparent; + border-top-color: transparent; + content: ""; + display: block; + height: 1em; + position: relative; + width: 1em; } + +.number { + align-items: center; + background-color: whitesmoke; + border-radius: 290486px; + display: inline-flex; + font-size: 1.25rem; + height: 2em; + justify-content: center; + margin-right: 1.5rem; + min-width: 2.5em; + padding: 0.25rem 0.5rem; + text-align: center; + vertical-align: top; } + +.breadcrumb { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + align-items: stretch; + display: flex; + font-size: 1rem; + overflow: hidden; + overflow-x: auto; + white-space: nowrap; } + .breadcrumb:not(:last-child) { + margin-bottom: 1.5rem; } + .breadcrumb a { + align-items: center; + color: #3273dc; + display: flex; + justify-content: center; + padding: 0.5em 0.75em; } + .breadcrumb a:hover { + color: #363636; } + .breadcrumb li { + align-items: center; + display: flex; } + .breadcrumb li:first-child a { + padding-left: 0; } + .breadcrumb li.is-active a { + color: #363636; + cursor: default; + pointer-events: none; } + .breadcrumb li + li::before { + color: #4a4a4a; + content: "\0002f"; } + .breadcrumb ul, .breadcrumb ol { + align-items: center; + display: flex; + flex-grow: 1; + flex-shrink: 0; + justify-content: flex-start; } + .breadcrumb .icon:first-child { + margin-right: 0.5em; } + .breadcrumb .icon:last-child { + margin-left: 0.5em; } + .breadcrumb.is-centered ol, .breadcrumb.is-centered ul { + justify-content: center; } + .breadcrumb.is-right ol, .breadcrumb.is-right ul { + justify-content: flex-end; } + .breadcrumb.is-small { + font-size: 0.75rem; } + .breadcrumb.is-medium { + font-size: 1.25rem; } + .breadcrumb.is-large { + font-size: 1.5rem; } + .breadcrumb.has-arrow-separator li + li::before { + content: "\02192"; } + .breadcrumb.has-bullet-separator li + li::before { + content: "\02022"; } + .breadcrumb.has-dot-separator li + li::before { + content: "\000b7"; } + .breadcrumb.has-succeeds-separator li + li::before { + content: "\0227B"; } + +.card { + background-color: white; + box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); + color: #4a4a4a; + max-width: 100%; + position: relative; } + +.card-header { + align-items: stretch; + box-shadow: 0 1px 2px rgba(10, 10, 10, 0.1); + display: flex; } + +.card-header-title { + align-items: center; + color: #363636; + display: flex; + flex-grow: 1; + font-weight: 700; + padding: 0.75rem; } + .card-header-title.is-centered { + justify-content: center; } + +.card-header-icon { + align-items: center; + cursor: pointer; + display: flex; + justify-content: center; + padding: 0.75rem; } + +.card-image { + display: block; + position: relative; } + +.card-content { + padding: 1.5rem; } + +.card-footer { + border-top: 1px solid #dbdbdb; + align-items: stretch; + display: flex; } + +.card-footer-item { + align-items: center; + display: flex; + flex-basis: 0; + flex-grow: 1; + flex-shrink: 0; + justify-content: center; + padding: 0.75rem; } + .card-footer-item:not(:last-child) { + border-right: 1px solid #dbdbdb; } + +.card .media:not(:last-child) { + margin-bottom: 0.75rem; } + +.dropdown { + display: inline-flex; + position: relative; + vertical-align: top; } + .dropdown.is-active .dropdown-menu, .dropdown.is-hoverable:hover .dropdown-menu { + display: block; } + .dropdown.is-right .dropdown-menu { + left: auto; + right: 0; } + .dropdown.is-up .dropdown-menu { + bottom: 100%; + padding-bottom: 4px; + padding-top: unset; + top: auto; } + +.dropdown-menu { + display: none; + left: 0; + min-width: 12rem; + padding-top: 4px; + position: absolute; + top: 100%; + z-index: 20; } + +.dropdown-content { + background-color: white; + border-radius: 3px; + box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); + padding-bottom: 0.5rem; + padding-top: 0.5rem; } + +.dropdown-item { + color: #4a4a4a; + display: block; + font-size: 0.875rem; + line-height: 1.5; + padding: 0.375rem 1rem; + position: relative; } + +a.dropdown-item { + padding-right: 3rem; + white-space: nowrap; } + a.dropdown-item:hover { + background-color: whitesmoke; + color: #0a0a0a; } + a.dropdown-item.is-active { + background-color: #3273dc; + color: #fff; } + +.dropdown-divider { + background-color: #dbdbdb; + border: none; + display: block; + height: 1px; + margin: 0.5rem 0; } + +.level { + align-items: center; + justify-content: space-between; } + .level:not(:last-child) { + margin-bottom: 1.5rem; } + .level code { + border-radius: 3px; } + .level img { + display: inline-block; + vertical-align: top; } + .level.is-mobile { + display: flex; } + .level.is-mobile .level-left, + .level.is-mobile .level-right { + display: flex; } + .level.is-mobile .level-left + .level-right { + margin-top: 0; } + .level.is-mobile .level-item { + margin-right: 0.75rem; } + .level.is-mobile .level-item:not(:last-child) { + margin-bottom: 0; } + .level.is-mobile .level-item:not(.is-narrow) { + flex-grow: 1; } + @media screen and (min-width: 769px), print { + .level { + display: flex; } + .level > .level-item:not(.is-narrow) { + flex-grow: 1; } } + +.level-item { + align-items: center; + display: flex; + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; + justify-content: center; } + .level-item .title, + .level-item .subtitle { + margin-bottom: 0; } + @media screen and (max-width: 768px) { + .level-item:not(:last-child) { + margin-bottom: 0.75rem; } } + +.level-left, +.level-right { + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; } + .level-left .level-item.is-flexible, + .level-right .level-item.is-flexible { + flex-grow: 1; } + @media screen and (min-width: 769px), print { + .level-left .level-item:not(:last-child), + .level-right .level-item:not(:last-child) { + margin-right: 0.75rem; } } + +.level-left { + align-items: center; + justify-content: flex-start; } + @media screen and (max-width: 768px) { + .level-left + .level-right { + margin-top: 1.5rem; } } + @media screen and (min-width: 769px), print { + .level-left { + display: flex; } } + +.level-right { + align-items: center; + justify-content: flex-end; } + @media screen and (min-width: 769px), print { + .level-right { + display: flex; } } + +.media { + align-items: flex-start; + display: flex; + text-align: left; } + .media .content:not(:last-child) { + margin-bottom: 0.75rem; } + .media .media { + border-top: 1px solid rgba(219, 219, 219, 0.5); + display: flex; + padding-top: 0.75rem; } + .media .media .content:not(:last-child), + .media .media .control:not(:last-child) { + margin-bottom: 0.5rem; } + .media .media .media { + padding-top: 0.5rem; } + .media .media .media + .media { + margin-top: 0.5rem; } + .media + .media { + border-top: 1px solid rgba(219, 219, 219, 0.5); + margin-top: 1rem; + padding-top: 1rem; } + .media.is-large + .media { + margin-top: 1.5rem; + padding-top: 1.5rem; } + +.media-left, +.media-right { + flex-basis: auto; + flex-grow: 0; + flex-shrink: 0; } + +.media-left { + margin-right: 1rem; } + +.media-right { + margin-left: 1rem; } + +.media-content { + flex-basis: auto; + flex-grow: 1; + flex-shrink: 1; + text-align: left; } + +.menu { + font-size: 1rem; } + .menu.is-small { + font-size: 0.75rem; } + .menu.is-medium { + font-size: 1.25rem; } + .menu.is-large { + font-size: 1.5rem; } + +.menu-list { + line-height: 1.25; } + .menu-list a { + border-radius: 2px; + color: #4a4a4a; + display: block; + padding: 0.5em 0.75em; } + .menu-list a:hover { + background-color: whitesmoke; + color: #363636; } + .menu-list a.is-active { + background-color: #3273dc; + color: #fff; } + .menu-list li ul { + border-left: 1px solid #dbdbdb; + margin: 0.75em; + padding-left: 0.75em; } + +.menu-label { + color: #7a7a7a; + font-size: 0.75em; + letter-spacing: 0.1em; + text-transform: uppercase; } + .menu-label:not(:first-child) { + margin-top: 1em; } + .menu-label:not(:last-child) { + margin-bottom: 1em; } + +.message { + background-color: whitesmoke; + border-radius: 3px; + font-size: 1rem; } + .message:not(:last-child) { + margin-bottom: 1.5rem; } + .message strong { + color: currentColor; } + .message a:not(.button):not(.tag) { + color: currentColor; + text-decoration: underline; } + .message.is-small { + font-size: 0.75rem; } + .message.is-medium { + font-size: 1.25rem; } + .message.is-large { + font-size: 1.5rem; } + .message.is-white { + background-color: white; } + .message.is-white .message-header { + background-color: white; + color: #0a0a0a; } + .message.is-white .message-body { + border-color: white; + color: #4d4d4d; } + .message.is-black { + background-color: #fafafa; } + .message.is-black .message-header { + background-color: #0a0a0a; + color: white; } + .message.is-black .message-body { + border-color: #0a0a0a; + color: #090909; } + .message.is-light { + background-color: #fafafa; } + .message.is-light .message-header { + background-color: whitesmoke; + color: #363636; } + .message.is-light .message-body { + border-color: whitesmoke; + color: #505050; } + .message.is-dark { + background-color: #fafafa; } + .message.is-dark .message-header { + background-color: #363636; + color: whitesmoke; } + .message.is-dark .message-body { + border-color: #363636; + color: #2a2a2a; } + .message.is-primary { + background-color: #fef7f6; } + .message.is-primary .message-header { + background-color: #C93312; + color: #fff; } + .message.is-primary .message-body { + border-color: #C93312; + color: #8a2711; } + .message.is-link { + background-color: #f6f9fe; } + .message.is-link .message-header { + background-color: #3273dc; + color: #fff; } + .message.is-link .message-body { + border-color: #3273dc; + color: #22509a; } + .message.is-info { + background-color: #f6fbfe; } + .message.is-info .message-header { + background-color: #209cee; + color: #fff; } + .message.is-info .message-body { + border-color: #209cee; + color: #12537e; } + .message.is-success { + background-color: #f6fef9; } + .message.is-success .message-header { + background-color: #23d160; + color: #fff; } + .message.is-success .message-body { + border-color: #23d160; + color: #0e301a; } + .message.is-warning { + background-color: #fffdf5; } + .message.is-warning .message-header { + background-color: #ffdd57; + color: #FFFFFF; } + .message.is-warning .message-body { + border-color: #ffdd57; + color: #3b3108; } + .message.is-danger { + background-color: #fff5f7; } + .message.is-danger .message-header { + background-color: #ff3860; + color: #fff; } + .message.is-danger .message-body { + border-color: #ff3860; + color: #cd0930; } + +.message-header { + align-items: center; + background-color: #4a4a4a; + border-radius: 3px 3px 0 0; + color: #fff; + display: flex; + justify-content: space-between; + line-height: 1.25; + padding: 0.5em 0.75em; + position: relative; } + .message-header .delete { + flex-grow: 0; + flex-shrink: 0; + margin-left: 0.75em; } + .message-header + .message-body { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-top: none; } + +.message-body { + border: 1px solid #dbdbdb; + border-radius: 3px; + color: #4a4a4a; + padding: 1em 1.25em; } + .message-body code, + .message-body pre { + background-color: white; } + .message-body pre code { + background-color: transparent; } + +.modal { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + align-items: center; + display: none; + justify-content: center; + overflow: hidden; + position: fixed; + z-index: 20; } + .modal.is-active { + display: flex; } + +.modal-background { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + background-color: rgba(10, 10, 10, 0.86); } + +.modal-content, +.modal-card { + margin: 0 20px; + max-height: calc(100vh - 160px); + overflow: auto; + position: relative; + width: 100%; } + @media screen and (min-width: 769px), print { + .modal-content, + .modal-card { + margin: 0 auto; + max-height: calc(100vh - 40px); + width: 640px; } } + +.modal-close { + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -moz-appearance: none; + -webkit-appearance: none; + background-color: rgba(10, 10, 10, 0.2); + border: none; + border-radius: 290486px; + cursor: pointer; + display: inline-block; + flex-grow: 0; + flex-shrink: 0; + font-size: 0; + height: 20px; + max-height: 20px; + max-width: 20px; + min-height: 20px; + min-width: 20px; + outline: none; + position: relative; + vertical-align: top; + width: 20px; + background: none; + height: 40px; + position: fixed; + right: 20px; + top: 20px; + width: 40px; } + .modal-close:before, .modal-close:after { + background-color: white; + content: ""; + display: block; + left: 50%; + position: absolute; + top: 50%; + transform: translateX(-50%) translateY(-50%) rotate(45deg); + transform-origin: center center; } + .modal-close:before { + height: 2px; + width: 50%; } + .modal-close:after { + height: 50%; + width: 2px; } + .modal-close:hover, .modal-close:focus { + background-color: rgba(10, 10, 10, 0.3); } + .modal-close:active { + background-color: rgba(10, 10, 10, 0.4); } + .modal-close.is-small { + height: 16px; + max-height: 16px; + max-width: 16px; + min-height: 16px; + min-width: 16px; + width: 16px; } + .modal-close.is-medium { + height: 24px; + max-height: 24px; + max-width: 24px; + min-height: 24px; + min-width: 24px; + width: 24px; } + .modal-close.is-large { + height: 32px; + max-height: 32px; + max-width: 32px; + min-height: 32px; + min-width: 32px; + width: 32px; } + +.modal-card { + display: flex; + flex-direction: column; + max-height: calc(100vh - 40px); + overflow: hidden; } + +.modal-card-head, +.modal-card-foot { + align-items: center; + background-color: whitesmoke; + display: flex; + flex-shrink: 0; + justify-content: flex-start; + padding: 20px; + position: relative; } + +.modal-card-head { + border-bottom: 1px solid #dbdbdb; + border-top-left-radius: 5px; + border-top-right-radius: 5px; } + +.modal-card-title { + color: #363636; + flex-grow: 1; + flex-shrink: 0; + font-size: 1.5rem; + line-height: 1; } + +.modal-card-foot { + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + border-top: 1px solid #dbdbdb; } + .modal-card-foot .button:not(:last-child) { + margin-right: 10px; } + +.modal-card-body { + -webkit-overflow-scrolling: touch; + background-color: white; + flex-grow: 1; + flex-shrink: 1; + overflow: auto; + padding: 20px; } + +.navbar { + background-color: white; + min-height: 3.25rem; + position: relative; } + .navbar.is-white { + background-color: white; + color: #0a0a0a; } + .navbar.is-white .navbar-brand > .navbar-item, + .navbar.is-white .navbar-brand .navbar-link { + color: #0a0a0a; } + .navbar.is-white .navbar-brand > a.navbar-item:hover, .navbar.is-white .navbar-brand > a.navbar-item.is-active, + .navbar.is-white .navbar-brand .navbar-link:hover, + .navbar.is-white .navbar-brand .navbar-link.is-active { + background-color: #f2f2f2; + color: #0a0a0a; } + .navbar.is-white .navbar-brand .navbar-link::after { + border-color: #0a0a0a; } + @media screen and (min-width: 1024px) { + .navbar.is-white .navbar-start > .navbar-item, + .navbar.is-white .navbar-start .navbar-link, + .navbar.is-white .navbar-end > .navbar-item, + .navbar.is-white .navbar-end .navbar-link { + color: #0a0a0a; } + .navbar.is-white .navbar-start > a.navbar-item:hover, .navbar.is-white .navbar-start > a.navbar-item.is-active, + .navbar.is-white .navbar-start .navbar-link:hover, + .navbar.is-white .navbar-start .navbar-link.is-active, + .navbar.is-white .navbar-end > a.navbar-item:hover, + .navbar.is-white .navbar-end > a.navbar-item.is-active, + .navbar.is-white .navbar-end .navbar-link:hover, + .navbar.is-white .navbar-end .navbar-link.is-active { + background-color: #f2f2f2; + color: #0a0a0a; } + .navbar.is-white .navbar-start .navbar-link::after, + .navbar.is-white .navbar-end .navbar-link::after { + border-color: #0a0a0a; } + .navbar.is-white .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #f2f2f2; + color: #0a0a0a; } + .navbar.is-white .navbar-dropdown a.navbar-item.is-active { + background-color: white; + color: #0a0a0a; } } + .navbar.is-black { + background-color: #0a0a0a; + color: white; } + .navbar.is-black .navbar-brand > .navbar-item, + .navbar.is-black .navbar-brand .navbar-link { + color: white; } + .navbar.is-black .navbar-brand > a.navbar-item:hover, .navbar.is-black .navbar-brand > a.navbar-item.is-active, + .navbar.is-black .navbar-brand .navbar-link:hover, + .navbar.is-black .navbar-brand .navbar-link.is-active { + background-color: black; + color: white; } + .navbar.is-black .navbar-brand .navbar-link::after { + border-color: white; } + @media screen and (min-width: 1024px) { + .navbar.is-black .navbar-start > .navbar-item, + .navbar.is-black .navbar-start .navbar-link, + .navbar.is-black .navbar-end > .navbar-item, + .navbar.is-black .navbar-end .navbar-link { + color: white; } + .navbar.is-black .navbar-start > a.navbar-item:hover, .navbar.is-black .navbar-start > a.navbar-item.is-active, + .navbar.is-black .navbar-start .navbar-link:hover, + .navbar.is-black .navbar-start .navbar-link.is-active, + .navbar.is-black .navbar-end > a.navbar-item:hover, + .navbar.is-black .navbar-end > a.navbar-item.is-active, + .navbar.is-black .navbar-end .navbar-link:hover, + .navbar.is-black .navbar-end .navbar-link.is-active { + background-color: black; + color: white; } + .navbar.is-black .navbar-start .navbar-link::after, + .navbar.is-black .navbar-end .navbar-link::after { + border-color: white; } + .navbar.is-black .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link { + background-color: black; + color: white; } + .navbar.is-black .navbar-dropdown a.navbar-item.is-active { + background-color: #0a0a0a; + color: white; } } + .navbar.is-light { + background-color: whitesmoke; + color: #363636; } + .navbar.is-light .navbar-brand > .navbar-item, + .navbar.is-light .navbar-brand .navbar-link { + color: #363636; } + .navbar.is-light .navbar-brand > a.navbar-item:hover, .navbar.is-light .navbar-brand > a.navbar-item.is-active, + .navbar.is-light .navbar-brand .navbar-link:hover, + .navbar.is-light .navbar-brand .navbar-link.is-active { + background-color: #e8e8e8; + color: #363636; } + .navbar.is-light .navbar-brand .navbar-link::after { + border-color: #363636; } + @media screen and (min-width: 1024px) { + .navbar.is-light .navbar-start > .navbar-item, + .navbar.is-light .navbar-start .navbar-link, + .navbar.is-light .navbar-end > .navbar-item, + .navbar.is-light .navbar-end .navbar-link { + color: #363636; } + .navbar.is-light .navbar-start > a.navbar-item:hover, .navbar.is-light .navbar-start > a.navbar-item.is-active, + .navbar.is-light .navbar-start .navbar-link:hover, + .navbar.is-light .navbar-start .navbar-link.is-active, + .navbar.is-light .navbar-end > a.navbar-item:hover, + .navbar.is-light .navbar-end > a.navbar-item.is-active, + .navbar.is-light .navbar-end .navbar-link:hover, + .navbar.is-light .navbar-end .navbar-link.is-active { + background-color: #e8e8e8; + color: #363636; } + .navbar.is-light .navbar-start .navbar-link::after, + .navbar.is-light .navbar-end .navbar-link::after { + border-color: #363636; } + .navbar.is-light .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #e8e8e8; + color: #363636; } + .navbar.is-light .navbar-dropdown a.navbar-item.is-active { + background-color: whitesmoke; + color: #363636; } } + .navbar.is-dark { + background-color: #363636; + color: whitesmoke; } + .navbar.is-dark .navbar-brand > .navbar-item, + .navbar.is-dark .navbar-brand .navbar-link { + color: whitesmoke; } + .navbar.is-dark .navbar-brand > a.navbar-item:hover, .navbar.is-dark .navbar-brand > a.navbar-item.is-active, + .navbar.is-dark .navbar-brand .navbar-link:hover, + .navbar.is-dark .navbar-brand .navbar-link.is-active { + background-color: #292929; + color: whitesmoke; } + .navbar.is-dark .navbar-brand .navbar-link::after { + border-color: whitesmoke; } + @media screen and (min-width: 1024px) { + .navbar.is-dark .navbar-start > .navbar-item, + .navbar.is-dark .navbar-start .navbar-link, + .navbar.is-dark .navbar-end > .navbar-item, + .navbar.is-dark .navbar-end .navbar-link { + color: whitesmoke; } + .navbar.is-dark .navbar-start > a.navbar-item:hover, .navbar.is-dark .navbar-start > a.navbar-item.is-active, + .navbar.is-dark .navbar-start .navbar-link:hover, + .navbar.is-dark .navbar-start .navbar-link.is-active, + .navbar.is-dark .navbar-end > a.navbar-item:hover, + .navbar.is-dark .navbar-end > a.navbar-item.is-active, + .navbar.is-dark .navbar-end .navbar-link:hover, + .navbar.is-dark .navbar-end .navbar-link.is-active { + background-color: #292929; + color: whitesmoke; } + .navbar.is-dark .navbar-start .navbar-link::after, + .navbar.is-dark .navbar-end .navbar-link::after { + border-color: whitesmoke; } + .navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #292929; + color: whitesmoke; } + .navbar.is-dark .navbar-dropdown a.navbar-item.is-active { + background-color: #363636; + color: whitesmoke; } } + .navbar.is-primary { + background-color: #C93312; + color: #fff; } + .navbar.is-primary .navbar-brand > .navbar-item, + .navbar.is-primary .navbar-brand .navbar-link { + color: #fff; } + .navbar.is-primary .navbar-brand > a.navbar-item:hover, .navbar.is-primary .navbar-brand > a.navbar-item.is-active, + .navbar.is-primary .navbar-brand .navbar-link:hover, + .navbar.is-primary .navbar-brand .navbar-link.is-active { + background-color: #b22d10; + color: #fff; } + .navbar.is-primary .navbar-brand .navbar-link::after { + border-color: #fff; } + @media screen and (min-width: 1024px) { + .navbar.is-primary .navbar-start > .navbar-item, + .navbar.is-primary .navbar-start .navbar-link, + .navbar.is-primary .navbar-end > .navbar-item, + .navbar.is-primary .navbar-end .navbar-link { + color: #fff; } + .navbar.is-primary .navbar-start > a.navbar-item:hover, .navbar.is-primary .navbar-start > a.navbar-item.is-active, + .navbar.is-primary .navbar-start .navbar-link:hover, + .navbar.is-primary .navbar-start .navbar-link.is-active, + .navbar.is-primary .navbar-end > a.navbar-item:hover, + .navbar.is-primary .navbar-end > a.navbar-item.is-active, + .navbar.is-primary .navbar-end .navbar-link:hover, + .navbar.is-primary .navbar-end .navbar-link.is-active { + background-color: #b22d10; + color: #fff; } + .navbar.is-primary .navbar-start .navbar-link::after, + .navbar.is-primary .navbar-end .navbar-link::after { + border-color: #fff; } + .navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #b22d10; + color: #fff; } + .navbar.is-primary .navbar-dropdown a.navbar-item.is-active { + background-color: #C93312; + color: #fff; } } + .navbar.is-link { + background-color: #3273dc; + color: #fff; } + .navbar.is-link .navbar-brand > .navbar-item, + .navbar.is-link .navbar-brand .navbar-link { + color: #fff; } + .navbar.is-link .navbar-brand > a.navbar-item:hover, .navbar.is-link .navbar-brand > a.navbar-item.is-active, + .navbar.is-link .navbar-brand .navbar-link:hover, + .navbar.is-link .navbar-brand .navbar-link.is-active { + background-color: #2366d1; + color: #fff; } + .navbar.is-link .navbar-brand .navbar-link::after { + border-color: #fff; } + @media screen and (min-width: 1024px) { + .navbar.is-link .navbar-start > .navbar-item, + .navbar.is-link .navbar-start .navbar-link, + .navbar.is-link .navbar-end > .navbar-item, + .navbar.is-link .navbar-end .navbar-link { + color: #fff; } + .navbar.is-link .navbar-start > a.navbar-item:hover, .navbar.is-link .navbar-start > a.navbar-item.is-active, + .navbar.is-link .navbar-start .navbar-link:hover, + .navbar.is-link .navbar-start .navbar-link.is-active, + .navbar.is-link .navbar-end > a.navbar-item:hover, + .navbar.is-link .navbar-end > a.navbar-item.is-active, + .navbar.is-link .navbar-end .navbar-link:hover, + .navbar.is-link .navbar-end .navbar-link.is-active { + background-color: #2366d1; + color: #fff; } + .navbar.is-link .navbar-start .navbar-link::after, + .navbar.is-link .navbar-end .navbar-link::after { + border-color: #fff; } + .navbar.is-link .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #2366d1; + color: #fff; } + .navbar.is-link .navbar-dropdown a.navbar-item.is-active { + background-color: #3273dc; + color: #fff; } } + .navbar.is-info { + background-color: #209cee; + color: #fff; } + .navbar.is-info .navbar-brand > .navbar-item, + .navbar.is-info .navbar-brand .navbar-link { + color: #fff; } + .navbar.is-info .navbar-brand > a.navbar-item:hover, .navbar.is-info .navbar-brand > a.navbar-item.is-active, + .navbar.is-info .navbar-brand .navbar-link:hover, + .navbar.is-info .navbar-brand .navbar-link.is-active { + background-color: #118fe4; + color: #fff; } + .navbar.is-info .navbar-brand .navbar-link::after { + border-color: #fff; } + @media screen and (min-width: 1024px) { + .navbar.is-info .navbar-start > .navbar-item, + .navbar.is-info .navbar-start .navbar-link, + .navbar.is-info .navbar-end > .navbar-item, + .navbar.is-info .navbar-end .navbar-link { + color: #fff; } + .navbar.is-info .navbar-start > a.navbar-item:hover, .navbar.is-info .navbar-start > a.navbar-item.is-active, + .navbar.is-info .navbar-start .navbar-link:hover, + .navbar.is-info .navbar-start .navbar-link.is-active, + .navbar.is-info .navbar-end > a.navbar-item:hover, + .navbar.is-info .navbar-end > a.navbar-item.is-active, + .navbar.is-info .navbar-end .navbar-link:hover, + .navbar.is-info .navbar-end .navbar-link.is-active { + background-color: #118fe4; + color: #fff; } + .navbar.is-info .navbar-start .navbar-link::after, + .navbar.is-info .navbar-end .navbar-link::after { + border-color: #fff; } + .navbar.is-info .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #118fe4; + color: #fff; } + .navbar.is-info .navbar-dropdown a.navbar-item.is-active { + background-color: #209cee; + color: #fff; } } + .navbar.is-success { + background-color: #23d160; + color: #fff; } + .navbar.is-success .navbar-brand > .navbar-item, + .navbar.is-success .navbar-brand .navbar-link { + color: #fff; } + .navbar.is-success .navbar-brand > a.navbar-item:hover, .navbar.is-success .navbar-brand > a.navbar-item.is-active, + .navbar.is-success .navbar-brand .navbar-link:hover, + .navbar.is-success .navbar-brand .navbar-link.is-active { + background-color: #20bc56; + color: #fff; } + .navbar.is-success .navbar-brand .navbar-link::after { + border-color: #fff; } + @media screen and (min-width: 1024px) { + .navbar.is-success .navbar-start > .navbar-item, + .navbar.is-success .navbar-start .navbar-link, + .navbar.is-success .navbar-end > .navbar-item, + .navbar.is-success .navbar-end .navbar-link { + color: #fff; } + .navbar.is-success .navbar-start > a.navbar-item:hover, .navbar.is-success .navbar-start > a.navbar-item.is-active, + .navbar.is-success .navbar-start .navbar-link:hover, + .navbar.is-success .navbar-start .navbar-link.is-active, + .navbar.is-success .navbar-end > a.navbar-item:hover, + .navbar.is-success .navbar-end > a.navbar-item.is-active, + .navbar.is-success .navbar-end .navbar-link:hover, + .navbar.is-success .navbar-end .navbar-link.is-active { + background-color: #20bc56; + color: #fff; } + .navbar.is-success .navbar-start .navbar-link::after, + .navbar.is-success .navbar-end .navbar-link::after { + border-color: #fff; } + .navbar.is-success .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #20bc56; + color: #fff; } + .navbar.is-success .navbar-dropdown a.navbar-item.is-active { + background-color: #23d160; + color: #fff; } } + .navbar.is-warning { + background-color: #ffdd57; + color: #FFFFFF; } + .navbar.is-warning .navbar-brand > .navbar-item, + .navbar.is-warning .navbar-brand .navbar-link { + color: #FFFFFF; } + .navbar.is-warning .navbar-brand > a.navbar-item:hover, .navbar.is-warning .navbar-brand > a.navbar-item.is-active, + .navbar.is-warning .navbar-brand .navbar-link:hover, + .navbar.is-warning .navbar-brand .navbar-link.is-active { + background-color: #ffd83d; + color: #FFFFFF; } + .navbar.is-warning .navbar-brand .navbar-link::after { + border-color: #FFFFFF; } + @media screen and (min-width: 1024px) { + .navbar.is-warning .navbar-start > .navbar-item, + .navbar.is-warning .navbar-start .navbar-link, + .navbar.is-warning .navbar-end > .navbar-item, + .navbar.is-warning .navbar-end .navbar-link { + color: #FFFFFF; } + .navbar.is-warning .navbar-start > a.navbar-item:hover, .navbar.is-warning .navbar-start > a.navbar-item.is-active, + .navbar.is-warning .navbar-start .navbar-link:hover, + .navbar.is-warning .navbar-start .navbar-link.is-active, + .navbar.is-warning .navbar-end > a.navbar-item:hover, + .navbar.is-warning .navbar-end > a.navbar-item.is-active, + .navbar.is-warning .navbar-end .navbar-link:hover, + .navbar.is-warning .navbar-end .navbar-link.is-active { + background-color: #ffd83d; + color: #FFFFFF; } + .navbar.is-warning .navbar-start .navbar-link::after, + .navbar.is-warning .navbar-end .navbar-link::after { + border-color: #FFFFFF; } + .navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #ffd83d; + color: #FFFFFF; } + .navbar.is-warning .navbar-dropdown a.navbar-item.is-active { + background-color: #ffdd57; + color: #FFFFFF; } } + .navbar.is-danger { + background-color: #ff3860; + color: #fff; } + .navbar.is-danger .navbar-brand > .navbar-item, + .navbar.is-danger .navbar-brand .navbar-link { + color: #fff; } + .navbar.is-danger .navbar-brand > a.navbar-item:hover, .navbar.is-danger .navbar-brand > a.navbar-item.is-active, + .navbar.is-danger .navbar-brand .navbar-link:hover, + .navbar.is-danger .navbar-brand .navbar-link.is-active { + background-color: #ff1f4b; + color: #fff; } + .navbar.is-danger .navbar-brand .navbar-link::after { + border-color: #fff; } + @media screen and (min-width: 1024px) { + .navbar.is-danger .navbar-start > .navbar-item, + .navbar.is-danger .navbar-start .navbar-link, + .navbar.is-danger .navbar-end > .navbar-item, + .navbar.is-danger .navbar-end .navbar-link { + color: #fff; } + .navbar.is-danger .navbar-start > a.navbar-item:hover, .navbar.is-danger .navbar-start > a.navbar-item.is-active, + .navbar.is-danger .navbar-start .navbar-link:hover, + .navbar.is-danger .navbar-start .navbar-link.is-active, + .navbar.is-danger .navbar-end > a.navbar-item:hover, + .navbar.is-danger .navbar-end > a.navbar-item.is-active, + .navbar.is-danger .navbar-end .navbar-link:hover, + .navbar.is-danger .navbar-end .navbar-link.is-active { + background-color: #ff1f4b; + color: #fff; } + .navbar.is-danger .navbar-start .navbar-link::after, + .navbar.is-danger .navbar-end .navbar-link::after { + border-color: #fff; } + .navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link, + .navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link { + background-color: #ff1f4b; + color: #fff; } + .navbar.is-danger .navbar-dropdown a.navbar-item.is-active { + background-color: #ff3860; + color: #fff; } } + .navbar > .container { + align-items: stretch; + display: flex; + min-height: 3.25rem; + width: 100%; } + .navbar.has-shadow { + box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1); } + .navbar.is-fixed-bottom, .navbar.is-fixed-top { + left: 0; + position: fixed; + right: 0; + z-index: 30; } + .navbar.is-fixed-bottom { + bottom: 0; } + .navbar.is-fixed-bottom.has-shadow { + box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); } + .navbar.is-fixed-top { + top: 0; } + +html.has-navbar-fixed-top { + padding-top: 3.25rem; } + +html.has-navbar-fixed-bottom { + padding-bottom: 3.25rem; } + +.navbar-brand, +.navbar-tabs { + align-items: stretch; + display: flex; + flex-shrink: 0; + min-height: 3.25rem; } + +.navbar-tabs { + -webkit-overflow-scrolling: touch; + max-width: 100vw; + overflow-x: auto; + overflow-y: hidden; } + +.navbar-burger { + cursor: pointer; + display: block; + height: 3.25rem; + position: relative; + width: 3.25rem; + margin-left: auto; } + .navbar-burger span { + background-color: currentColor; + display: block; + height: 1px; + left: calc(50% - 8px); + position: absolute; + transform-origin: center; + transition-duration: 86ms; + transition-property: background-color, opacity, transform; + transition-timing-function: ease-out; + width: 16px; } + .navbar-burger span:nth-child(1) { + top: calc(50% - 6px); } + .navbar-burger span:nth-child(2) { + top: calc(50% - 1px); } + .navbar-burger span:nth-child(3) { + top: calc(50% + 4px); } + .navbar-burger:hover { + background-color: rgba(0, 0, 0, 0.05); } + .navbar-burger.is-active span:nth-child(1) { + transform: translateY(5px) rotate(45deg); } + .navbar-burger.is-active span:nth-child(2) { + opacity: 0; } + .navbar-burger.is-active span:nth-child(3) { + transform: translateY(-5px) rotate(-45deg); } + +.navbar-menu { + display: none; } + +.navbar-item, +.navbar-link { + color: #4a4a4a; + display: block; + line-height: 1.5; + padding: 0.5rem 1rem; + position: relative; } + +a.navbar-item:hover, a.navbar-item.is-active, +a.navbar-link:hover, +a.navbar-link.is-active { + background-color: whitesmoke; + color: #3273dc; } + +.navbar-item { + flex-grow: 0; + flex-shrink: 0; } + .navbar-item img { + max-height: 1.75rem; } + .navbar-item.has-dropdown { + padding: 0; } + .navbar-item.is-expanded { + flex-grow: 1; + flex-shrink: 1; } + .navbar-item.is-tab { + border-bottom: 1px solid transparent; + min-height: 3.25rem; + padding-bottom: calc(0.5rem - 1px); } + .navbar-item.is-tab:hover { + background-color: transparent; + border-bottom-color: #3273dc; } + .navbar-item.is-tab.is-active { + background-color: transparent; + border-bottom-color: #3273dc; + border-bottom-style: solid; + border-bottom-width: 3px; + color: #3273dc; + padding-bottom: calc(0.5rem - 3px); } + +.navbar-content { + flex-grow: 1; + flex-shrink: 1; } + +.navbar-link { + padding-right: 2.5em; } + +.navbar-dropdown { + font-size: 0.875rem; + padding-bottom: 0.5rem; + padding-top: 0.5rem; } + .navbar-dropdown .navbar-item { + padding-left: 1.5rem; + padding-right: 1.5rem; } + +.navbar-divider { + background-color: #dbdbdb; + border: none; + display: none; + height: 1px; + margin: 0.5rem 0; } + +@media screen and (max-width: 1023px) { + .navbar > .container { + display: block; } + .navbar-brand .navbar-item, + .navbar-tabs .navbar-item { + align-items: center; + display: flex; } + .navbar-menu { + background-color: white; + box-shadow: 0 8px 16px rgba(10, 10, 10, 0.1); + padding: 0.5rem 0; } + .navbar-menu.is-active { + display: block; } + .navbar.is-fixed-bottom-touch, .navbar.is-fixed-top-touch { + left: 0; + position: fixed; + right: 0; + z-index: 30; } + .navbar.is-fixed-bottom-touch { + bottom: 0; } + .navbar.is-fixed-bottom-touch.has-shadow { + box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); } + .navbar.is-fixed-top-touch { + top: 0; } + .navbar.is-fixed-top .navbar-menu, .navbar.is-fixed-top-touch .navbar-menu { + -webkit-overflow-scrolling: touch; + max-height: calc(100vh - 3.25rem); + overflow: auto; } + html.has-navbar-fixed-top-touch { + padding-top: 3.25rem; } + html.has-navbar-fixed-bottom-touch { + padding-bottom: 3.25rem; } } + +@media screen and (min-width: 1024px) { + .navbar, + .navbar-menu, + .navbar-start, + .navbar-end { + align-items: stretch; + display: flex; } + .navbar { + min-height: 3.25rem; } + .navbar.is-transparent a.navbar-item:hover, .navbar.is-transparent a.navbar-item.is-active, + .navbar.is-transparent a.navbar-link:hover, + .navbar.is-transparent a.navbar-link.is-active { + background-color: transparent !important; } + .navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link, .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link { + background-color: transparent !important; } + .navbar.is-transparent .navbar-dropdown a.navbar-item:hover { + background-color: whitesmoke; + color: #0a0a0a; } + .navbar.is-transparent .navbar-dropdown a.navbar-item.is-active { + background-color: whitesmoke; + color: #3273dc; } + .navbar-burger { + display: none; } + .navbar-item, + .navbar-link { + align-items: center; + display: flex; } + .navbar-item.has-dropdown { + align-items: stretch; } + .navbar-item.has-dropdown-up .navbar-link::after { + transform: rotate(135deg) translate(0.25em, -0.25em); } + .navbar-item.has-dropdown-up .navbar-dropdown { + border-bottom: 1px solid #dbdbdb; + border-radius: 5px 5px 0 0; + border-top: none; + bottom: 100%; + box-shadow: 0 -8px 8px rgba(10, 10, 10, 0.1); + top: auto; } + .navbar-item.is-active .navbar-dropdown, .navbar-item.is-hoverable:hover .navbar-dropdown { + display: block; } + .navbar-item.is-active .navbar-dropdown.is-boxed, .navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed { + opacity: 1; + pointer-events: auto; + transform: translateY(0); } + .navbar-link::after { + border: 1px solid #3273dc; + border-right: 0; + border-top: 0; + content: " "; + display: block; + height: 0.5em; + pointer-events: none; + position: absolute; + transform: rotate(-45deg); + transform-origin: center; + width: 0.5em; + margin-top: -0.375em; + right: 1.125em; + top: 50%; } + .navbar-menu { + flex-grow: 1; + flex-shrink: 0; } + .navbar-start { + justify-content: flex-start; + margin-right: auto; } + .navbar-end { + justify-content: flex-end; + margin-left: auto; } + .navbar-dropdown { + background-color: white; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + border-top: 1px solid #dbdbdb; + box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1); + display: none; + font-size: 0.875rem; + left: 0; + min-width: 100%; + position: absolute; + top: 100%; + z-index: 20; } + .navbar-dropdown .navbar-item { + padding: 0.375rem 1rem; + white-space: nowrap; } + .navbar-dropdown a.navbar-item { + padding-right: 3rem; } + .navbar-dropdown a.navbar-item:hover { + background-color: whitesmoke; + color: #0a0a0a; } + .navbar-dropdown a.navbar-item.is-active { + background-color: whitesmoke; + color: #3273dc; } + .navbar-dropdown.is-boxed { + border-radius: 5px; + border-top: none; + box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); + display: block; + opacity: 0; + pointer-events: none; + top: calc(100% + (-4px)); + transform: translateY(-5px); + transition-duration: 86ms; + transition-property: opacity, transform; } + .navbar-dropdown.is-right { + left: auto; + right: 0; } + .navbar-divider { + display: block; } + .navbar > .container .navbar-brand, + .container > .navbar .navbar-brand { + margin-left: -1rem; } + .navbar > .container .navbar-menu, + .container > .navbar .navbar-menu { + margin-right: -1rem; } + .navbar.is-fixed-bottom-desktop, .navbar.is-fixed-top-desktop { + left: 0; + position: fixed; + right: 0; + z-index: 30; } + .navbar.is-fixed-bottom-desktop { + bottom: 0; } + .navbar.is-fixed-bottom-desktop.has-shadow { + box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); } + .navbar.is-fixed-top-desktop { + top: 0; } + html.has-navbar-fixed-top-desktop { + padding-top: 3.25rem; } + html.has-navbar-fixed-bottom-desktop { + padding-bottom: 3.25rem; } + a.navbar-item.is-active, + a.navbar-link.is-active { + color: #0a0a0a; } + a.navbar-item.is-active:not(:hover), + a.navbar-link.is-active:not(:hover) { + background-color: transparent; } + .navbar-item.has-dropdown:hover .navbar-link, .navbar-item.has-dropdown.is-active .navbar-link { + background-color: whitesmoke; } } + +.pagination { + font-size: 1rem; + margin: -0.25rem; } + .pagination.is-small { + font-size: 0.75rem; } + .pagination.is-medium { + font-size: 1.25rem; } + .pagination.is-large { + font-size: 1.5rem; } + +.pagination, +.pagination-list { + align-items: center; + display: flex; + justify-content: center; + text-align: center; } + +.pagination-previous, +.pagination-next, +.pagination-link, +.pagination-ellipsis { + -moz-appearance: none; + -webkit-appearance: none; + align-items: center; + border: 1px solid transparent; + border-radius: 3px; + box-shadow: none; + display: inline-flex; + font-size: 1rem; + height: 2.25em; + justify-content: flex-start; + line-height: 1.5; + padding-bottom: calc(0.375em - 1px); + padding-left: calc(0.625em - 1px); + padding-right: calc(0.625em - 1px); + padding-top: calc(0.375em - 1px); + position: relative; + vertical-align: top; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + font-size: 1em; + padding-left: 0.5em; + padding-right: 0.5em; + justify-content: center; + margin: 0.25rem; + text-align: center; } + .pagination-previous:focus, .pagination-previous.is-focused, .pagination-previous:active, .pagination-previous.is-active, + .pagination-next:focus, + .pagination-next.is-focused, + .pagination-next:active, + .pagination-next.is-active, + .pagination-link:focus, + .pagination-link.is-focused, + .pagination-link:active, + .pagination-link.is-active, + .pagination-ellipsis:focus, + .pagination-ellipsis.is-focused, + .pagination-ellipsis:active, + .pagination-ellipsis.is-active { + outline: none; } + .pagination-previous[disabled], + .pagination-next[disabled], + .pagination-link[disabled], + .pagination-ellipsis[disabled] { + cursor: not-allowed; } + +.pagination-previous, +.pagination-next, +.pagination-link { + border-color: #dbdbdb; + min-width: 2.25em; } + .pagination-previous:hover, + .pagination-next:hover, + .pagination-link:hover { + border-color: #b5b5b5; + color: #363636; } + .pagination-previous:focus, + .pagination-next:focus, + .pagination-link:focus { + border-color: #3273dc; } + .pagination-previous:active, + .pagination-next:active, + .pagination-link:active { + box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2); } + .pagination-previous[disabled], + .pagination-next[disabled], + .pagination-link[disabled] { + background-color: #dbdbdb; + border-color: #dbdbdb; + box-shadow: none; + color: #7a7a7a; + opacity: 0.5; } + +.pagination-previous, +.pagination-next { + padding-left: 0.75em; + padding-right: 0.75em; + white-space: nowrap; } + +.pagination-link.is-current { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; } + +.pagination-ellipsis { + color: #b5b5b5; + pointer-events: none; } + +.pagination-list { + flex-wrap: wrap; } + +@media screen and (max-width: 768px) { + .pagination { + flex-wrap: wrap; } + .pagination-previous, + .pagination-next { + flex-grow: 1; + flex-shrink: 1; } + .pagination-list li { + flex-grow: 1; + flex-shrink: 1; } } + +@media screen and (min-width: 769px), print { + .pagination-list { + flex-grow: 1; + flex-shrink: 1; + justify-content: flex-start; + order: 1; } + .pagination-previous { + order: 2; } + .pagination-next { + order: 3; } + .pagination { + justify-content: space-between; } + .pagination.is-centered .pagination-previous { + order: 1; } + .pagination.is-centered .pagination-list { + justify-content: center; + order: 2; } + .pagination.is-centered .pagination-next { + order: 3; } + .pagination.is-right .pagination-previous { + order: 1; } + .pagination.is-right .pagination-next { + order: 2; } + .pagination.is-right .pagination-list { + justify-content: flex-end; + order: 3; } } + +.panel { + font-size: 1rem; } + .panel:not(:last-child) { + margin-bottom: 1.5rem; } + +.panel-heading, +.panel-tabs, +.panel-block { + border-bottom: 1px solid #dbdbdb; + border-left: 1px solid #dbdbdb; + border-right: 1px solid #dbdbdb; } + .panel-heading:first-child, + .panel-tabs:first-child, + .panel-block:first-child { + border-top: 1px solid #dbdbdb; } + +.panel-heading { + background-color: whitesmoke; + border-radius: 3px 3px 0 0; + color: #363636; + font-size: 1.25em; + font-weight: 300; + line-height: 1.25; + padding: 0.5em 0.75em; } + +.panel-tabs { + align-items: flex-end; + display: flex; + font-size: 0.875em; + justify-content: center; } + .panel-tabs a { + border-bottom: 1px solid #dbdbdb; + margin-bottom: -1px; + padding: 0.5em; } + .panel-tabs a.is-active { + border-bottom-color: #4a4a4a; + color: #363636; } + +.panel-list a { + color: #4a4a4a; } + .panel-list a:hover { + color: #3273dc; } + +.panel-block { + align-items: center; + color: #363636; + display: flex; + justify-content: flex-start; + padding: 0.5em 0.75em; } + .panel-block input[type="checkbox"] { + margin-right: 0.75em; } + .panel-block > .control { + flex-grow: 1; + flex-shrink: 1; + width: 100%; } + .panel-block.is-wrapped { + flex-wrap: wrap; } + .panel-block.is-active { + border-left-color: #3273dc; + color: #363636; } + .panel-block.is-active .panel-icon { + color: #3273dc; } + +a.panel-block, +label.panel-block { + cursor: pointer; } + a.panel-block:hover, + label.panel-block:hover { + background-color: whitesmoke; } + +.panel-icon { + display: inline-block; + font-size: 14px; + height: 1em; + line-height: 1em; + text-align: center; + vertical-align: top; + width: 1em; + color: #7a7a7a; + margin-right: 0.75em; } + .panel-icon .fa { + font-size: inherit; + line-height: inherit; } + +.tabs { + -webkit-overflow-scrolling: touch; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + align-items: stretch; + display: flex; + font-size: 1rem; + justify-content: space-between; + overflow: hidden; + overflow-x: auto; + white-space: nowrap; } + .tabs:not(:last-child) { + margin-bottom: 1.5rem; } + .tabs a { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + color: #4a4a4a; + display: flex; + justify-content: center; + margin-bottom: -1px; + padding: 0.5em 1em; + vertical-align: top; } + .tabs a:hover { + border-bottom-color: #363636; + color: #363636; } + .tabs li { + display: block; } + .tabs li.is-active a { + border-bottom-color: #3273dc; + color: #3273dc; } + .tabs ul { + align-items: center; + border-bottom-color: #dbdbdb; + border-bottom-style: solid; + border-bottom-width: 1px; + display: flex; + flex-grow: 1; + flex-shrink: 0; + justify-content: flex-start; } + .tabs ul.is-left { + padding-right: 0.75em; } + .tabs ul.is-center { + flex: none; + justify-content: center; + padding-left: 0.75em; + padding-right: 0.75em; } + .tabs ul.is-right { + justify-content: flex-end; + padding-left: 0.75em; } + .tabs .icon:first-child { + margin-right: 0.5em; } + .tabs .icon:last-child { + margin-left: 0.5em; } + .tabs.is-centered ul { + justify-content: center; } + .tabs.is-right ul { + justify-content: flex-end; } + .tabs.is-boxed a { + border: 1px solid transparent; + border-radius: 3px 3px 0 0; } + .tabs.is-boxed a:hover { + background-color: whitesmoke; + border-bottom-color: #dbdbdb; } + .tabs.is-boxed li.is-active a { + background-color: white; + border-color: #dbdbdb; + border-bottom-color: transparent !important; } + .tabs.is-fullwidth li { + flex-grow: 1; + flex-shrink: 0; } + .tabs.is-toggle a { + border-color: #dbdbdb; + border-style: solid; + border-width: 1px; + margin-bottom: 0; + position: relative; } + .tabs.is-toggle a:hover { + background-color: whitesmoke; + border-color: #b5b5b5; + z-index: 2; } + .tabs.is-toggle li + li { + margin-left: -1px; } + .tabs.is-toggle li:first-child a { + border-radius: 3px 0 0 3px; } + .tabs.is-toggle li:last-child a { + border-radius: 0 3px 3px 0; } + .tabs.is-toggle li.is-active a { + background-color: #3273dc; + border-color: #3273dc; + color: #fff; + z-index: 1; } + .tabs.is-toggle ul { + border-bottom: none; } + .tabs.is-small { + font-size: 0.75rem; } + .tabs.is-medium { + font-size: 1.25rem; } + .tabs.is-large { + font-size: 1.5rem; } + +.hero { + align-items: stretch; + display: flex; + flex-direction: column; + justify-content: space-between; } + .hero .navbar { + background: none; } + .hero .tabs ul { + border-bottom: none; } + .hero.is-white { + background-color: white; + color: #0a0a0a; } + .hero.is-white a:not(.button), + .hero.is-white strong { + color: inherit; } + .hero.is-white .title { + color: #0a0a0a; } + .hero.is-white .subtitle { + color: rgba(10, 10, 10, 0.9); } + .hero.is-white .subtitle a:not(.button), + .hero.is-white .subtitle strong { + color: #0a0a0a; } + @media screen and (max-width: 1023px) { + .hero.is-white .navbar-menu { + background-color: white; } } + .hero.is-white .navbar-item, + .hero.is-white .navbar-link { + color: rgba(10, 10, 10, 0.7); } + .hero.is-white a.navbar-item:hover, .hero.is-white a.navbar-item.is-active, + .hero.is-white .navbar-link:hover, + .hero.is-white .navbar-link.is-active { + background-color: #f2f2f2; + color: #0a0a0a; } + .hero.is-white .tabs a { + color: #0a0a0a; + opacity: 0.9; } + .hero.is-white .tabs a:hover { + opacity: 1; } + .hero.is-white .tabs li.is-active a { + opacity: 1; } + .hero.is-white .tabs.is-boxed a, .hero.is-white .tabs.is-toggle a { + color: #0a0a0a; } + .hero.is-white .tabs.is-boxed a:hover, .hero.is-white .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-white .tabs.is-boxed li.is-active a, .hero.is-white .tabs.is-boxed li.is-active a:hover, .hero.is-white .tabs.is-toggle li.is-active a, .hero.is-white .tabs.is-toggle li.is-active a:hover { + background-color: #0a0a0a; + border-color: #0a0a0a; + color: white; } + .hero.is-white.is-bold { + background-image: linear-gradient(141deg, #e6e6e6 0%, white 71%, white 100%); } + @media screen and (max-width: 768px) { + .hero.is-white.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #e6e6e6 0%, white 71%, white 100%); } } + .hero.is-black { + background-color: #0a0a0a; + color: white; } + .hero.is-black a:not(.button), + .hero.is-black strong { + color: inherit; } + .hero.is-black .title { + color: white; } + .hero.is-black .subtitle { + color: rgba(255, 255, 255, 0.9); } + .hero.is-black .subtitle a:not(.button), + .hero.is-black .subtitle strong { + color: white; } + @media screen and (max-width: 1023px) { + .hero.is-black .navbar-menu { + background-color: #0a0a0a; } } + .hero.is-black .navbar-item, + .hero.is-black .navbar-link { + color: rgba(255, 255, 255, 0.7); } + .hero.is-black a.navbar-item:hover, .hero.is-black a.navbar-item.is-active, + .hero.is-black .navbar-link:hover, + .hero.is-black .navbar-link.is-active { + background-color: black; + color: white; } + .hero.is-black .tabs a { + color: white; + opacity: 0.9; } + .hero.is-black .tabs a:hover { + opacity: 1; } + .hero.is-black .tabs li.is-active a { + opacity: 1; } + .hero.is-black .tabs.is-boxed a, .hero.is-black .tabs.is-toggle a { + color: white; } + .hero.is-black .tabs.is-boxed a:hover, .hero.is-black .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-black .tabs.is-boxed li.is-active a, .hero.is-black .tabs.is-boxed li.is-active a:hover, .hero.is-black .tabs.is-toggle li.is-active a, .hero.is-black .tabs.is-toggle li.is-active a:hover { + background-color: white; + border-color: white; + color: #0a0a0a; } + .hero.is-black.is-bold { + background-image: linear-gradient(141deg, black 0%, #0a0a0a 71%, #181616 100%); } + @media screen and (max-width: 768px) { + .hero.is-black.is-bold .navbar-menu { + background-image: linear-gradient(141deg, black 0%, #0a0a0a 71%, #181616 100%); } } + .hero.is-light { + background-color: whitesmoke; + color: #363636; } + .hero.is-light a:not(.button), + .hero.is-light strong { + color: inherit; } + .hero.is-light .title { + color: #363636; } + .hero.is-light .subtitle { + color: rgba(54, 54, 54, 0.9); } + .hero.is-light .subtitle a:not(.button), + .hero.is-light .subtitle strong { + color: #363636; } + @media screen and (max-width: 1023px) { + .hero.is-light .navbar-menu { + background-color: whitesmoke; } } + .hero.is-light .navbar-item, + .hero.is-light .navbar-link { + color: rgba(54, 54, 54, 0.7); } + .hero.is-light a.navbar-item:hover, .hero.is-light a.navbar-item.is-active, + .hero.is-light .navbar-link:hover, + .hero.is-light .navbar-link.is-active { + background-color: #e8e8e8; + color: #363636; } + .hero.is-light .tabs a { + color: #363636; + opacity: 0.9; } + .hero.is-light .tabs a:hover { + opacity: 1; } + .hero.is-light .tabs li.is-active a { + opacity: 1; } + .hero.is-light .tabs.is-boxed a, .hero.is-light .tabs.is-toggle a { + color: #363636; } + .hero.is-light .tabs.is-boxed a:hover, .hero.is-light .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-light .tabs.is-boxed li.is-active a, .hero.is-light .tabs.is-boxed li.is-active a:hover, .hero.is-light .tabs.is-toggle li.is-active a, .hero.is-light .tabs.is-toggle li.is-active a:hover { + background-color: #363636; + border-color: #363636; + color: whitesmoke; } + .hero.is-light.is-bold { + background-image: linear-gradient(141deg, #dfd8d9 0%, whitesmoke 71%, white 100%); } + @media screen and (max-width: 768px) { + .hero.is-light.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #dfd8d9 0%, whitesmoke 71%, white 100%); } } + .hero.is-dark { + background-color: #363636; + color: whitesmoke; } + .hero.is-dark a:not(.button), + .hero.is-dark strong { + color: inherit; } + .hero.is-dark .title { + color: whitesmoke; } + .hero.is-dark .subtitle { + color: rgba(245, 245, 245, 0.9); } + .hero.is-dark .subtitle a:not(.button), + .hero.is-dark .subtitle strong { + color: whitesmoke; } + @media screen and (max-width: 1023px) { + .hero.is-dark .navbar-menu { + background-color: #363636; } } + .hero.is-dark .navbar-item, + .hero.is-dark .navbar-link { + color: rgba(245, 245, 245, 0.7); } + .hero.is-dark a.navbar-item:hover, .hero.is-dark a.navbar-item.is-active, + .hero.is-dark .navbar-link:hover, + .hero.is-dark .navbar-link.is-active { + background-color: #292929; + color: whitesmoke; } + .hero.is-dark .tabs a { + color: whitesmoke; + opacity: 0.9; } + .hero.is-dark .tabs a:hover { + opacity: 1; } + .hero.is-dark .tabs li.is-active a { + opacity: 1; } + .hero.is-dark .tabs.is-boxed a, .hero.is-dark .tabs.is-toggle a { + color: whitesmoke; } + .hero.is-dark .tabs.is-boxed a:hover, .hero.is-dark .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-dark .tabs.is-boxed li.is-active a, .hero.is-dark .tabs.is-boxed li.is-active a:hover, .hero.is-dark .tabs.is-toggle li.is-active a, .hero.is-dark .tabs.is-toggle li.is-active a:hover { + background-color: whitesmoke; + border-color: whitesmoke; + color: #363636; } + .hero.is-dark.is-bold { + background-image: linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%); } + @media screen and (max-width: 768px) { + .hero.is-dark.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%); } } + .hero.is-primary { + background-color: #C93312; + color: #fff; } + .hero.is-primary a:not(.button), + .hero.is-primary strong { + color: inherit; } + .hero.is-primary .title { + color: #fff; } + .hero.is-primary .subtitle { + color: rgba(255, 255, 255, 0.9); } + .hero.is-primary .subtitle a:not(.button), + .hero.is-primary .subtitle strong { + color: #fff; } + @media screen and (max-width: 1023px) { + .hero.is-primary .navbar-menu { + background-color: #C93312; } } + .hero.is-primary .navbar-item, + .hero.is-primary .navbar-link { + color: rgba(255, 255, 255, 0.7); } + .hero.is-primary a.navbar-item:hover, .hero.is-primary a.navbar-item.is-active, + .hero.is-primary .navbar-link:hover, + .hero.is-primary .navbar-link.is-active { + background-color: #b22d10; + color: #fff; } + .hero.is-primary .tabs a { + color: #fff; + opacity: 0.9; } + .hero.is-primary .tabs a:hover { + opacity: 1; } + .hero.is-primary .tabs li.is-active a { + opacity: 1; } + .hero.is-primary .tabs.is-boxed a, .hero.is-primary .tabs.is-toggle a { + color: #fff; } + .hero.is-primary .tabs.is-boxed a:hover, .hero.is-primary .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-primary .tabs.is-boxed li.is-active a, .hero.is-primary .tabs.is-boxed li.is-active a:hover, .hero.is-primary .tabs.is-toggle li.is-active a, .hero.is-primary .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #C93312; } + .hero.is-primary.is-bold { + background-image: linear-gradient(141deg, #a30805 0%, #C93312 71%, #e7590e 100%); } + @media screen and (max-width: 768px) { + .hero.is-primary.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #a30805 0%, #C93312 71%, #e7590e 100%); } } + .hero.is-link { + background-color: #3273dc; + color: #fff; } + .hero.is-link a:not(.button), + .hero.is-link strong { + color: inherit; } + .hero.is-link .title { + color: #fff; } + .hero.is-link .subtitle { + color: rgba(255, 255, 255, 0.9); } + .hero.is-link .subtitle a:not(.button), + .hero.is-link .subtitle strong { + color: #fff; } + @media screen and (max-width: 1023px) { + .hero.is-link .navbar-menu { + background-color: #3273dc; } } + .hero.is-link .navbar-item, + .hero.is-link .navbar-link { + color: rgba(255, 255, 255, 0.7); } + .hero.is-link a.navbar-item:hover, .hero.is-link a.navbar-item.is-active, + .hero.is-link .navbar-link:hover, + .hero.is-link .navbar-link.is-active { + background-color: #2366d1; + color: #fff; } + .hero.is-link .tabs a { + color: #fff; + opacity: 0.9; } + .hero.is-link .tabs a:hover { + opacity: 1; } + .hero.is-link .tabs li.is-active a { + opacity: 1; } + .hero.is-link .tabs.is-boxed a, .hero.is-link .tabs.is-toggle a { + color: #fff; } + .hero.is-link .tabs.is-boxed a:hover, .hero.is-link .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-link .tabs.is-boxed li.is-active a, .hero.is-link .tabs.is-boxed li.is-active a:hover, .hero.is-link .tabs.is-toggle li.is-active a, .hero.is-link .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #3273dc; } + .hero.is-link.is-bold { + background-image: linear-gradient(141deg, #1577c6 0%, #3273dc 71%, #4366e5 100%); } + @media screen and (max-width: 768px) { + .hero.is-link.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #1577c6 0%, #3273dc 71%, #4366e5 100%); } } + .hero.is-info { + background-color: #209cee; + color: #fff; } + .hero.is-info a:not(.button), + .hero.is-info strong { + color: inherit; } + .hero.is-info .title { + color: #fff; } + .hero.is-info .subtitle { + color: rgba(255, 255, 255, 0.9); } + .hero.is-info .subtitle a:not(.button), + .hero.is-info .subtitle strong { + color: #fff; } + @media screen and (max-width: 1023px) { + .hero.is-info .navbar-menu { + background-color: #209cee; } } + .hero.is-info .navbar-item, + .hero.is-info .navbar-link { + color: rgba(255, 255, 255, 0.7); } + .hero.is-info a.navbar-item:hover, .hero.is-info a.navbar-item.is-active, + .hero.is-info .navbar-link:hover, + .hero.is-info .navbar-link.is-active { + background-color: #118fe4; + color: #fff; } + .hero.is-info .tabs a { + color: #fff; + opacity: 0.9; } + .hero.is-info .tabs a:hover { + opacity: 1; } + .hero.is-info .tabs li.is-active a { + opacity: 1; } + .hero.is-info .tabs.is-boxed a, .hero.is-info .tabs.is-toggle a { + color: #fff; } + .hero.is-info .tabs.is-boxed a:hover, .hero.is-info .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-info .tabs.is-boxed li.is-active a, .hero.is-info .tabs.is-boxed li.is-active a:hover, .hero.is-info .tabs.is-toggle li.is-active a, .hero.is-info .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #209cee; } + .hero.is-info.is-bold { + background-image: linear-gradient(141deg, #04a6d7 0%, #209cee 71%, #3287f5 100%); } + @media screen and (max-width: 768px) { + .hero.is-info.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #04a6d7 0%, #209cee 71%, #3287f5 100%); } } + .hero.is-success { + background-color: #23d160; + color: #fff; } + .hero.is-success a:not(.button), + .hero.is-success strong { + color: inherit; } + .hero.is-success .title { + color: #fff; } + .hero.is-success .subtitle { + color: rgba(255, 255, 255, 0.9); } + .hero.is-success .subtitle a:not(.button), + .hero.is-success .subtitle strong { + color: #fff; } + @media screen and (max-width: 1023px) { + .hero.is-success .navbar-menu { + background-color: #23d160; } } + .hero.is-success .navbar-item, + .hero.is-success .navbar-link { + color: rgba(255, 255, 255, 0.7); } + .hero.is-success a.navbar-item:hover, .hero.is-success a.navbar-item.is-active, + .hero.is-success .navbar-link:hover, + .hero.is-success .navbar-link.is-active { + background-color: #20bc56; + color: #fff; } + .hero.is-success .tabs a { + color: #fff; + opacity: 0.9; } + .hero.is-success .tabs a:hover { + opacity: 1; } + .hero.is-success .tabs li.is-active a { + opacity: 1; } + .hero.is-success .tabs.is-boxed a, .hero.is-success .tabs.is-toggle a { + color: #fff; } + .hero.is-success .tabs.is-boxed a:hover, .hero.is-success .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-success .tabs.is-boxed li.is-active a, .hero.is-success .tabs.is-boxed li.is-active a:hover, .hero.is-success .tabs.is-toggle li.is-active a, .hero.is-success .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #23d160; } + .hero.is-success.is-bold { + background-image: linear-gradient(141deg, #12af2f 0%, #23d160 71%, #2ce28a 100%); } + @media screen and (max-width: 768px) { + .hero.is-success.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #12af2f 0%, #23d160 71%, #2ce28a 100%); } } + .hero.is-warning { + background-color: #ffdd57; + color: #FFFFFF; } + .hero.is-warning a:not(.button), + .hero.is-warning strong { + color: inherit; } + .hero.is-warning .title { + color: #FFFFFF; } + .hero.is-warning .subtitle { + color: rgba(255, 255, 255, 0.9); } + .hero.is-warning .subtitle a:not(.button), + .hero.is-warning .subtitle strong { + color: #FFFFFF; } + @media screen and (max-width: 1023px) { + .hero.is-warning .navbar-menu { + background-color: #ffdd57; } } + .hero.is-warning .navbar-item, + .hero.is-warning .navbar-link { + color: rgba(255, 255, 255, 0.7); } + .hero.is-warning a.navbar-item:hover, .hero.is-warning a.navbar-item.is-active, + .hero.is-warning .navbar-link:hover, + .hero.is-warning .navbar-link.is-active { + background-color: #ffd83d; + color: #FFFFFF; } + .hero.is-warning .tabs a { + color: #FFFFFF; + opacity: 0.9; } + .hero.is-warning .tabs a:hover { + opacity: 1; } + .hero.is-warning .tabs li.is-active a { + opacity: 1; } + .hero.is-warning .tabs.is-boxed a, .hero.is-warning .tabs.is-toggle a { + color: #FFFFFF; } + .hero.is-warning .tabs.is-boxed a:hover, .hero.is-warning .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-warning .tabs.is-boxed li.is-active a, .hero.is-warning .tabs.is-boxed li.is-active a:hover, .hero.is-warning .tabs.is-toggle li.is-active a, .hero.is-warning .tabs.is-toggle li.is-active a:hover { + background-color: #FFFFFF; + border-color: #FFFFFF; + color: #ffdd57; } + .hero.is-warning.is-bold { + background-image: linear-gradient(141deg, #ffaf24 0%, #ffdd57 71%, #fffa70 100%); } + @media screen and (max-width: 768px) { + .hero.is-warning.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #ffaf24 0%, #ffdd57 71%, #fffa70 100%); } } + .hero.is-danger { + background-color: #ff3860; + color: #fff; } + .hero.is-danger a:not(.button), + .hero.is-danger strong { + color: inherit; } + .hero.is-danger .title { + color: #fff; } + .hero.is-danger .subtitle { + color: rgba(255, 255, 255, 0.9); } + .hero.is-danger .subtitle a:not(.button), + .hero.is-danger .subtitle strong { + color: #fff; } + @media screen and (max-width: 1023px) { + .hero.is-danger .navbar-menu { + background-color: #ff3860; } } + .hero.is-danger .navbar-item, + .hero.is-danger .navbar-link { + color: rgba(255, 255, 255, 0.7); } + .hero.is-danger a.navbar-item:hover, .hero.is-danger a.navbar-item.is-active, + .hero.is-danger .navbar-link:hover, + .hero.is-danger .navbar-link.is-active { + background-color: #ff1f4b; + color: #fff; } + .hero.is-danger .tabs a { + color: #fff; + opacity: 0.9; } + .hero.is-danger .tabs a:hover { + opacity: 1; } + .hero.is-danger .tabs li.is-active a { + opacity: 1; } + .hero.is-danger .tabs.is-boxed a, .hero.is-danger .tabs.is-toggle a { + color: #fff; } + .hero.is-danger .tabs.is-boxed a:hover, .hero.is-danger .tabs.is-toggle a:hover { + background-color: rgba(10, 10, 10, 0.1); } + .hero.is-danger .tabs.is-boxed li.is-active a, .hero.is-danger .tabs.is-boxed li.is-active a:hover, .hero.is-danger .tabs.is-toggle li.is-active a, .hero.is-danger .tabs.is-toggle li.is-active a:hover { + background-color: #fff; + border-color: #fff; + color: #ff3860; } + .hero.is-danger.is-bold { + background-image: linear-gradient(141deg, #ff0561 0%, #ff3860 71%, #ff5257 100%); } + @media screen and (max-width: 768px) { + .hero.is-danger.is-bold .navbar-menu { + background-image: linear-gradient(141deg, #ff0561 0%, #ff3860 71%, #ff5257 100%); } } + .hero.is-small .hero-body { + padding-bottom: 1.5rem; + padding-top: 1.5rem; } + @media screen and (min-width: 769px), print { + .hero.is-medium .hero-body { + padding-bottom: 9rem; + padding-top: 9rem; } } + @media screen and (min-width: 769px), print { + .hero.is-large .hero-body { + padding-bottom: 18rem; + padding-top: 18rem; } } + .hero.is-halfheight .hero-body, .hero.is-fullheight .hero-body { + align-items: center; + display: flex; } + .hero.is-halfheight .hero-body > .container, .hero.is-fullheight .hero-body > .container { + flex-grow: 1; + flex-shrink: 1; } + .hero.is-halfheight { + min-height: 50vh; } + .hero.is-fullheight { + min-height: 100vh; } + +.hero-video { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + overflow: hidden; } + .hero-video video { + left: 50%; + min-height: 100%; + min-width: 100%; + position: absolute; + top: 50%; + transform: translate3d(-50%, -50%, 0); } + .hero-video.is-transparent { + opacity: 0.3; } + @media screen and (max-width: 768px) { + .hero-video { + display: none; } } + +.hero-buttons { + margin-top: 1.5rem; } + @media screen and (max-width: 768px) { + .hero-buttons .button { + display: flex; } + .hero-buttons .button:not(:last-child) { + margin-bottom: 0.75rem; } } + @media screen and (min-width: 769px), print { + .hero-buttons { + display: flex; + justify-content: center; } + .hero-buttons .button:not(:last-child) { + margin-right: 1.5rem; } } + +.hero-head, +.hero-foot { + flex-grow: 0; + flex-shrink: 0; } + +.hero-body { + flex-grow: 1; + flex-shrink: 0; + padding: 3rem 1.5rem; } + +.section { + padding: 3rem 1.5rem; } + @media screen and (min-width: 1024px) { + .section.is-medium { + padding: 9rem 1.5rem; } + .section.is-large { + padding: 18rem 1.5rem; } } + +.footer { + background-color: whitesmoke; + padding: 3rem 1.5rem 6rem; } + +.sidebody { + height: 100vh; + overflow-x: hidden; + overflow-y: scroll; } + +.example { + margin-bottom: 1em; } + .example .highlight { + margin: 0; } + .example .path { + font-style: italic; + width: 100%; + text-align: right; } + +.sidebar .version { + padding: 1em; } + +.sidebar .brand { + background-color: #303030; + color: #c0c0c0; + padding: 1em; + top: 0; } + +.sidebar .menu { + padding: 1em; } + +.mainbody { + padding: 3em; } + +code { + color: #1a9f1a; + font-size: 0.875em; + font-weight: normal; } + +.content h2 { + padding-top: 1em; + border-top: 1px solid #c0c0c0; } diff --git a/docs/src/themes/mitmproxydocs/theme.toml b/docs/src/themes/mitmproxydocs/theme.toml new file mode 100644 index 000000000..5909676be --- /dev/null +++ b/docs/src/themes/mitmproxydocs/theme.toml @@ -0,0 +1,2 @@ +name = "mitmproxy" +description = "mitmproxy's internal theme" \ No newline at end of file diff --git a/docs/style/fa/_animated.scss b/docs/style/fa/_animated.scss new file mode 100644 index 000000000..7c7c0e173 --- /dev/null +++ b/docs/style/fa/_animated.scss @@ -0,0 +1,20 @@ +// Animated Icons +// -------------------------- + +.#{$fa-css-prefix}-spin { + animation: fa-spin 2s infinite linear; +} + +.#{$fa-css-prefix}-pulse { + animation: fa-spin 1s infinite steps(8); +} + +@keyframes fa-spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} diff --git a/docs/style/fa/_bordered-pulled.scss b/docs/style/fa/_bordered-pulled.scss new file mode 100644 index 000000000..c8c4274c4 --- /dev/null +++ b/docs/style/fa/_bordered-pulled.scss @@ -0,0 +1,20 @@ +// Bordered & Pulled +// ------------------------- + +.#{$fa-css-prefix}-border { + border: solid .08em $fa-border-color; + border-radius: .1em; + padding: .2em .25em .15em; +} + +.#{$fa-css-prefix}-pull-left { float: left; } +.#{$fa-css-prefix}-pull-right { float: right; } + +.#{$fa-css-prefix}, +.fas, +.far, +.fal, +.fab { + &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } + &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } +} diff --git a/docs/style/fa/_core.scss b/docs/style/fa/_core.scss new file mode 100644 index 000000000..7fd37f855 --- /dev/null +++ b/docs/style/fa/_core.scss @@ -0,0 +1,16 @@ +// Base Class Definition +// ------------------------- + +.#{$fa-css-prefix}, +.fas, +.far, +.fal, +.fab { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + line-height: 1; +} diff --git a/docs/style/fa/_fixed-width.scss b/docs/style/fa/_fixed-width.scss new file mode 100644 index 000000000..5b33eb49a --- /dev/null +++ b/docs/style/fa/_fixed-width.scss @@ -0,0 +1,6 @@ +// Fixed Width Icons +// ------------------------- +.#{$fa-css-prefix}-fw { + text-align: center; + width: (20em / 16); +} diff --git a/docs/style/fa/_icons.scss b/docs/style/fa/_icons.scss new file mode 100644 index 000000000..bfd2b4694 --- /dev/null +++ b/docs/style/fa/_icons.scss @@ -0,0 +1,792 @@ +/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen +readers do not read off random characters that represent icons */ + +.#{$fa-css-prefix}-500px:before { content: fa-content($fa-var-500px); } +.#{$fa-css-prefix}-accessible-icon:before { content: fa-content($fa-var-accessible-icon); } +.#{$fa-css-prefix}-accusoft:before { content: fa-content($fa-var-accusoft); } +.#{$fa-css-prefix}-address-book:before { content: fa-content($fa-var-address-book); } +.#{$fa-css-prefix}-address-card:before { content: fa-content($fa-var-address-card); } +.#{$fa-css-prefix}-adjust:before { content: fa-content($fa-var-adjust); } +.#{$fa-css-prefix}-adn:before { content: fa-content($fa-var-adn); } +.#{$fa-css-prefix}-adversal:before { content: fa-content($fa-var-adversal); } +.#{$fa-css-prefix}-affiliatetheme:before { content: fa-content($fa-var-affiliatetheme); } +.#{$fa-css-prefix}-algolia:before { content: fa-content($fa-var-algolia); } +.#{$fa-css-prefix}-align-center:before { content: fa-content($fa-var-align-center); } +.#{$fa-css-prefix}-align-justify:before { content: fa-content($fa-var-align-justify); } +.#{$fa-css-prefix}-align-left:before { content: fa-content($fa-var-align-left); } +.#{$fa-css-prefix}-align-right:before { content: fa-content($fa-var-align-right); } +.#{$fa-css-prefix}-amazon:before { content: fa-content($fa-var-amazon); } +.#{$fa-css-prefix}-amazon-pay:before { content: fa-content($fa-var-amazon-pay); } +.#{$fa-css-prefix}-ambulance:before { content: fa-content($fa-var-ambulance); } +.#{$fa-css-prefix}-american-sign-language-interpreting:before { content: fa-content($fa-var-american-sign-language-interpreting); } +.#{$fa-css-prefix}-amilia:before { content: fa-content($fa-var-amilia); } +.#{$fa-css-prefix}-anchor:before { content: fa-content($fa-var-anchor); } +.#{$fa-css-prefix}-android:before { content: fa-content($fa-var-android); } +.#{$fa-css-prefix}-angellist:before { content: fa-content($fa-var-angellist); } +.#{$fa-css-prefix}-angle-double-down:before { content: fa-content($fa-var-angle-double-down); } +.#{$fa-css-prefix}-angle-double-left:before { content: fa-content($fa-var-angle-double-left); } +.#{$fa-css-prefix}-angle-double-right:before { content: fa-content($fa-var-angle-double-right); } +.#{$fa-css-prefix}-angle-double-up:before { content: fa-content($fa-var-angle-double-up); } +.#{$fa-css-prefix}-angle-down:before { content: fa-content($fa-var-angle-down); } +.#{$fa-css-prefix}-angle-left:before { content: fa-content($fa-var-angle-left); } +.#{$fa-css-prefix}-angle-right:before { content: fa-content($fa-var-angle-right); } +.#{$fa-css-prefix}-angle-up:before { content: fa-content($fa-var-angle-up); } +.#{$fa-css-prefix}-angrycreative:before { content: fa-content($fa-var-angrycreative); } +.#{$fa-css-prefix}-angular:before { content: fa-content($fa-var-angular); } +.#{$fa-css-prefix}-app-store:before { content: fa-content($fa-var-app-store); } +.#{$fa-css-prefix}-app-store-ios:before { content: fa-content($fa-var-app-store-ios); } +.#{$fa-css-prefix}-apper:before { content: fa-content($fa-var-apper); } +.#{$fa-css-prefix}-apple:before { content: fa-content($fa-var-apple); } +.#{$fa-css-prefix}-apple-pay:before { content: fa-content($fa-var-apple-pay); } +.#{$fa-css-prefix}-archive:before { content: fa-content($fa-var-archive); } +.#{$fa-css-prefix}-arrow-alt-circle-down:before { content: fa-content($fa-var-arrow-alt-circle-down); } +.#{$fa-css-prefix}-arrow-alt-circle-left:before { content: fa-content($fa-var-arrow-alt-circle-left); } +.#{$fa-css-prefix}-arrow-alt-circle-right:before { content: fa-content($fa-var-arrow-alt-circle-right); } +.#{$fa-css-prefix}-arrow-alt-circle-up:before { content: fa-content($fa-var-arrow-alt-circle-up); } +.#{$fa-css-prefix}-arrow-circle-down:before { content: fa-content($fa-var-arrow-circle-down); } +.#{$fa-css-prefix}-arrow-circle-left:before { content: fa-content($fa-var-arrow-circle-left); } +.#{$fa-css-prefix}-arrow-circle-right:before { content: fa-content($fa-var-arrow-circle-right); } +.#{$fa-css-prefix}-arrow-circle-up:before { content: fa-content($fa-var-arrow-circle-up); } +.#{$fa-css-prefix}-arrow-down:before { content: fa-content($fa-var-arrow-down); } +.#{$fa-css-prefix}-arrow-left:before { content: fa-content($fa-var-arrow-left); } +.#{$fa-css-prefix}-arrow-right:before { content: fa-content($fa-var-arrow-right); } +.#{$fa-css-prefix}-arrow-up:before { content: fa-content($fa-var-arrow-up); } +.#{$fa-css-prefix}-arrows-alt:before { content: fa-content($fa-var-arrows-alt); } +.#{$fa-css-prefix}-arrows-alt-h:before { content: fa-content($fa-var-arrows-alt-h); } +.#{$fa-css-prefix}-arrows-alt-v:before { content: fa-content($fa-var-arrows-alt-v); } +.#{$fa-css-prefix}-assistive-listening-systems:before { content: fa-content($fa-var-assistive-listening-systems); } +.#{$fa-css-prefix}-asterisk:before { content: fa-content($fa-var-asterisk); } +.#{$fa-css-prefix}-asymmetrik:before { content: fa-content($fa-var-asymmetrik); } +.#{$fa-css-prefix}-at:before { content: fa-content($fa-var-at); } +.#{$fa-css-prefix}-audible:before { content: fa-content($fa-var-audible); } +.#{$fa-css-prefix}-audio-description:before { content: fa-content($fa-var-audio-description); } +.#{$fa-css-prefix}-autoprefixer:before { content: fa-content($fa-var-autoprefixer); } +.#{$fa-css-prefix}-avianex:before { content: fa-content($fa-var-avianex); } +.#{$fa-css-prefix}-aviato:before { content: fa-content($fa-var-aviato); } +.#{$fa-css-prefix}-aws:before { content: fa-content($fa-var-aws); } +.#{$fa-css-prefix}-backward:before { content: fa-content($fa-var-backward); } +.#{$fa-css-prefix}-balance-scale:before { content: fa-content($fa-var-balance-scale); } +.#{$fa-css-prefix}-ban:before { content: fa-content($fa-var-ban); } +.#{$fa-css-prefix}-bandcamp:before { content: fa-content($fa-var-bandcamp); } +.#{$fa-css-prefix}-barcode:before { content: fa-content($fa-var-barcode); } +.#{$fa-css-prefix}-bars:before { content: fa-content($fa-var-bars); } +.#{$fa-css-prefix}-bath:before { content: fa-content($fa-var-bath); } +.#{$fa-css-prefix}-battery-empty:before { content: fa-content($fa-var-battery-empty); } +.#{$fa-css-prefix}-battery-full:before { content: fa-content($fa-var-battery-full); } +.#{$fa-css-prefix}-battery-half:before { content: fa-content($fa-var-battery-half); } +.#{$fa-css-prefix}-battery-quarter:before { content: fa-content($fa-var-battery-quarter); } +.#{$fa-css-prefix}-battery-three-quarters:before { content: fa-content($fa-var-battery-three-quarters); } +.#{$fa-css-prefix}-bed:before { content: fa-content($fa-var-bed); } +.#{$fa-css-prefix}-beer:before { content: fa-content($fa-var-beer); } +.#{$fa-css-prefix}-behance:before { content: fa-content($fa-var-behance); } +.#{$fa-css-prefix}-behance-square:before { content: fa-content($fa-var-behance-square); } +.#{$fa-css-prefix}-bell:before { content: fa-content($fa-var-bell); } +.#{$fa-css-prefix}-bell-slash:before { content: fa-content($fa-var-bell-slash); } +.#{$fa-css-prefix}-bicycle:before { content: fa-content($fa-var-bicycle); } +.#{$fa-css-prefix}-bimobject:before { content: fa-content($fa-var-bimobject); } +.#{$fa-css-prefix}-binoculars:before { content: fa-content($fa-var-binoculars); } +.#{$fa-css-prefix}-birthday-cake:before { content: fa-content($fa-var-birthday-cake); } +.#{$fa-css-prefix}-bitbucket:before { content: fa-content($fa-var-bitbucket); } +.#{$fa-css-prefix}-bitcoin:before { content: fa-content($fa-var-bitcoin); } +.#{$fa-css-prefix}-bity:before { content: fa-content($fa-var-bity); } +.#{$fa-css-prefix}-black-tie:before { content: fa-content($fa-var-black-tie); } +.#{$fa-css-prefix}-blackberry:before { content: fa-content($fa-var-blackberry); } +.#{$fa-css-prefix}-blind:before { content: fa-content($fa-var-blind); } +.#{$fa-css-prefix}-blogger:before { content: fa-content($fa-var-blogger); } +.#{$fa-css-prefix}-blogger-b:before { content: fa-content($fa-var-blogger-b); } +.#{$fa-css-prefix}-bluetooth:before { content: fa-content($fa-var-bluetooth); } +.#{$fa-css-prefix}-bluetooth-b:before { content: fa-content($fa-var-bluetooth-b); } +.#{$fa-css-prefix}-bold:before { content: fa-content($fa-var-bold); } +.#{$fa-css-prefix}-bolt:before { content: fa-content($fa-var-bolt); } +.#{$fa-css-prefix}-bomb:before { content: fa-content($fa-var-bomb); } +.#{$fa-css-prefix}-book:before { content: fa-content($fa-var-book); } +.#{$fa-css-prefix}-bookmark:before { content: fa-content($fa-var-bookmark); } +.#{$fa-css-prefix}-braille:before { content: fa-content($fa-var-braille); } +.#{$fa-css-prefix}-briefcase:before { content: fa-content($fa-var-briefcase); } +.#{$fa-css-prefix}-btc:before { content: fa-content($fa-var-btc); } +.#{$fa-css-prefix}-bug:before { content: fa-content($fa-var-bug); } +.#{$fa-css-prefix}-building:before { content: fa-content($fa-var-building); } +.#{$fa-css-prefix}-bullhorn:before { content: fa-content($fa-var-bullhorn); } +.#{$fa-css-prefix}-bullseye:before { content: fa-content($fa-var-bullseye); } +.#{$fa-css-prefix}-buromobelexperte:before { content: fa-content($fa-var-buromobelexperte); } +.#{$fa-css-prefix}-bus:before { content: fa-content($fa-var-bus); } +.#{$fa-css-prefix}-buysellads:before { content: fa-content($fa-var-buysellads); } +.#{$fa-css-prefix}-calculator:before { content: fa-content($fa-var-calculator); } +.#{$fa-css-prefix}-calendar:before { content: fa-content($fa-var-calendar); } +.#{$fa-css-prefix}-calendar-alt:before { content: fa-content($fa-var-calendar-alt); } +.#{$fa-css-prefix}-calendar-check:before { content: fa-content($fa-var-calendar-check); } +.#{$fa-css-prefix}-calendar-minus:before { content: fa-content($fa-var-calendar-minus); } +.#{$fa-css-prefix}-calendar-plus:before { content: fa-content($fa-var-calendar-plus); } +.#{$fa-css-prefix}-calendar-times:before { content: fa-content($fa-var-calendar-times); } +.#{$fa-css-prefix}-camera:before { content: fa-content($fa-var-camera); } +.#{$fa-css-prefix}-camera-retro:before { content: fa-content($fa-var-camera-retro); } +.#{$fa-css-prefix}-car:before { content: fa-content($fa-var-car); } +.#{$fa-css-prefix}-caret-down:before { content: fa-content($fa-var-caret-down); } +.#{$fa-css-prefix}-caret-left:before { content: fa-content($fa-var-caret-left); } +.#{$fa-css-prefix}-caret-right:before { content: fa-content($fa-var-caret-right); } +.#{$fa-css-prefix}-caret-square-down:before { content: fa-content($fa-var-caret-square-down); } +.#{$fa-css-prefix}-caret-square-left:before { content: fa-content($fa-var-caret-square-left); } +.#{$fa-css-prefix}-caret-square-right:before { content: fa-content($fa-var-caret-square-right); } +.#{$fa-css-prefix}-caret-square-up:before { content: fa-content($fa-var-caret-square-up); } +.#{$fa-css-prefix}-caret-up:before { content: fa-content($fa-var-caret-up); } +.#{$fa-css-prefix}-cart-arrow-down:before { content: fa-content($fa-var-cart-arrow-down); } +.#{$fa-css-prefix}-cart-plus:before { content: fa-content($fa-var-cart-plus); } +.#{$fa-css-prefix}-cc-amazon-pay:before { content: fa-content($fa-var-cc-amazon-pay); } +.#{$fa-css-prefix}-cc-amex:before { content: fa-content($fa-var-cc-amex); } +.#{$fa-css-prefix}-cc-apple-pay:before { content: fa-content($fa-var-cc-apple-pay); } +.#{$fa-css-prefix}-cc-diners-club:before { content: fa-content($fa-var-cc-diners-club); } +.#{$fa-css-prefix}-cc-discover:before { content: fa-content($fa-var-cc-discover); } +.#{$fa-css-prefix}-cc-jcb:before { content: fa-content($fa-var-cc-jcb); } +.#{$fa-css-prefix}-cc-mastercard:before { content: fa-content($fa-var-cc-mastercard); } +.#{$fa-css-prefix}-cc-paypal:before { content: fa-content($fa-var-cc-paypal); } +.#{$fa-css-prefix}-cc-stripe:before { content: fa-content($fa-var-cc-stripe); } +.#{$fa-css-prefix}-cc-visa:before { content: fa-content($fa-var-cc-visa); } +.#{$fa-css-prefix}-centercode:before { content: fa-content($fa-var-centercode); } +.#{$fa-css-prefix}-certificate:before { content: fa-content($fa-var-certificate); } +.#{$fa-css-prefix}-chart-area:before { content: fa-content($fa-var-chart-area); } +.#{$fa-css-prefix}-chart-bar:before { content: fa-content($fa-var-chart-bar); } +.#{$fa-css-prefix}-chart-line:before { content: fa-content($fa-var-chart-line); } +.#{$fa-css-prefix}-chart-pie:before { content: fa-content($fa-var-chart-pie); } +.#{$fa-css-prefix}-check:before { content: fa-content($fa-var-check); } +.#{$fa-css-prefix}-check-circle:before { content: fa-content($fa-var-check-circle); } +.#{$fa-css-prefix}-check-square:before { content: fa-content($fa-var-check-square); } +.#{$fa-css-prefix}-chevron-circle-down:before { content: fa-content($fa-var-chevron-circle-down); } +.#{$fa-css-prefix}-chevron-circle-left:before { content: fa-content($fa-var-chevron-circle-left); } +.#{$fa-css-prefix}-chevron-circle-right:before { content: fa-content($fa-var-chevron-circle-right); } +.#{$fa-css-prefix}-chevron-circle-up:before { content: fa-content($fa-var-chevron-circle-up); } +.#{$fa-css-prefix}-chevron-down:before { content: fa-content($fa-var-chevron-down); } +.#{$fa-css-prefix}-chevron-left:before { content: fa-content($fa-var-chevron-left); } +.#{$fa-css-prefix}-chevron-right:before { content: fa-content($fa-var-chevron-right); } +.#{$fa-css-prefix}-chevron-up:before { content: fa-content($fa-var-chevron-up); } +.#{$fa-css-prefix}-child:before { content: fa-content($fa-var-child); } +.#{$fa-css-prefix}-chrome:before { content: fa-content($fa-var-chrome); } +.#{$fa-css-prefix}-circle:before { content: fa-content($fa-var-circle); } +.#{$fa-css-prefix}-circle-notch:before { content: fa-content($fa-var-circle-notch); } +.#{$fa-css-prefix}-clipboard:before { content: fa-content($fa-var-clipboard); } +.#{$fa-css-prefix}-clock:before { content: fa-content($fa-var-clock); } +.#{$fa-css-prefix}-clone:before { content: fa-content($fa-var-clone); } +.#{$fa-css-prefix}-closed-captioning:before { content: fa-content($fa-var-closed-captioning); } +.#{$fa-css-prefix}-cloud:before { content: fa-content($fa-var-cloud); } +.#{$fa-css-prefix}-cloud-download-alt:before { content: fa-content($fa-var-cloud-download-alt); } +.#{$fa-css-prefix}-cloud-upload-alt:before { content: fa-content($fa-var-cloud-upload-alt); } +.#{$fa-css-prefix}-cloudscale:before { content: fa-content($fa-var-cloudscale); } +.#{$fa-css-prefix}-cloudsmith:before { content: fa-content($fa-var-cloudsmith); } +.#{$fa-css-prefix}-cloudversify:before { content: fa-content($fa-var-cloudversify); } +.#{$fa-css-prefix}-code:before { content: fa-content($fa-var-code); } +.#{$fa-css-prefix}-code-branch:before { content: fa-content($fa-var-code-branch); } +.#{$fa-css-prefix}-codepen:before { content: fa-content($fa-var-codepen); } +.#{$fa-css-prefix}-codiepie:before { content: fa-content($fa-var-codiepie); } +.#{$fa-css-prefix}-coffee:before { content: fa-content($fa-var-coffee); } +.#{$fa-css-prefix}-cog:before { content: fa-content($fa-var-cog); } +.#{$fa-css-prefix}-cogs:before { content: fa-content($fa-var-cogs); } +.#{$fa-css-prefix}-columns:before { content: fa-content($fa-var-columns); } +.#{$fa-css-prefix}-comment:before { content: fa-content($fa-var-comment); } +.#{$fa-css-prefix}-comment-alt:before { content: fa-content($fa-var-comment-alt); } +.#{$fa-css-prefix}-comments:before { content: fa-content($fa-var-comments); } +.#{$fa-css-prefix}-compass:before { content: fa-content($fa-var-compass); } +.#{$fa-css-prefix}-compress:before { content: fa-content($fa-var-compress); } +.#{$fa-css-prefix}-connectdevelop:before { content: fa-content($fa-var-connectdevelop); } +.#{$fa-css-prefix}-contao:before { content: fa-content($fa-var-contao); } +.#{$fa-css-prefix}-copy:before { content: fa-content($fa-var-copy); } +.#{$fa-css-prefix}-copyright:before { content: fa-content($fa-var-copyright); } +.#{$fa-css-prefix}-cpanel:before { content: fa-content($fa-var-cpanel); } +.#{$fa-css-prefix}-creative-commons:before { content: fa-content($fa-var-creative-commons); } +.#{$fa-css-prefix}-credit-card:before { content: fa-content($fa-var-credit-card); } +.#{$fa-css-prefix}-crop:before { content: fa-content($fa-var-crop); } +.#{$fa-css-prefix}-crosshairs:before { content: fa-content($fa-var-crosshairs); } +.#{$fa-css-prefix}-css3:before { content: fa-content($fa-var-css3); } +.#{$fa-css-prefix}-css3-alt:before { content: fa-content($fa-var-css3-alt); } +.#{$fa-css-prefix}-cube:before { content: fa-content($fa-var-cube); } +.#{$fa-css-prefix}-cubes:before { content: fa-content($fa-var-cubes); } +.#{$fa-css-prefix}-cut:before { content: fa-content($fa-var-cut); } +.#{$fa-css-prefix}-cuttlefish:before { content: fa-content($fa-var-cuttlefish); } +.#{$fa-css-prefix}-d-and-d:before { content: fa-content($fa-var-d-and-d); } +.#{$fa-css-prefix}-dashcube:before { content: fa-content($fa-var-dashcube); } +.#{$fa-css-prefix}-database:before { content: fa-content($fa-var-database); } +.#{$fa-css-prefix}-deaf:before { content: fa-content($fa-var-deaf); } +.#{$fa-css-prefix}-delicious:before { content: fa-content($fa-var-delicious); } +.#{$fa-css-prefix}-deploydog:before { content: fa-content($fa-var-deploydog); } +.#{$fa-css-prefix}-deskpro:before { content: fa-content($fa-var-deskpro); } +.#{$fa-css-prefix}-desktop:before { content: fa-content($fa-var-desktop); } +.#{$fa-css-prefix}-deviantart:before { content: fa-content($fa-var-deviantart); } +.#{$fa-css-prefix}-digg:before { content: fa-content($fa-var-digg); } +.#{$fa-css-prefix}-digital-ocean:before { content: fa-content($fa-var-digital-ocean); } +.#{$fa-css-prefix}-discord:before { content: fa-content($fa-var-discord); } +.#{$fa-css-prefix}-discourse:before { content: fa-content($fa-var-discourse); } +.#{$fa-css-prefix}-dochub:before { content: fa-content($fa-var-dochub); } +.#{$fa-css-prefix}-docker:before { content: fa-content($fa-var-docker); } +.#{$fa-css-prefix}-dollar-sign:before { content: fa-content($fa-var-dollar-sign); } +.#{$fa-css-prefix}-dot-circle:before { content: fa-content($fa-var-dot-circle); } +.#{$fa-css-prefix}-download:before { content: fa-content($fa-var-download); } +.#{$fa-css-prefix}-draft2digital:before { content: fa-content($fa-var-draft2digital); } +.#{$fa-css-prefix}-dribbble:before { content: fa-content($fa-var-dribbble); } +.#{$fa-css-prefix}-dribbble-square:before { content: fa-content($fa-var-dribbble-square); } +.#{$fa-css-prefix}-dropbox:before { content: fa-content($fa-var-dropbox); } +.#{$fa-css-prefix}-drupal:before { content: fa-content($fa-var-drupal); } +.#{$fa-css-prefix}-dyalog:before { content: fa-content($fa-var-dyalog); } +.#{$fa-css-prefix}-earlybirds:before { content: fa-content($fa-var-earlybirds); } +.#{$fa-css-prefix}-edge:before { content: fa-content($fa-var-edge); } +.#{$fa-css-prefix}-edit:before { content: fa-content($fa-var-edit); } +.#{$fa-css-prefix}-eject:before { content: fa-content($fa-var-eject); } +.#{$fa-css-prefix}-ellipsis-h:before { content: fa-content($fa-var-ellipsis-h); } +.#{$fa-css-prefix}-ellipsis-v:before { content: fa-content($fa-var-ellipsis-v); } +.#{$fa-css-prefix}-ember:before { content: fa-content($fa-var-ember); } +.#{$fa-css-prefix}-empire:before { content: fa-content($fa-var-empire); } +.#{$fa-css-prefix}-envelope:before { content: fa-content($fa-var-envelope); } +.#{$fa-css-prefix}-envelope-open:before { content: fa-content($fa-var-envelope-open); } +.#{$fa-css-prefix}-envelope-square:before { content: fa-content($fa-var-envelope-square); } +.#{$fa-css-prefix}-envira:before { content: fa-content($fa-var-envira); } +.#{$fa-css-prefix}-eraser:before { content: fa-content($fa-var-eraser); } +.#{$fa-css-prefix}-erlang:before { content: fa-content($fa-var-erlang); } +.#{$fa-css-prefix}-ethereum:before { content: fa-content($fa-var-ethereum); } +.#{$fa-css-prefix}-etsy:before { content: fa-content($fa-var-etsy); } +.#{$fa-css-prefix}-euro-sign:before { content: fa-content($fa-var-euro-sign); } +.#{$fa-css-prefix}-exchange-alt:before { content: fa-content($fa-var-exchange-alt); } +.#{$fa-css-prefix}-exclamation:before { content: fa-content($fa-var-exclamation); } +.#{$fa-css-prefix}-exclamation-circle:before { content: fa-content($fa-var-exclamation-circle); } +.#{$fa-css-prefix}-exclamation-triangle:before { content: fa-content($fa-var-exclamation-triangle); } +.#{$fa-css-prefix}-expand:before { content: fa-content($fa-var-expand); } +.#{$fa-css-prefix}-expand-arrows-alt:before { content: fa-content($fa-var-expand-arrows-alt); } +.#{$fa-css-prefix}-expeditedssl:before { content: fa-content($fa-var-expeditedssl); } +.#{$fa-css-prefix}-external-link-alt:before { content: fa-content($fa-var-external-link-alt); } +.#{$fa-css-prefix}-external-link-square-alt:before { content: fa-content($fa-var-external-link-square-alt); } +.#{$fa-css-prefix}-eye:before { content: fa-content($fa-var-eye); } +.#{$fa-css-prefix}-eye-dropper:before { content: fa-content($fa-var-eye-dropper); } +.#{$fa-css-prefix}-eye-slash:before { content: fa-content($fa-var-eye-slash); } +.#{$fa-css-prefix}-facebook:before { content: fa-content($fa-var-facebook); } +.#{$fa-css-prefix}-facebook-f:before { content: fa-content($fa-var-facebook-f); } +.#{$fa-css-prefix}-facebook-messenger:before { content: fa-content($fa-var-facebook-messenger); } +.#{$fa-css-prefix}-facebook-square:before { content: fa-content($fa-var-facebook-square); } +.#{$fa-css-prefix}-fast-backward:before { content: fa-content($fa-var-fast-backward); } +.#{$fa-css-prefix}-fast-forward:before { content: fa-content($fa-var-fast-forward); } +.#{$fa-css-prefix}-fax:before { content: fa-content($fa-var-fax); } +.#{$fa-css-prefix}-female:before { content: fa-content($fa-var-female); } +.#{$fa-css-prefix}-fighter-jet:before { content: fa-content($fa-var-fighter-jet); } +.#{$fa-css-prefix}-file:before { content: fa-content($fa-var-file); } +.#{$fa-css-prefix}-file-alt:before { content: fa-content($fa-var-file-alt); } +.#{$fa-css-prefix}-file-archive:before { content: fa-content($fa-var-file-archive); } +.#{$fa-css-prefix}-file-audio:before { content: fa-content($fa-var-file-audio); } +.#{$fa-css-prefix}-file-code:before { content: fa-content($fa-var-file-code); } +.#{$fa-css-prefix}-file-excel:before { content: fa-content($fa-var-file-excel); } +.#{$fa-css-prefix}-file-image:before { content: fa-content($fa-var-file-image); } +.#{$fa-css-prefix}-file-pdf:before { content: fa-content($fa-var-file-pdf); } +.#{$fa-css-prefix}-file-powerpoint:before { content: fa-content($fa-var-file-powerpoint); } +.#{$fa-css-prefix}-file-video:before { content: fa-content($fa-var-file-video); } +.#{$fa-css-prefix}-file-word:before { content: fa-content($fa-var-file-word); } +.#{$fa-css-prefix}-film:before { content: fa-content($fa-var-film); } +.#{$fa-css-prefix}-filter:before { content: fa-content($fa-var-filter); } +.#{$fa-css-prefix}-fire:before { content: fa-content($fa-var-fire); } +.#{$fa-css-prefix}-fire-extinguisher:before { content: fa-content($fa-var-fire-extinguisher); } +.#{$fa-css-prefix}-firefox:before { content: fa-content($fa-var-firefox); } +.#{$fa-css-prefix}-first-order:before { content: fa-content($fa-var-first-order); } +.#{$fa-css-prefix}-firstdraft:before { content: fa-content($fa-var-firstdraft); } +.#{$fa-css-prefix}-flag:before { content: fa-content($fa-var-flag); } +.#{$fa-css-prefix}-flag-checkered:before { content: fa-content($fa-var-flag-checkered); } +.#{$fa-css-prefix}-flask:before { content: fa-content($fa-var-flask); } +.#{$fa-css-prefix}-flickr:before { content: fa-content($fa-var-flickr); } +.#{$fa-css-prefix}-fly:before { content: fa-content($fa-var-fly); } +.#{$fa-css-prefix}-folder:before { content: fa-content($fa-var-folder); } +.#{$fa-css-prefix}-folder-open:before { content: fa-content($fa-var-folder-open); } +.#{$fa-css-prefix}-font:before { content: fa-content($fa-var-font); } +.#{$fa-css-prefix}-font-awesome:before { content: fa-content($fa-var-font-awesome); } +.#{$fa-css-prefix}-font-awesome-alt:before { content: fa-content($fa-var-font-awesome-alt); } +.#{$fa-css-prefix}-font-awesome-flag:before { content: fa-content($fa-var-font-awesome-flag); } +.#{$fa-css-prefix}-fonticons:before { content: fa-content($fa-var-fonticons); } +.#{$fa-css-prefix}-fonticons-fi:before { content: fa-content($fa-var-fonticons-fi); } +.#{$fa-css-prefix}-fort-awesome:before { content: fa-content($fa-var-fort-awesome); } +.#{$fa-css-prefix}-fort-awesome-alt:before { content: fa-content($fa-var-fort-awesome-alt); } +.#{$fa-css-prefix}-forumbee:before { content: fa-content($fa-var-forumbee); } +.#{$fa-css-prefix}-forward:before { content: fa-content($fa-var-forward); } +.#{$fa-css-prefix}-foursquare:before { content: fa-content($fa-var-foursquare); } +.#{$fa-css-prefix}-free-code-camp:before { content: fa-content($fa-var-free-code-camp); } +.#{$fa-css-prefix}-freebsd:before { content: fa-content($fa-var-freebsd); } +.#{$fa-css-prefix}-frown:before { content: fa-content($fa-var-frown); } +.#{$fa-css-prefix}-futbol:before { content: fa-content($fa-var-futbol); } +.#{$fa-css-prefix}-gamepad:before { content: fa-content($fa-var-gamepad); } +.#{$fa-css-prefix}-gavel:before { content: fa-content($fa-var-gavel); } +.#{$fa-css-prefix}-gem:before { content: fa-content($fa-var-gem); } +.#{$fa-css-prefix}-genderless:before { content: fa-content($fa-var-genderless); } +.#{$fa-css-prefix}-get-pocket:before { content: fa-content($fa-var-get-pocket); } +.#{$fa-css-prefix}-gg:before { content: fa-content($fa-var-gg); } +.#{$fa-css-prefix}-gg-circle:before { content: fa-content($fa-var-gg-circle); } +.#{$fa-css-prefix}-gift:before { content: fa-content($fa-var-gift); } +.#{$fa-css-prefix}-git:before { content: fa-content($fa-var-git); } +.#{$fa-css-prefix}-git-square:before { content: fa-content($fa-var-git-square); } +.#{$fa-css-prefix}-github:before { content: fa-content($fa-var-github); } +.#{$fa-css-prefix}-github-alt:before { content: fa-content($fa-var-github-alt); } +.#{$fa-css-prefix}-github-square:before { content: fa-content($fa-var-github-square); } +.#{$fa-css-prefix}-gitkraken:before { content: fa-content($fa-var-gitkraken); } +.#{$fa-css-prefix}-gitlab:before { content: fa-content($fa-var-gitlab); } +.#{$fa-css-prefix}-gitter:before { content: fa-content($fa-var-gitter); } +.#{$fa-css-prefix}-glass-martini:before { content: fa-content($fa-var-glass-martini); } +.#{$fa-css-prefix}-glide:before { content: fa-content($fa-var-glide); } +.#{$fa-css-prefix}-glide-g:before { content: fa-content($fa-var-glide-g); } +.#{$fa-css-prefix}-globe:before { content: fa-content($fa-var-globe); } +.#{$fa-css-prefix}-gofore:before { content: fa-content($fa-var-gofore); } +.#{$fa-css-prefix}-goodreads:before { content: fa-content($fa-var-goodreads); } +.#{$fa-css-prefix}-goodreads-g:before { content: fa-content($fa-var-goodreads-g); } +.#{$fa-css-prefix}-google:before { content: fa-content($fa-var-google); } +.#{$fa-css-prefix}-google-drive:before { content: fa-content($fa-var-google-drive); } +.#{$fa-css-prefix}-google-play:before { content: fa-content($fa-var-google-play); } +.#{$fa-css-prefix}-google-plus:before { content: fa-content($fa-var-google-plus); } +.#{$fa-css-prefix}-google-plus-g:before { content: fa-content($fa-var-google-plus-g); } +.#{$fa-css-prefix}-google-plus-square:before { content: fa-content($fa-var-google-plus-square); } +.#{$fa-css-prefix}-google-wallet:before { content: fa-content($fa-var-google-wallet); } +.#{$fa-css-prefix}-graduation-cap:before { content: fa-content($fa-var-graduation-cap); } +.#{$fa-css-prefix}-gratipay:before { content: fa-content($fa-var-gratipay); } +.#{$fa-css-prefix}-grav:before { content: fa-content($fa-var-grav); } +.#{$fa-css-prefix}-gripfire:before { content: fa-content($fa-var-gripfire); } +.#{$fa-css-prefix}-grunt:before { content: fa-content($fa-var-grunt); } +.#{$fa-css-prefix}-gulp:before { content: fa-content($fa-var-gulp); } +.#{$fa-css-prefix}-h-square:before { content: fa-content($fa-var-h-square); } +.#{$fa-css-prefix}-hacker-news:before { content: fa-content($fa-var-hacker-news); } +.#{$fa-css-prefix}-hacker-news-square:before { content: fa-content($fa-var-hacker-news-square); } +.#{$fa-css-prefix}-hand-lizard:before { content: fa-content($fa-var-hand-lizard); } +.#{$fa-css-prefix}-hand-paper:before { content: fa-content($fa-var-hand-paper); } +.#{$fa-css-prefix}-hand-peace:before { content: fa-content($fa-var-hand-peace); } +.#{$fa-css-prefix}-hand-point-down:before { content: fa-content($fa-var-hand-point-down); } +.#{$fa-css-prefix}-hand-point-left:before { content: fa-content($fa-var-hand-point-left); } +.#{$fa-css-prefix}-hand-point-right:before { content: fa-content($fa-var-hand-point-right); } +.#{$fa-css-prefix}-hand-point-up:before { content: fa-content($fa-var-hand-point-up); } +.#{$fa-css-prefix}-hand-pointer:before { content: fa-content($fa-var-hand-pointer); } +.#{$fa-css-prefix}-hand-rock:before { content: fa-content($fa-var-hand-rock); } +.#{$fa-css-prefix}-hand-scissors:before { content: fa-content($fa-var-hand-scissors); } +.#{$fa-css-prefix}-hand-spock:before { content: fa-content($fa-var-hand-spock); } +.#{$fa-css-prefix}-handshake:before { content: fa-content($fa-var-handshake); } +.#{$fa-css-prefix}-hashtag:before { content: fa-content($fa-var-hashtag); } +.#{$fa-css-prefix}-hdd:before { content: fa-content($fa-var-hdd); } +.#{$fa-css-prefix}-heading:before { content: fa-content($fa-var-heading); } +.#{$fa-css-prefix}-headphones:before { content: fa-content($fa-var-headphones); } +.#{$fa-css-prefix}-heart:before { content: fa-content($fa-var-heart); } +.#{$fa-css-prefix}-heartbeat:before { content: fa-content($fa-var-heartbeat); } +.#{$fa-css-prefix}-hire-a-helper:before { content: fa-content($fa-var-hire-a-helper); } +.#{$fa-css-prefix}-history:before { content: fa-content($fa-var-history); } +.#{$fa-css-prefix}-home:before { content: fa-content($fa-var-home); } +.#{$fa-css-prefix}-hooli:before { content: fa-content($fa-var-hooli); } +.#{$fa-css-prefix}-hospital:before { content: fa-content($fa-var-hospital); } +.#{$fa-css-prefix}-hotjar:before { content: fa-content($fa-var-hotjar); } +.#{$fa-css-prefix}-hourglass:before { content: fa-content($fa-var-hourglass); } +.#{$fa-css-prefix}-hourglass-end:before { content: fa-content($fa-var-hourglass-end); } +.#{$fa-css-prefix}-hourglass-half:before { content: fa-content($fa-var-hourglass-half); } +.#{$fa-css-prefix}-hourglass-start:before { content: fa-content($fa-var-hourglass-start); } +.#{$fa-css-prefix}-houzz:before { content: fa-content($fa-var-houzz); } +.#{$fa-css-prefix}-html5:before { content: fa-content($fa-var-html5); } +.#{$fa-css-prefix}-hubspot:before { content: fa-content($fa-var-hubspot); } +.#{$fa-css-prefix}-i-cursor:before { content: fa-content($fa-var-i-cursor); } +.#{$fa-css-prefix}-id-badge:before { content: fa-content($fa-var-id-badge); } +.#{$fa-css-prefix}-id-card:before { content: fa-content($fa-var-id-card); } +.#{$fa-css-prefix}-image:before { content: fa-content($fa-var-image); } +.#{$fa-css-prefix}-images:before { content: fa-content($fa-var-images); } +.#{$fa-css-prefix}-imdb:before { content: fa-content($fa-var-imdb); } +.#{$fa-css-prefix}-inbox:before { content: fa-content($fa-var-inbox); } +.#{$fa-css-prefix}-indent:before { content: fa-content($fa-var-indent); } +.#{$fa-css-prefix}-industry:before { content: fa-content($fa-var-industry); } +.#{$fa-css-prefix}-info:before { content: fa-content($fa-var-info); } +.#{$fa-css-prefix}-info-circle:before { content: fa-content($fa-var-info-circle); } +.#{$fa-css-prefix}-instagram:before { content: fa-content($fa-var-instagram); } +.#{$fa-css-prefix}-internet-explorer:before { content: fa-content($fa-var-internet-explorer); } +.#{$fa-css-prefix}-ioxhost:before { content: fa-content($fa-var-ioxhost); } +.#{$fa-css-prefix}-italic:before { content: fa-content($fa-var-italic); } +.#{$fa-css-prefix}-itunes:before { content: fa-content($fa-var-itunes); } +.#{$fa-css-prefix}-itunes-note:before { content: fa-content($fa-var-itunes-note); } +.#{$fa-css-prefix}-jenkins:before { content: fa-content($fa-var-jenkins); } +.#{$fa-css-prefix}-joget:before { content: fa-content($fa-var-joget); } +.#{$fa-css-prefix}-joomla:before { content: fa-content($fa-var-joomla); } +.#{$fa-css-prefix}-js:before { content: fa-content($fa-var-js); } +.#{$fa-css-prefix}-js-square:before { content: fa-content($fa-var-js-square); } +.#{$fa-css-prefix}-jsfiddle:before { content: fa-content($fa-var-jsfiddle); } +.#{$fa-css-prefix}-key:before { content: fa-content($fa-var-key); } +.#{$fa-css-prefix}-keyboard:before { content: fa-content($fa-var-keyboard); } +.#{$fa-css-prefix}-keycdn:before { content: fa-content($fa-var-keycdn); } +.#{$fa-css-prefix}-kickstarter:before { content: fa-content($fa-var-kickstarter); } +.#{$fa-css-prefix}-kickstarter-k:before { content: fa-content($fa-var-kickstarter-k); } +.#{$fa-css-prefix}-korvue:before { content: fa-content($fa-var-korvue); } +.#{$fa-css-prefix}-language:before { content: fa-content($fa-var-language); } +.#{$fa-css-prefix}-laptop:before { content: fa-content($fa-var-laptop); } +.#{$fa-css-prefix}-laravel:before { content: fa-content($fa-var-laravel); } +.#{$fa-css-prefix}-lastfm:before { content: fa-content($fa-var-lastfm); } +.#{$fa-css-prefix}-lastfm-square:before { content: fa-content($fa-var-lastfm-square); } +.#{$fa-css-prefix}-leaf:before { content: fa-content($fa-var-leaf); } +.#{$fa-css-prefix}-leanpub:before { content: fa-content($fa-var-leanpub); } +.#{$fa-css-prefix}-lemon:before { content: fa-content($fa-var-lemon); } +.#{$fa-css-prefix}-less:before { content: fa-content($fa-var-less); } +.#{$fa-css-prefix}-level-down-alt:before { content: fa-content($fa-var-level-down-alt); } +.#{$fa-css-prefix}-level-up-alt:before { content: fa-content($fa-var-level-up-alt); } +.#{$fa-css-prefix}-life-ring:before { content: fa-content($fa-var-life-ring); } +.#{$fa-css-prefix}-lightbulb:before { content: fa-content($fa-var-lightbulb); } +.#{$fa-css-prefix}-line:before { content: fa-content($fa-var-line); } +.#{$fa-css-prefix}-link:before { content: fa-content($fa-var-link); } +.#{$fa-css-prefix}-linkedin:before { content: fa-content($fa-var-linkedin); } +.#{$fa-css-prefix}-linkedin-in:before { content: fa-content($fa-var-linkedin-in); } +.#{$fa-css-prefix}-linode:before { content: fa-content($fa-var-linode); } +.#{$fa-css-prefix}-linux:before { content: fa-content($fa-var-linux); } +.#{$fa-css-prefix}-lira-sign:before { content: fa-content($fa-var-lira-sign); } +.#{$fa-css-prefix}-list:before { content: fa-content($fa-var-list); } +.#{$fa-css-prefix}-list-alt:before { content: fa-content($fa-var-list-alt); } +.#{$fa-css-prefix}-list-ol:before { content: fa-content($fa-var-list-ol); } +.#{$fa-css-prefix}-list-ul:before { content: fa-content($fa-var-list-ul); } +.#{$fa-css-prefix}-location-arrow:before { content: fa-content($fa-var-location-arrow); } +.#{$fa-css-prefix}-lock:before { content: fa-content($fa-var-lock); } +.#{$fa-css-prefix}-lock-open:before { content: fa-content($fa-var-lock-open); } +.#{$fa-css-prefix}-long-arrow-alt-down:before { content: fa-content($fa-var-long-arrow-alt-down); } +.#{$fa-css-prefix}-long-arrow-alt-left:before { content: fa-content($fa-var-long-arrow-alt-left); } +.#{$fa-css-prefix}-long-arrow-alt-right:before { content: fa-content($fa-var-long-arrow-alt-right); } +.#{$fa-css-prefix}-long-arrow-alt-up:before { content: fa-content($fa-var-long-arrow-alt-up); } +.#{$fa-css-prefix}-low-vision:before { content: fa-content($fa-var-low-vision); } +.#{$fa-css-prefix}-lyft:before { content: fa-content($fa-var-lyft); } +.#{$fa-css-prefix}-magento:before { content: fa-content($fa-var-magento); } +.#{$fa-css-prefix}-magic:before { content: fa-content($fa-var-magic); } +.#{$fa-css-prefix}-magnet:before { content: fa-content($fa-var-magnet); } +.#{$fa-css-prefix}-male:before { content: fa-content($fa-var-male); } +.#{$fa-css-prefix}-map:before { content: fa-content($fa-var-map); } +.#{$fa-css-prefix}-map-marker:before { content: fa-content($fa-var-map-marker); } +.#{$fa-css-prefix}-map-marker-alt:before { content: fa-content($fa-var-map-marker-alt); } +.#{$fa-css-prefix}-map-pin:before { content: fa-content($fa-var-map-pin); } +.#{$fa-css-prefix}-map-signs:before { content: fa-content($fa-var-map-signs); } +.#{$fa-css-prefix}-mars:before { content: fa-content($fa-var-mars); } +.#{$fa-css-prefix}-mars-double:before { content: fa-content($fa-var-mars-double); } +.#{$fa-css-prefix}-mars-stroke:before { content: fa-content($fa-var-mars-stroke); } +.#{$fa-css-prefix}-mars-stroke-h:before { content: fa-content($fa-var-mars-stroke-h); } +.#{$fa-css-prefix}-mars-stroke-v:before { content: fa-content($fa-var-mars-stroke-v); } +.#{$fa-css-prefix}-maxcdn:before { content: fa-content($fa-var-maxcdn); } +.#{$fa-css-prefix}-medapps:before { content: fa-content($fa-var-medapps); } +.#{$fa-css-prefix}-medium:before { content: fa-content($fa-var-medium); } +.#{$fa-css-prefix}-medium-m:before { content: fa-content($fa-var-medium-m); } +.#{$fa-css-prefix}-medkit:before { content: fa-content($fa-var-medkit); } +.#{$fa-css-prefix}-medrt:before { content: fa-content($fa-var-medrt); } +.#{$fa-css-prefix}-meetup:before { content: fa-content($fa-var-meetup); } +.#{$fa-css-prefix}-meh:before { content: fa-content($fa-var-meh); } +.#{$fa-css-prefix}-mercury:before { content: fa-content($fa-var-mercury); } +.#{$fa-css-prefix}-microchip:before { content: fa-content($fa-var-microchip); } +.#{$fa-css-prefix}-microphone:before { content: fa-content($fa-var-microphone); } +.#{$fa-css-prefix}-microphone-slash:before { content: fa-content($fa-var-microphone-slash); } +.#{$fa-css-prefix}-microsoft:before { content: fa-content($fa-var-microsoft); } +.#{$fa-css-prefix}-minus:before { content: fa-content($fa-var-minus); } +.#{$fa-css-prefix}-minus-circle:before { content: fa-content($fa-var-minus-circle); } +.#{$fa-css-prefix}-minus-square:before { content: fa-content($fa-var-minus-square); } +.#{$fa-css-prefix}-mix:before { content: fa-content($fa-var-mix); } +.#{$fa-css-prefix}-mixcloud:before { content: fa-content($fa-var-mixcloud); } +.#{$fa-css-prefix}-mizuni:before { content: fa-content($fa-var-mizuni); } +.#{$fa-css-prefix}-mobile:before { content: fa-content($fa-var-mobile); } +.#{$fa-css-prefix}-mobile-alt:before { content: fa-content($fa-var-mobile-alt); } +.#{$fa-css-prefix}-modx:before { content: fa-content($fa-var-modx); } +.#{$fa-css-prefix}-monero:before { content: fa-content($fa-var-monero); } +.#{$fa-css-prefix}-money-bill-alt:before { content: fa-content($fa-var-money-bill-alt); } +.#{$fa-css-prefix}-moon:before { content: fa-content($fa-var-moon); } +.#{$fa-css-prefix}-motorcycle:before { content: fa-content($fa-var-motorcycle); } +.#{$fa-css-prefix}-mouse-pointer:before { content: fa-content($fa-var-mouse-pointer); } +.#{$fa-css-prefix}-music:before { content: fa-content($fa-var-music); } +.#{$fa-css-prefix}-napster:before { content: fa-content($fa-var-napster); } +.#{$fa-css-prefix}-neuter:before { content: fa-content($fa-var-neuter); } +.#{$fa-css-prefix}-newspaper:before { content: fa-content($fa-var-newspaper); } +.#{$fa-css-prefix}-nintendo-switch:before { content: fa-content($fa-var-nintendo-switch); } +.#{$fa-css-prefix}-node:before { content: fa-content($fa-var-node); } +.#{$fa-css-prefix}-node-js:before { content: fa-content($fa-var-node-js); } +.#{$fa-css-prefix}-npm:before { content: fa-content($fa-var-npm); } +.#{$fa-css-prefix}-ns8:before { content: fa-content($fa-var-ns8); } +.#{$fa-css-prefix}-nutritionix:before { content: fa-content($fa-var-nutritionix); } +.#{$fa-css-prefix}-object-group:before { content: fa-content($fa-var-object-group); } +.#{$fa-css-prefix}-object-ungroup:before { content: fa-content($fa-var-object-ungroup); } +.#{$fa-css-prefix}-odnoklassniki:before { content: fa-content($fa-var-odnoklassniki); } +.#{$fa-css-prefix}-odnoklassniki-square:before { content: fa-content($fa-var-odnoklassniki-square); } +.#{$fa-css-prefix}-opencart:before { content: fa-content($fa-var-opencart); } +.#{$fa-css-prefix}-openid:before { content: fa-content($fa-var-openid); } +.#{$fa-css-prefix}-opera:before { content: fa-content($fa-var-opera); } +.#{$fa-css-prefix}-optin-monster:before { content: fa-content($fa-var-optin-monster); } +.#{$fa-css-prefix}-osi:before { content: fa-content($fa-var-osi); } +.#{$fa-css-prefix}-outdent:before { content: fa-content($fa-var-outdent); } +.#{$fa-css-prefix}-page4:before { content: fa-content($fa-var-page4); } +.#{$fa-css-prefix}-pagelines:before { content: fa-content($fa-var-pagelines); } +.#{$fa-css-prefix}-paint-brush:before { content: fa-content($fa-var-paint-brush); } +.#{$fa-css-prefix}-palfed:before { content: fa-content($fa-var-palfed); } +.#{$fa-css-prefix}-paper-plane:before { content: fa-content($fa-var-paper-plane); } +.#{$fa-css-prefix}-paperclip:before { content: fa-content($fa-var-paperclip); } +.#{$fa-css-prefix}-paragraph:before { content: fa-content($fa-var-paragraph); } +.#{$fa-css-prefix}-paste:before { content: fa-content($fa-var-paste); } +.#{$fa-css-prefix}-patreon:before { content: fa-content($fa-var-patreon); } +.#{$fa-css-prefix}-pause:before { content: fa-content($fa-var-pause); } +.#{$fa-css-prefix}-pause-circle:before { content: fa-content($fa-var-pause-circle); } +.#{$fa-css-prefix}-paw:before { content: fa-content($fa-var-paw); } +.#{$fa-css-prefix}-paypal:before { content: fa-content($fa-var-paypal); } +.#{$fa-css-prefix}-pen-square:before { content: fa-content($fa-var-pen-square); } +.#{$fa-css-prefix}-pencil-alt:before { content: fa-content($fa-var-pencil-alt); } +.#{$fa-css-prefix}-percent:before { content: fa-content($fa-var-percent); } +.#{$fa-css-prefix}-periscope:before { content: fa-content($fa-var-periscope); } +.#{$fa-css-prefix}-phabricator:before { content: fa-content($fa-var-phabricator); } +.#{$fa-css-prefix}-phoenix-framework:before { content: fa-content($fa-var-phoenix-framework); } +.#{$fa-css-prefix}-phone:before { content: fa-content($fa-var-phone); } +.#{$fa-css-prefix}-phone-square:before { content: fa-content($fa-var-phone-square); } +.#{$fa-css-prefix}-phone-volume:before { content: fa-content($fa-var-phone-volume); } +.#{$fa-css-prefix}-pied-piper:before { content: fa-content($fa-var-pied-piper); } +.#{$fa-css-prefix}-pied-piper-alt:before { content: fa-content($fa-var-pied-piper-alt); } +.#{$fa-css-prefix}-pied-piper-pp:before { content: fa-content($fa-var-pied-piper-pp); } +.#{$fa-css-prefix}-pinterest:before { content: fa-content($fa-var-pinterest); } +.#{$fa-css-prefix}-pinterest-p:before { content: fa-content($fa-var-pinterest-p); } +.#{$fa-css-prefix}-pinterest-square:before { content: fa-content($fa-var-pinterest-square); } +.#{$fa-css-prefix}-plane:before { content: fa-content($fa-var-plane); } +.#{$fa-css-prefix}-play:before { content: fa-content($fa-var-play); } +.#{$fa-css-prefix}-play-circle:before { content: fa-content($fa-var-play-circle); } +.#{$fa-css-prefix}-playstation:before { content: fa-content($fa-var-playstation); } +.#{$fa-css-prefix}-plug:before { content: fa-content($fa-var-plug); } +.#{$fa-css-prefix}-plus:before { content: fa-content($fa-var-plus); } +.#{$fa-css-prefix}-plus-circle:before { content: fa-content($fa-var-plus-circle); } +.#{$fa-css-prefix}-plus-square:before { content: fa-content($fa-var-plus-square); } +.#{$fa-css-prefix}-podcast:before { content: fa-content($fa-var-podcast); } +.#{$fa-css-prefix}-pound-sign:before { content: fa-content($fa-var-pound-sign); } +.#{$fa-css-prefix}-power-off:before { content: fa-content($fa-var-power-off); } +.#{$fa-css-prefix}-print:before { content: fa-content($fa-var-print); } +.#{$fa-css-prefix}-product-hunt:before { content: fa-content($fa-var-product-hunt); } +.#{$fa-css-prefix}-pushed:before { content: fa-content($fa-var-pushed); } +.#{$fa-css-prefix}-puzzle-piece:before { content: fa-content($fa-var-puzzle-piece); } +.#{$fa-css-prefix}-python:before { content: fa-content($fa-var-python); } +.#{$fa-css-prefix}-qq:before { content: fa-content($fa-var-qq); } +.#{$fa-css-prefix}-qrcode:before { content: fa-content($fa-var-qrcode); } +.#{$fa-css-prefix}-question:before { content: fa-content($fa-var-question); } +.#{$fa-css-prefix}-question-circle:before { content: fa-content($fa-var-question-circle); } +.#{$fa-css-prefix}-quora:before { content: fa-content($fa-var-quora); } +.#{$fa-css-prefix}-quote-left:before { content: fa-content($fa-var-quote-left); } +.#{$fa-css-prefix}-quote-right:before { content: fa-content($fa-var-quote-right); } +.#{$fa-css-prefix}-random:before { content: fa-content($fa-var-random); } +.#{$fa-css-prefix}-ravelry:before { content: fa-content($fa-var-ravelry); } +.#{$fa-css-prefix}-react:before { content: fa-content($fa-var-react); } +.#{$fa-css-prefix}-rebel:before { content: fa-content($fa-var-rebel); } +.#{$fa-css-prefix}-recycle:before { content: fa-content($fa-var-recycle); } +.#{$fa-css-prefix}-red-river:before { content: fa-content($fa-var-red-river); } +.#{$fa-css-prefix}-reddit:before { content: fa-content($fa-var-reddit); } +.#{$fa-css-prefix}-reddit-alien:before { content: fa-content($fa-var-reddit-alien); } +.#{$fa-css-prefix}-reddit-square:before { content: fa-content($fa-var-reddit-square); } +.#{$fa-css-prefix}-redo:before { content: fa-content($fa-var-redo); } +.#{$fa-css-prefix}-redo-alt:before { content: fa-content($fa-var-redo-alt); } +.#{$fa-css-prefix}-registered:before { content: fa-content($fa-var-registered); } +.#{$fa-css-prefix}-rendact:before { content: fa-content($fa-var-rendact); } +.#{$fa-css-prefix}-renren:before { content: fa-content($fa-var-renren); } +.#{$fa-css-prefix}-reply:before { content: fa-content($fa-var-reply); } +.#{$fa-css-prefix}-reply-all:before { content: fa-content($fa-var-reply-all); } +.#{$fa-css-prefix}-replyd:before { content: fa-content($fa-var-replyd); } +.#{$fa-css-prefix}-resolving:before { content: fa-content($fa-var-resolving); } +.#{$fa-css-prefix}-retweet:before { content: fa-content($fa-var-retweet); } +.#{$fa-css-prefix}-road:before { content: fa-content($fa-var-road); } +.#{$fa-css-prefix}-rocket:before { content: fa-content($fa-var-rocket); } +.#{$fa-css-prefix}-rocketchat:before { content: fa-content($fa-var-rocketchat); } +.#{$fa-css-prefix}-rockrms:before { content: fa-content($fa-var-rockrms); } +.#{$fa-css-prefix}-rss:before { content: fa-content($fa-var-rss); } +.#{$fa-css-prefix}-rss-square:before { content: fa-content($fa-var-rss-square); } +.#{$fa-css-prefix}-ruble-sign:before { content: fa-content($fa-var-ruble-sign); } +.#{$fa-css-prefix}-rupee-sign:before { content: fa-content($fa-var-rupee-sign); } +.#{$fa-css-prefix}-safari:before { content: fa-content($fa-var-safari); } +.#{$fa-css-prefix}-sass:before { content: fa-content($fa-var-sass); } +.#{$fa-css-prefix}-save:before { content: fa-content($fa-var-save); } +.#{$fa-css-prefix}-schlix:before { content: fa-content($fa-var-schlix); } +.#{$fa-css-prefix}-scribd:before { content: fa-content($fa-var-scribd); } +.#{$fa-css-prefix}-search:before { content: fa-content($fa-var-search); } +.#{$fa-css-prefix}-search-minus:before { content: fa-content($fa-var-search-minus); } +.#{$fa-css-prefix}-search-plus:before { content: fa-content($fa-var-search-plus); } +.#{$fa-css-prefix}-searchengin:before { content: fa-content($fa-var-searchengin); } +.#{$fa-css-prefix}-sellcast:before { content: fa-content($fa-var-sellcast); } +.#{$fa-css-prefix}-sellsy:before { content: fa-content($fa-var-sellsy); } +.#{$fa-css-prefix}-server:before { content: fa-content($fa-var-server); } +.#{$fa-css-prefix}-servicestack:before { content: fa-content($fa-var-servicestack); } +.#{$fa-css-prefix}-share:before { content: fa-content($fa-var-share); } +.#{$fa-css-prefix}-share-alt:before { content: fa-content($fa-var-share-alt); } +.#{$fa-css-prefix}-share-alt-square:before { content: fa-content($fa-var-share-alt-square); } +.#{$fa-css-prefix}-share-square:before { content: fa-content($fa-var-share-square); } +.#{$fa-css-prefix}-shekel-sign:before { content: fa-content($fa-var-shekel-sign); } +.#{$fa-css-prefix}-shield-alt:before { content: fa-content($fa-var-shield-alt); } +.#{$fa-css-prefix}-ship:before { content: fa-content($fa-var-ship); } +.#{$fa-css-prefix}-shirtsinbulk:before { content: fa-content($fa-var-shirtsinbulk); } +.#{$fa-css-prefix}-shopping-bag:before { content: fa-content($fa-var-shopping-bag); } +.#{$fa-css-prefix}-shopping-basket:before { content: fa-content($fa-var-shopping-basket); } +.#{$fa-css-prefix}-shopping-cart:before { content: fa-content($fa-var-shopping-cart); } +.#{$fa-css-prefix}-shower:before { content: fa-content($fa-var-shower); } +.#{$fa-css-prefix}-sign-in-alt:before { content: fa-content($fa-var-sign-in-alt); } +.#{$fa-css-prefix}-sign-language:before { content: fa-content($fa-var-sign-language); } +.#{$fa-css-prefix}-sign-out-alt:before { content: fa-content($fa-var-sign-out-alt); } +.#{$fa-css-prefix}-signal:before { content: fa-content($fa-var-signal); } +.#{$fa-css-prefix}-simplybuilt:before { content: fa-content($fa-var-simplybuilt); } +.#{$fa-css-prefix}-sistrix:before { content: fa-content($fa-var-sistrix); } +.#{$fa-css-prefix}-sitemap:before { content: fa-content($fa-var-sitemap); } +.#{$fa-css-prefix}-skyatlas:before { content: fa-content($fa-var-skyatlas); } +.#{$fa-css-prefix}-skype:before { content: fa-content($fa-var-skype); } +.#{$fa-css-prefix}-slack:before { content: fa-content($fa-var-slack); } +.#{$fa-css-prefix}-slack-hash:before { content: fa-content($fa-var-slack-hash); } +.#{$fa-css-prefix}-sliders-h:before { content: fa-content($fa-var-sliders-h); } +.#{$fa-css-prefix}-slideshare:before { content: fa-content($fa-var-slideshare); } +.#{$fa-css-prefix}-smile:before { content: fa-content($fa-var-smile); } +.#{$fa-css-prefix}-snapchat:before { content: fa-content($fa-var-snapchat); } +.#{$fa-css-prefix}-snapchat-ghost:before { content: fa-content($fa-var-snapchat-ghost); } +.#{$fa-css-prefix}-snapchat-square:before { content: fa-content($fa-var-snapchat-square); } +.#{$fa-css-prefix}-snowflake:before { content: fa-content($fa-var-snowflake); } +.#{$fa-css-prefix}-sort:before { content: fa-content($fa-var-sort); } +.#{$fa-css-prefix}-sort-alpha-down:before { content: fa-content($fa-var-sort-alpha-down); } +.#{$fa-css-prefix}-sort-alpha-up:before { content: fa-content($fa-var-sort-alpha-up); } +.#{$fa-css-prefix}-sort-amount-down:before { content: fa-content($fa-var-sort-amount-down); } +.#{$fa-css-prefix}-sort-amount-up:before { content: fa-content($fa-var-sort-amount-up); } +.#{$fa-css-prefix}-sort-down:before { content: fa-content($fa-var-sort-down); } +.#{$fa-css-prefix}-sort-numeric-down:before { content: fa-content($fa-var-sort-numeric-down); } +.#{$fa-css-prefix}-sort-numeric-up:before { content: fa-content($fa-var-sort-numeric-up); } +.#{$fa-css-prefix}-sort-up:before { content: fa-content($fa-var-sort-up); } +.#{$fa-css-prefix}-soundcloud:before { content: fa-content($fa-var-soundcloud); } +.#{$fa-css-prefix}-space-shuttle:before { content: fa-content($fa-var-space-shuttle); } +.#{$fa-css-prefix}-speakap:before { content: fa-content($fa-var-speakap); } +.#{$fa-css-prefix}-spinner:before { content: fa-content($fa-var-spinner); } +.#{$fa-css-prefix}-spotify:before { content: fa-content($fa-var-spotify); } +.#{$fa-css-prefix}-square:before { content: fa-content($fa-var-square); } +.#{$fa-css-prefix}-stack-exchange:before { content: fa-content($fa-var-stack-exchange); } +.#{$fa-css-prefix}-stack-overflow:before { content: fa-content($fa-var-stack-overflow); } +.#{$fa-css-prefix}-star:before { content: fa-content($fa-var-star); } +.#{$fa-css-prefix}-star-half:before { content: fa-content($fa-var-star-half); } +.#{$fa-css-prefix}-staylinked:before { content: fa-content($fa-var-staylinked); } +.#{$fa-css-prefix}-steam:before { content: fa-content($fa-var-steam); } +.#{$fa-css-prefix}-steam-square:before { content: fa-content($fa-var-steam-square); } +.#{$fa-css-prefix}-steam-symbol:before { content: fa-content($fa-var-steam-symbol); } +.#{$fa-css-prefix}-step-backward:before { content: fa-content($fa-var-step-backward); } +.#{$fa-css-prefix}-step-forward:before { content: fa-content($fa-var-step-forward); } +.#{$fa-css-prefix}-stethoscope:before { content: fa-content($fa-var-stethoscope); } +.#{$fa-css-prefix}-sticker-mule:before { content: fa-content($fa-var-sticker-mule); } +.#{$fa-css-prefix}-sticky-note:before { content: fa-content($fa-var-sticky-note); } +.#{$fa-css-prefix}-stop:before { content: fa-content($fa-var-stop); } +.#{$fa-css-prefix}-stop-circle:before { content: fa-content($fa-var-stop-circle); } +.#{$fa-css-prefix}-stopwatch:before { content: fa-content($fa-var-stopwatch); } +.#{$fa-css-prefix}-strava:before { content: fa-content($fa-var-strava); } +.#{$fa-css-prefix}-street-view:before { content: fa-content($fa-var-street-view); } +.#{$fa-css-prefix}-strikethrough:before { content: fa-content($fa-var-strikethrough); } +.#{$fa-css-prefix}-stripe:before { content: fa-content($fa-var-stripe); } +.#{$fa-css-prefix}-stripe-s:before { content: fa-content($fa-var-stripe-s); } +.#{$fa-css-prefix}-studiovinari:before { content: fa-content($fa-var-studiovinari); } +.#{$fa-css-prefix}-stumbleupon:before { content: fa-content($fa-var-stumbleupon); } +.#{$fa-css-prefix}-stumbleupon-circle:before { content: fa-content($fa-var-stumbleupon-circle); } +.#{$fa-css-prefix}-subscript:before { content: fa-content($fa-var-subscript); } +.#{$fa-css-prefix}-subway:before { content: fa-content($fa-var-subway); } +.#{$fa-css-prefix}-suitcase:before { content: fa-content($fa-var-suitcase); } +.#{$fa-css-prefix}-sun:before { content: fa-content($fa-var-sun); } +.#{$fa-css-prefix}-superpowers:before { content: fa-content($fa-var-superpowers); } +.#{$fa-css-prefix}-superscript:before { content: fa-content($fa-var-superscript); } +.#{$fa-css-prefix}-supple:before { content: fa-content($fa-var-supple); } +.#{$fa-css-prefix}-sync:before { content: fa-content($fa-var-sync); } +.#{$fa-css-prefix}-sync-alt:before { content: fa-content($fa-var-sync-alt); } +.#{$fa-css-prefix}-table:before { content: fa-content($fa-var-table); } +.#{$fa-css-prefix}-tablet:before { content: fa-content($fa-var-tablet); } +.#{$fa-css-prefix}-tablet-alt:before { content: fa-content($fa-var-tablet-alt); } +.#{$fa-css-prefix}-tachometer-alt:before { content: fa-content($fa-var-tachometer-alt); } +.#{$fa-css-prefix}-tag:before { content: fa-content($fa-var-tag); } +.#{$fa-css-prefix}-tags:before { content: fa-content($fa-var-tags); } +.#{$fa-css-prefix}-tasks:before { content: fa-content($fa-var-tasks); } +.#{$fa-css-prefix}-taxi:before { content: fa-content($fa-var-taxi); } +.#{$fa-css-prefix}-telegram:before { content: fa-content($fa-var-telegram); } +.#{$fa-css-prefix}-telegram-plane:before { content: fa-content($fa-var-telegram-plane); } +.#{$fa-css-prefix}-tencent-weibo:before { content: fa-content($fa-var-tencent-weibo); } +.#{$fa-css-prefix}-terminal:before { content: fa-content($fa-var-terminal); } +.#{$fa-css-prefix}-text-height:before { content: fa-content($fa-var-text-height); } +.#{$fa-css-prefix}-text-width:before { content: fa-content($fa-var-text-width); } +.#{$fa-css-prefix}-th:before { content: fa-content($fa-var-th); } +.#{$fa-css-prefix}-th-large:before { content: fa-content($fa-var-th-large); } +.#{$fa-css-prefix}-th-list:before { content: fa-content($fa-var-th-list); } +.#{$fa-css-prefix}-themeisle:before { content: fa-content($fa-var-themeisle); } +.#{$fa-css-prefix}-thermometer-empty:before { content: fa-content($fa-var-thermometer-empty); } +.#{$fa-css-prefix}-thermometer-full:before { content: fa-content($fa-var-thermometer-full); } +.#{$fa-css-prefix}-thermometer-half:before { content: fa-content($fa-var-thermometer-half); } +.#{$fa-css-prefix}-thermometer-quarter:before { content: fa-content($fa-var-thermometer-quarter); } +.#{$fa-css-prefix}-thermometer-three-quarters:before { content: fa-content($fa-var-thermometer-three-quarters); } +.#{$fa-css-prefix}-thumbs-down:before { content: fa-content($fa-var-thumbs-down); } +.#{$fa-css-prefix}-thumbs-up:before { content: fa-content($fa-var-thumbs-up); } +.#{$fa-css-prefix}-thumbtack:before { content: fa-content($fa-var-thumbtack); } +.#{$fa-css-prefix}-ticket-alt:before { content: fa-content($fa-var-ticket-alt); } +.#{$fa-css-prefix}-times:before { content: fa-content($fa-var-times); } +.#{$fa-css-prefix}-times-circle:before { content: fa-content($fa-var-times-circle); } +.#{$fa-css-prefix}-tint:before { content: fa-content($fa-var-tint); } +.#{$fa-css-prefix}-toggle-off:before { content: fa-content($fa-var-toggle-off); } +.#{$fa-css-prefix}-toggle-on:before { content: fa-content($fa-var-toggle-on); } +.#{$fa-css-prefix}-trademark:before { content: fa-content($fa-var-trademark); } +.#{$fa-css-prefix}-train:before { content: fa-content($fa-var-train); } +.#{$fa-css-prefix}-transgender:before { content: fa-content($fa-var-transgender); } +.#{$fa-css-prefix}-transgender-alt:before { content: fa-content($fa-var-transgender-alt); } +.#{$fa-css-prefix}-trash:before { content: fa-content($fa-var-trash); } +.#{$fa-css-prefix}-trash-alt:before { content: fa-content($fa-var-trash-alt); } +.#{$fa-css-prefix}-tree:before { content: fa-content($fa-var-tree); } +.#{$fa-css-prefix}-trello:before { content: fa-content($fa-var-trello); } +.#{$fa-css-prefix}-tripadvisor:before { content: fa-content($fa-var-tripadvisor); } +.#{$fa-css-prefix}-trophy:before { content: fa-content($fa-var-trophy); } +.#{$fa-css-prefix}-truck:before { content: fa-content($fa-var-truck); } +.#{$fa-css-prefix}-tty:before { content: fa-content($fa-var-tty); } +.#{$fa-css-prefix}-tumblr:before { content: fa-content($fa-var-tumblr); } +.#{$fa-css-prefix}-tumblr-square:before { content: fa-content($fa-var-tumblr-square); } +.#{$fa-css-prefix}-tv:before { content: fa-content($fa-var-tv); } +.#{$fa-css-prefix}-twitch:before { content: fa-content($fa-var-twitch); } +.#{$fa-css-prefix}-twitter:before { content: fa-content($fa-var-twitter); } +.#{$fa-css-prefix}-twitter-square:before { content: fa-content($fa-var-twitter-square); } +.#{$fa-css-prefix}-typo3:before { content: fa-content($fa-var-typo3); } +.#{$fa-css-prefix}-uber:before { content: fa-content($fa-var-uber); } +.#{$fa-css-prefix}-uikit:before { content: fa-content($fa-var-uikit); } +.#{$fa-css-prefix}-umbrella:before { content: fa-content($fa-var-umbrella); } +.#{$fa-css-prefix}-underline:before { content: fa-content($fa-var-underline); } +.#{$fa-css-prefix}-undo:before { content: fa-content($fa-var-undo); } +.#{$fa-css-prefix}-undo-alt:before { content: fa-content($fa-var-undo-alt); } +.#{$fa-css-prefix}-uniregistry:before { content: fa-content($fa-var-uniregistry); } +.#{$fa-css-prefix}-universal-access:before { content: fa-content($fa-var-universal-access); } +.#{$fa-css-prefix}-university:before { content: fa-content($fa-var-university); } +.#{$fa-css-prefix}-unlink:before { content: fa-content($fa-var-unlink); } +.#{$fa-css-prefix}-unlock:before { content: fa-content($fa-var-unlock); } +.#{$fa-css-prefix}-unlock-alt:before { content: fa-content($fa-var-unlock-alt); } +.#{$fa-css-prefix}-untappd:before { content: fa-content($fa-var-untappd); } +.#{$fa-css-prefix}-upload:before { content: fa-content($fa-var-upload); } +.#{$fa-css-prefix}-usb:before { content: fa-content($fa-var-usb); } +.#{$fa-css-prefix}-user:before { content: fa-content($fa-var-user); } +.#{$fa-css-prefix}-user-circle:before { content: fa-content($fa-var-user-circle); } +.#{$fa-css-prefix}-user-md:before { content: fa-content($fa-var-user-md); } +.#{$fa-css-prefix}-user-plus:before { content: fa-content($fa-var-user-plus); } +.#{$fa-css-prefix}-user-secret:before { content: fa-content($fa-var-user-secret); } +.#{$fa-css-prefix}-user-times:before { content: fa-content($fa-var-user-times); } +.#{$fa-css-prefix}-users:before { content: fa-content($fa-var-users); } +.#{$fa-css-prefix}-ussunnah:before { content: fa-content($fa-var-ussunnah); } +.#{$fa-css-prefix}-utensil-spoon:before { content: fa-content($fa-var-utensil-spoon); } +.#{$fa-css-prefix}-utensils:before { content: fa-content($fa-var-utensils); } +.#{$fa-css-prefix}-vaadin:before { content: fa-content($fa-var-vaadin); } +.#{$fa-css-prefix}-venus:before { content: fa-content($fa-var-venus); } +.#{$fa-css-prefix}-venus-double:before { content: fa-content($fa-var-venus-double); } +.#{$fa-css-prefix}-venus-mars:before { content: fa-content($fa-var-venus-mars); } +.#{$fa-css-prefix}-viacoin:before { content: fa-content($fa-var-viacoin); } +.#{$fa-css-prefix}-viadeo:before { content: fa-content($fa-var-viadeo); } +.#{$fa-css-prefix}-viadeo-square:before { content: fa-content($fa-var-viadeo-square); } +.#{$fa-css-prefix}-viber:before { content: fa-content($fa-var-viber); } +.#{$fa-css-prefix}-video:before { content: fa-content($fa-var-video); } +.#{$fa-css-prefix}-vimeo:before { content: fa-content($fa-var-vimeo); } +.#{$fa-css-prefix}-vimeo-square:before { content: fa-content($fa-var-vimeo-square); } +.#{$fa-css-prefix}-vimeo-v:before { content: fa-content($fa-var-vimeo-v); } +.#{$fa-css-prefix}-vine:before { content: fa-content($fa-var-vine); } +.#{$fa-css-prefix}-vk:before { content: fa-content($fa-var-vk); } +.#{$fa-css-prefix}-vnv:before { content: fa-content($fa-var-vnv); } +.#{$fa-css-prefix}-volume-down:before { content: fa-content($fa-var-volume-down); } +.#{$fa-css-prefix}-volume-off:before { content: fa-content($fa-var-volume-off); } +.#{$fa-css-prefix}-volume-up:before { content: fa-content($fa-var-volume-up); } +.#{$fa-css-prefix}-vuejs:before { content: fa-content($fa-var-vuejs); } +.#{$fa-css-prefix}-weibo:before { content: fa-content($fa-var-weibo); } +.#{$fa-css-prefix}-weixin:before { content: fa-content($fa-var-weixin); } +.#{$fa-css-prefix}-whatsapp:before { content: fa-content($fa-var-whatsapp); } +.#{$fa-css-prefix}-whatsapp-square:before { content: fa-content($fa-var-whatsapp-square); } +.#{$fa-css-prefix}-wheelchair:before { content: fa-content($fa-var-wheelchair); } +.#{$fa-css-prefix}-whmcs:before { content: fa-content($fa-var-whmcs); } +.#{$fa-css-prefix}-wifi:before { content: fa-content($fa-var-wifi); } +.#{$fa-css-prefix}-wikipedia-w:before { content: fa-content($fa-var-wikipedia-w); } +.#{$fa-css-prefix}-window-close:before { content: fa-content($fa-var-window-close); } +.#{$fa-css-prefix}-window-maximize:before { content: fa-content($fa-var-window-maximize); } +.#{$fa-css-prefix}-window-minimize:before { content: fa-content($fa-var-window-minimize); } +.#{$fa-css-prefix}-window-restore:before { content: fa-content($fa-var-window-restore); } +.#{$fa-css-prefix}-windows:before { content: fa-content($fa-var-windows); } +.#{$fa-css-prefix}-won-sign:before { content: fa-content($fa-var-won-sign); } +.#{$fa-css-prefix}-wordpress:before { content: fa-content($fa-var-wordpress); } +.#{$fa-css-prefix}-wordpress-simple:before { content: fa-content($fa-var-wordpress-simple); } +.#{$fa-css-prefix}-wpbeginner:before { content: fa-content($fa-var-wpbeginner); } +.#{$fa-css-prefix}-wpexplorer:before { content: fa-content($fa-var-wpexplorer); } +.#{$fa-css-prefix}-wpforms:before { content: fa-content($fa-var-wpforms); } +.#{$fa-css-prefix}-wrench:before { content: fa-content($fa-var-wrench); } +.#{$fa-css-prefix}-xbox:before { content: fa-content($fa-var-xbox); } +.#{$fa-css-prefix}-xing:before { content: fa-content($fa-var-xing); } +.#{$fa-css-prefix}-xing-square:before { content: fa-content($fa-var-xing-square); } +.#{$fa-css-prefix}-y-combinator:before { content: fa-content($fa-var-y-combinator); } +.#{$fa-css-prefix}-yahoo:before { content: fa-content($fa-var-yahoo); } +.#{$fa-css-prefix}-yandex:before { content: fa-content($fa-var-yandex); } +.#{$fa-css-prefix}-yandex-international:before { content: fa-content($fa-var-yandex-international); } +.#{$fa-css-prefix}-yelp:before { content: fa-content($fa-var-yelp); } +.#{$fa-css-prefix}-yen-sign:before { content: fa-content($fa-var-yen-sign); } +.#{$fa-css-prefix}-yoast:before { content: fa-content($fa-var-yoast); } +.#{$fa-css-prefix}-youtube:before { content: fa-content($fa-var-youtube); } diff --git a/docs/style/fa/_larger.scss b/docs/style/fa/_larger.scss new file mode 100644 index 000000000..27c2ad5fc --- /dev/null +++ b/docs/style/fa/_larger.scss @@ -0,0 +1,23 @@ +// Icon Sizes +// ------------------------- + +// makes the font 33% larger relative to the icon container +.#{$fa-css-prefix}-lg { + font-size: (4em / 3); + line-height: (3em / 4); + vertical-align: -.0667em; +} + +.#{$fa-css-prefix}-xs { + font-size: .75em; +} + +.#{$fa-css-prefix}-sm { + font-size: .875em; +} + +@for $i from 1 through 10 { + .#{$fa-css-prefix}-#{$i}x { + font-size: $i * 1em; + } +} diff --git a/docs/style/fa/_list.scss b/docs/style/fa/_list.scss new file mode 100644 index 000000000..8ebf33333 --- /dev/null +++ b/docs/style/fa/_list.scss @@ -0,0 +1,18 @@ +// List Icons +// ------------------------- + +.#{$fa-css-prefix}-ul { + list-style-type: none; + margin-left: $fa-li-width * 5/4; + padding-left: 0; + + > li { position: relative; } +} + +.#{$fa-css-prefix}-li { + left: -$fa-li-width; + position: absolute; + text-align: center; + width: $fa-li-width; + line-height: inherit; +} diff --git a/docs/style/fa/_mixins.scss b/docs/style/fa/_mixins.scss new file mode 100644 index 000000000..06e549b69 --- /dev/null +++ b/docs/style/fa/_mixins.scss @@ -0,0 +1,57 @@ +// Mixins +// -------------------------- + +@mixin fa-icon { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + display: inline-block; + font-style: normal; + font-variant: normal; + font-weight: normal; + line-height: 1; + vertical-align: -15%; +} + +@mixin fa-icon-rotate($degrees, $rotation) { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; + transform: rotate($degrees); +} + +@mixin fa-icon-flip($horiz, $vert, $rotation) { + -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; + transform: scale($horiz, $vert); +} + + +// Only display content to screen readers. A la Bootstrap 4. +// +// See: http://a11yproject.com/posts/how-to-hide-content/ + +@mixin sr-only { + border: 0; + clip: rect(0, 0, 0, 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +// Use in conjunction with .sr-only to only display content when it's focused. +// +// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 +// +// Credit: HTML5 Boilerplate + +@mixin sr-only-focusable { + &:active, + &:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; + } +} diff --git a/docs/style/fa/_rotated-flipped.scss b/docs/style/fa/_rotated-flipped.scss new file mode 100644 index 000000000..995bc4cc7 --- /dev/null +++ b/docs/style/fa/_rotated-flipped.scss @@ -0,0 +1,23 @@ +// Rotated & Flipped Icons +// ------------------------- + +.#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } +.#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } +.#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } + +.#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } +.#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } +.#{$fa-css-prefix}-flip-horizontal.#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(-1, -1, 2); } + +// Hook for IE8-9 +// ------------------------- + +:root { + .#{$fa-css-prefix}-rotate-90, + .#{$fa-css-prefix}-rotate-180, + .#{$fa-css-prefix}-rotate-270, + .#{$fa-css-prefix}-flip-horizontal, + .#{$fa-css-prefix}-flip-vertical { + filter: none; + } +} diff --git a/docs/style/fa/_screen-reader.scss b/docs/style/fa/_screen-reader.scss new file mode 100644 index 000000000..5d0ab262f --- /dev/null +++ b/docs/style/fa/_screen-reader.scss @@ -0,0 +1,5 @@ +// Screen Readers +// ------------------------- + +.sr-only { @include sr-only; } +.sr-only-focusable { @include sr-only-focusable; } diff --git a/docs/style/fa/_stacked.scss b/docs/style/fa/_stacked.scss new file mode 100644 index 000000000..6c09d84cd --- /dev/null +++ b/docs/style/fa/_stacked.scss @@ -0,0 +1,31 @@ +// Stacked Icons +// ------------------------- + +.#{$fa-css-prefix}-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: middle; + width: 2em; +} + +.#{$fa-css-prefix}-stack-1x, +.#{$fa-css-prefix}-stack-2x { + left: 0; + position: absolute; + text-align: center; + width: 100%; +} + +.#{$fa-css-prefix}-stack-1x { + line-height: inherit; +} + +.#{$fa-css-prefix}-stack-2x { + font-size: 2em; +} + +.#{$fa-css-prefix}-inverse { + color: $fa-inverse; +} diff --git a/docs/style/fa/_variables.scss b/docs/style/fa/_variables.scss new file mode 100644 index 000000000..d2c4d474c --- /dev/null +++ b/docs/style/fa/_variables.scss @@ -0,0 +1,805 @@ +// Variables +// -------------------------- + +$fa-font-path: "../webfonts" !default; +$fa-font-size-base: 16px !default; +$fa-css-prefix: fa !default; +$fa-version: "5.0.2" !default; +$fa-border-color: #eee !default; +$fa-inverse: #fff !default; +$fa-li-width: 2em !default; + +// Convenience function used to set content property +@function fa-content($fa-var) { + @return unquote("\"#{ $fa-var }\""); +} + +$fa-var-500px: \f26e; +$fa-var-accessible-icon: \f368; +$fa-var-accusoft: \f369; +$fa-var-address-book: \f2b9; +$fa-var-address-card: \f2bb; +$fa-var-adjust: \f042; +$fa-var-adn: \f170; +$fa-var-adversal: \f36a; +$fa-var-affiliatetheme: \f36b; +$fa-var-algolia: \f36c; +$fa-var-align-center: \f037; +$fa-var-align-justify: \f039; +$fa-var-align-left: \f036; +$fa-var-align-right: \f038; +$fa-var-amazon: \f270; +$fa-var-amazon-pay: \f42c; +$fa-var-ambulance: \f0f9; +$fa-var-american-sign-language-interpreting: \f2a3; +$fa-var-amilia: \f36d; +$fa-var-anchor: \f13d; +$fa-var-android: \f17b; +$fa-var-angellist: \f209; +$fa-var-angle-double-down: \f103; +$fa-var-angle-double-left: \f100; +$fa-var-angle-double-right: \f101; +$fa-var-angle-double-up: \f102; +$fa-var-angle-down: \f107; +$fa-var-angle-left: \f104; +$fa-var-angle-right: \f105; +$fa-var-angle-up: \f106; +$fa-var-angrycreative: \f36e; +$fa-var-angular: \f420; +$fa-var-app-store: \f36f; +$fa-var-app-store-ios: \f370; +$fa-var-apper: \f371; +$fa-var-apple: \f179; +$fa-var-apple-pay: \f415; +$fa-var-archive: \f187; +$fa-var-arrow-alt-circle-down: \f358; +$fa-var-arrow-alt-circle-left: \f359; +$fa-var-arrow-alt-circle-right: \f35a; +$fa-var-arrow-alt-circle-up: \f35b; +$fa-var-arrow-circle-down: \f0ab; +$fa-var-arrow-circle-left: \f0a8; +$fa-var-arrow-circle-right: \f0a9; +$fa-var-arrow-circle-up: \f0aa; +$fa-var-arrow-down: \f063; +$fa-var-arrow-left: \f060; +$fa-var-arrow-right: \f061; +$fa-var-arrow-up: \f062; +$fa-var-arrows-alt: \f0b2; +$fa-var-arrows-alt-h: \f337; +$fa-var-arrows-alt-v: \f338; +$fa-var-assistive-listening-systems: \f2a2; +$fa-var-asterisk: \f069; +$fa-var-asymmetrik: \f372; +$fa-var-at: \f1fa; +$fa-var-audible: \f373; +$fa-var-audio-description: \f29e; +$fa-var-autoprefixer: \f41c; +$fa-var-avianex: \f374; +$fa-var-aviato: \f421; +$fa-var-aws: \f375; +$fa-var-backward: \f04a; +$fa-var-balance-scale: \f24e; +$fa-var-ban: \f05e; +$fa-var-bandcamp: \f2d5; +$fa-var-barcode: \f02a; +$fa-var-bars: \f0c9; +$fa-var-bath: \f2cd; +$fa-var-battery-empty: \f244; +$fa-var-battery-full: \f240; +$fa-var-battery-half: \f242; +$fa-var-battery-quarter: \f243; +$fa-var-battery-three-quarters: \f241; +$fa-var-bed: \f236; +$fa-var-beer: \f0fc; +$fa-var-behance: \f1b4; +$fa-var-behance-square: \f1b5; +$fa-var-bell: \f0f3; +$fa-var-bell-slash: \f1f6; +$fa-var-bicycle: \f206; +$fa-var-bimobject: \f378; +$fa-var-binoculars: \f1e5; +$fa-var-birthday-cake: \f1fd; +$fa-var-bitbucket: \f171; +$fa-var-bitcoin: \f379; +$fa-var-bity: \f37a; +$fa-var-black-tie: \f27e; +$fa-var-blackberry: \f37b; +$fa-var-blind: \f29d; +$fa-var-blogger: \f37c; +$fa-var-blogger-b: \f37d; +$fa-var-bluetooth: \f293; +$fa-var-bluetooth-b: \f294; +$fa-var-bold: \f032; +$fa-var-bolt: \f0e7; +$fa-var-bomb: \f1e2; +$fa-var-book: \f02d; +$fa-var-bookmark: \f02e; +$fa-var-braille: \f2a1; +$fa-var-briefcase: \f0b1; +$fa-var-btc: \f15a; +$fa-var-bug: \f188; +$fa-var-building: \f1ad; +$fa-var-bullhorn: \f0a1; +$fa-var-bullseye: \f140; +$fa-var-buromobelexperte: \f37f; +$fa-var-bus: \f207; +$fa-var-buysellads: \f20d; +$fa-var-calculator: \f1ec; +$fa-var-calendar: \f133; +$fa-var-calendar-alt: \f073; +$fa-var-calendar-check: \f274; +$fa-var-calendar-minus: \f272; +$fa-var-calendar-plus: \f271; +$fa-var-calendar-times: \f273; +$fa-var-camera: \f030; +$fa-var-camera-retro: \f083; +$fa-var-car: \f1b9; +$fa-var-caret-down: \f0d7; +$fa-var-caret-left: \f0d9; +$fa-var-caret-right: \f0da; +$fa-var-caret-square-down: \f150; +$fa-var-caret-square-left: \f191; +$fa-var-caret-square-right: \f152; +$fa-var-caret-square-up: \f151; +$fa-var-caret-up: \f0d8; +$fa-var-cart-arrow-down: \f218; +$fa-var-cart-plus: \f217; +$fa-var-cc-amazon-pay: \f42d; +$fa-var-cc-amex: \f1f3; +$fa-var-cc-apple-pay: \f416; +$fa-var-cc-diners-club: \f24c; +$fa-var-cc-discover: \f1f2; +$fa-var-cc-jcb: \f24b; +$fa-var-cc-mastercard: \f1f1; +$fa-var-cc-paypal: \f1f4; +$fa-var-cc-stripe: \f1f5; +$fa-var-cc-visa: \f1f0; +$fa-var-centercode: \f380; +$fa-var-certificate: \f0a3; +$fa-var-chart-area: \f1fe; +$fa-var-chart-bar: \f080; +$fa-var-chart-line: \f201; +$fa-var-chart-pie: \f200; +$fa-var-check: \f00c; +$fa-var-check-circle: \f058; +$fa-var-check-square: \f14a; +$fa-var-chevron-circle-down: \f13a; +$fa-var-chevron-circle-left: \f137; +$fa-var-chevron-circle-right: \f138; +$fa-var-chevron-circle-up: \f139; +$fa-var-chevron-down: \f078; +$fa-var-chevron-left: \f053; +$fa-var-chevron-right: \f054; +$fa-var-chevron-up: \f077; +$fa-var-child: \f1ae; +$fa-var-chrome: \f268; +$fa-var-circle: \f111; +$fa-var-circle-notch: \f1ce; +$fa-var-clipboard: \f328; +$fa-var-clock: \f017; +$fa-var-clone: \f24d; +$fa-var-closed-captioning: \f20a; +$fa-var-cloud: \f0c2; +$fa-var-cloud-download-alt: \f381; +$fa-var-cloud-upload-alt: \f382; +$fa-var-cloudscale: \f383; +$fa-var-cloudsmith: \f384; +$fa-var-cloudversify: \f385; +$fa-var-code: \f121; +$fa-var-code-branch: \f126; +$fa-var-codepen: \f1cb; +$fa-var-codiepie: \f284; +$fa-var-coffee: \f0f4; +$fa-var-cog: \f013; +$fa-var-cogs: \f085; +$fa-var-columns: \f0db; +$fa-var-comment: \f075; +$fa-var-comment-alt: \f27a; +$fa-var-comments: \f086; +$fa-var-compass: \f14e; +$fa-var-compress: \f066; +$fa-var-connectdevelop: \f20e; +$fa-var-contao: \f26d; +$fa-var-copy: \f0c5; +$fa-var-copyright: \f1f9; +$fa-var-cpanel: \f388; +$fa-var-creative-commons: \f25e; +$fa-var-credit-card: \f09d; +$fa-var-crop: \f125; +$fa-var-crosshairs: \f05b; +$fa-var-css3: \f13c; +$fa-var-css3-alt: \f38b; +$fa-var-cube: \f1b2; +$fa-var-cubes: \f1b3; +$fa-var-cut: \f0c4; +$fa-var-cuttlefish: \f38c; +$fa-var-d-and-d: \f38d; +$fa-var-dashcube: \f210; +$fa-var-database: \f1c0; +$fa-var-deaf: \f2a4; +$fa-var-delicious: \f1a5; +$fa-var-deploydog: \f38e; +$fa-var-deskpro: \f38f; +$fa-var-desktop: \f108; +$fa-var-deviantart: \f1bd; +$fa-var-digg: \f1a6; +$fa-var-digital-ocean: \f391; +$fa-var-discord: \f392; +$fa-var-discourse: \f393; +$fa-var-dochub: \f394; +$fa-var-docker: \f395; +$fa-var-dollar-sign: \f155; +$fa-var-dot-circle: \f192; +$fa-var-download: \f019; +$fa-var-draft2digital: \f396; +$fa-var-dribbble: \f17d; +$fa-var-dribbble-square: \f397; +$fa-var-dropbox: \f16b; +$fa-var-drupal: \f1a9; +$fa-var-dyalog: \f399; +$fa-var-earlybirds: \f39a; +$fa-var-edge: \f282; +$fa-var-edit: \f044; +$fa-var-eject: \f052; +$fa-var-ellipsis-h: \f141; +$fa-var-ellipsis-v: \f142; +$fa-var-ember: \f423; +$fa-var-empire: \f1d1; +$fa-var-envelope: \f0e0; +$fa-var-envelope-open: \f2b6; +$fa-var-envelope-square: \f199; +$fa-var-envira: \f299; +$fa-var-eraser: \f12d; +$fa-var-erlang: \f39d; +$fa-var-ethereum: \f42e; +$fa-var-etsy: \f2d7; +$fa-var-euro-sign: \f153; +$fa-var-exchange-alt: \f362; +$fa-var-exclamation: \f12a; +$fa-var-exclamation-circle: \f06a; +$fa-var-exclamation-triangle: \f071; +$fa-var-expand: \f065; +$fa-var-expand-arrows-alt: \f31e; +$fa-var-expeditedssl: \f23e; +$fa-var-external-link-alt: \f35d; +$fa-var-external-link-square-alt: \f360; +$fa-var-eye: \f06e; +$fa-var-eye-dropper: \f1fb; +$fa-var-eye-slash: \f070; +$fa-var-facebook: \f09a; +$fa-var-facebook-f: \f39e; +$fa-var-facebook-messenger: \f39f; +$fa-var-facebook-square: \f082; +$fa-var-fast-backward: \f049; +$fa-var-fast-forward: \f050; +$fa-var-fax: \f1ac; +$fa-var-female: \f182; +$fa-var-fighter-jet: \f0fb; +$fa-var-file: \f15b; +$fa-var-file-alt: \f15c; +$fa-var-file-archive: \f1c6; +$fa-var-file-audio: \f1c7; +$fa-var-file-code: \f1c9; +$fa-var-file-excel: \f1c3; +$fa-var-file-image: \f1c5; +$fa-var-file-pdf: \f1c1; +$fa-var-file-powerpoint: \f1c4; +$fa-var-file-video: \f1c8; +$fa-var-file-word: \f1c2; +$fa-var-film: \f008; +$fa-var-filter: \f0b0; +$fa-var-fire: \f06d; +$fa-var-fire-extinguisher: \f134; +$fa-var-firefox: \f269; +$fa-var-first-order: \f2b0; +$fa-var-firstdraft: \f3a1; +$fa-var-flag: \f024; +$fa-var-flag-checkered: \f11e; +$fa-var-flask: \f0c3; +$fa-var-flickr: \f16e; +$fa-var-fly: \f417; +$fa-var-folder: \f07b; +$fa-var-folder-open: \f07c; +$fa-var-font: \f031; +$fa-var-font-awesome: \f2b4; +$fa-var-font-awesome-alt: \f35c; +$fa-var-font-awesome-flag: \f425; +$fa-var-fonticons: \f280; +$fa-var-fonticons-fi: \f3a2; +$fa-var-fort-awesome: \f286; +$fa-var-fort-awesome-alt: \f3a3; +$fa-var-forumbee: \f211; +$fa-var-forward: \f04e; +$fa-var-foursquare: \f180; +$fa-var-free-code-camp: \f2c5; +$fa-var-freebsd: \f3a4; +$fa-var-frown: \f119; +$fa-var-futbol: \f1e3; +$fa-var-gamepad: \f11b; +$fa-var-gavel: \f0e3; +$fa-var-gem: \f3a5; +$fa-var-genderless: \f22d; +$fa-var-get-pocket: \f265; +$fa-var-gg: \f260; +$fa-var-gg-circle: \f261; +$fa-var-gift: \f06b; +$fa-var-git: \f1d3; +$fa-var-git-square: \f1d2; +$fa-var-github: \f09b; +$fa-var-github-alt: \f113; +$fa-var-github-square: \f092; +$fa-var-gitkraken: \f3a6; +$fa-var-gitlab: \f296; +$fa-var-gitter: \f426; +$fa-var-glass-martini: \f000; +$fa-var-glide: \f2a5; +$fa-var-glide-g: \f2a6; +$fa-var-globe: \f0ac; +$fa-var-gofore: \f3a7; +$fa-var-goodreads: \f3a8; +$fa-var-goodreads-g: \f3a9; +$fa-var-google: \f1a0; +$fa-var-google-drive: \f3aa; +$fa-var-google-play: \f3ab; +$fa-var-google-plus: \f2b3; +$fa-var-google-plus-g: \f0d5; +$fa-var-google-plus-square: \f0d4; +$fa-var-google-wallet: \f1ee; +$fa-var-graduation-cap: \f19d; +$fa-var-gratipay: \f184; +$fa-var-grav: \f2d6; +$fa-var-gripfire: \f3ac; +$fa-var-grunt: \f3ad; +$fa-var-gulp: \f3ae; +$fa-var-h-square: \f0fd; +$fa-var-hacker-news: \f1d4; +$fa-var-hacker-news-square: \f3af; +$fa-var-hand-lizard: \f258; +$fa-var-hand-paper: \f256; +$fa-var-hand-peace: \f25b; +$fa-var-hand-point-down: \f0a7; +$fa-var-hand-point-left: \f0a5; +$fa-var-hand-point-right: \f0a4; +$fa-var-hand-point-up: \f0a6; +$fa-var-hand-pointer: \f25a; +$fa-var-hand-rock: \f255; +$fa-var-hand-scissors: \f257; +$fa-var-hand-spock: \f259; +$fa-var-handshake: \f2b5; +$fa-var-hashtag: \f292; +$fa-var-hdd: \f0a0; +$fa-var-heading: \f1dc; +$fa-var-headphones: \f025; +$fa-var-heart: \f004; +$fa-var-heartbeat: \f21e; +$fa-var-hire-a-helper: \f3b0; +$fa-var-history: \f1da; +$fa-var-home: \f015; +$fa-var-hooli: \f427; +$fa-var-hospital: \f0f8; +$fa-var-hotjar: \f3b1; +$fa-var-hourglass: \f254; +$fa-var-hourglass-end: \f253; +$fa-var-hourglass-half: \f252; +$fa-var-hourglass-start: \f251; +$fa-var-houzz: \f27c; +$fa-var-html5: \f13b; +$fa-var-hubspot: \f3b2; +$fa-var-i-cursor: \f246; +$fa-var-id-badge: \f2c1; +$fa-var-id-card: \f2c2; +$fa-var-image: \f03e; +$fa-var-images: \f302; +$fa-var-imdb: \f2d8; +$fa-var-inbox: \f01c; +$fa-var-indent: \f03c; +$fa-var-industry: \f275; +$fa-var-info: \f129; +$fa-var-info-circle: \f05a; +$fa-var-instagram: \f16d; +$fa-var-internet-explorer: \f26b; +$fa-var-ioxhost: \f208; +$fa-var-italic: \f033; +$fa-var-itunes: \f3b4; +$fa-var-itunes-note: \f3b5; +$fa-var-jenkins: \f3b6; +$fa-var-joget: \f3b7; +$fa-var-joomla: \f1aa; +$fa-var-js: \f3b8; +$fa-var-js-square: \f3b9; +$fa-var-jsfiddle: \f1cc; +$fa-var-key: \f084; +$fa-var-keyboard: \f11c; +$fa-var-keycdn: \f3ba; +$fa-var-kickstarter: \f3bb; +$fa-var-kickstarter-k: \f3bc; +$fa-var-korvue: \f42f; +$fa-var-language: \f1ab; +$fa-var-laptop: \f109; +$fa-var-laravel: \f3bd; +$fa-var-lastfm: \f202; +$fa-var-lastfm-square: \f203; +$fa-var-leaf: \f06c; +$fa-var-leanpub: \f212; +$fa-var-lemon: \f094; +$fa-var-less: \f41d; +$fa-var-level-down-alt: \f3be; +$fa-var-level-up-alt: \f3bf; +$fa-var-life-ring: \f1cd; +$fa-var-lightbulb: \f0eb; +$fa-var-line: \f3c0; +$fa-var-link: \f0c1; +$fa-var-linkedin: \f08c; +$fa-var-linkedin-in: \f0e1; +$fa-var-linode: \f2b8; +$fa-var-linux: \f17c; +$fa-var-lira-sign: \f195; +$fa-var-list: \f03a; +$fa-var-list-alt: \f022; +$fa-var-list-ol: \f0cb; +$fa-var-list-ul: \f0ca; +$fa-var-location-arrow: \f124; +$fa-var-lock: \f023; +$fa-var-lock-open: \f3c1; +$fa-var-long-arrow-alt-down: \f309; +$fa-var-long-arrow-alt-left: \f30a; +$fa-var-long-arrow-alt-right: \f30b; +$fa-var-long-arrow-alt-up: \f30c; +$fa-var-low-vision: \f2a8; +$fa-var-lyft: \f3c3; +$fa-var-magento: \f3c4; +$fa-var-magic: \f0d0; +$fa-var-magnet: \f076; +$fa-var-male: \f183; +$fa-var-map: \f279; +$fa-var-map-marker: \f041; +$fa-var-map-marker-alt: \f3c5; +$fa-var-map-pin: \f276; +$fa-var-map-signs: \f277; +$fa-var-mars: \f222; +$fa-var-mars-double: \f227; +$fa-var-mars-stroke: \f229; +$fa-var-mars-stroke-h: \f22b; +$fa-var-mars-stroke-v: \f22a; +$fa-var-maxcdn: \f136; +$fa-var-medapps: \f3c6; +$fa-var-medium: \f23a; +$fa-var-medium-m: \f3c7; +$fa-var-medkit: \f0fa; +$fa-var-medrt: \f3c8; +$fa-var-meetup: \f2e0; +$fa-var-meh: \f11a; +$fa-var-mercury: \f223; +$fa-var-microchip: \f2db; +$fa-var-microphone: \f130; +$fa-var-microphone-slash: \f131; +$fa-var-microsoft: \f3ca; +$fa-var-minus: \f068; +$fa-var-minus-circle: \f056; +$fa-var-minus-square: \f146; +$fa-var-mix: \f3cb; +$fa-var-mixcloud: \f289; +$fa-var-mizuni: \f3cc; +$fa-var-mobile: \f10b; +$fa-var-mobile-alt: \f3cd; +$fa-var-modx: \f285; +$fa-var-monero: \f3d0; +$fa-var-money-bill-alt: \f3d1; +$fa-var-moon: \f186; +$fa-var-motorcycle: \f21c; +$fa-var-mouse-pointer: \f245; +$fa-var-music: \f001; +$fa-var-napster: \f3d2; +$fa-var-neuter: \f22c; +$fa-var-newspaper: \f1ea; +$fa-var-nintendo-switch: \f418; +$fa-var-node: \f419; +$fa-var-node-js: \f3d3; +$fa-var-npm: \f3d4; +$fa-var-ns8: \f3d5; +$fa-var-nutritionix: \f3d6; +$fa-var-object-group: \f247; +$fa-var-object-ungroup: \f248; +$fa-var-odnoklassniki: \f263; +$fa-var-odnoklassniki-square: \f264; +$fa-var-opencart: \f23d; +$fa-var-openid: \f19b; +$fa-var-opera: \f26a; +$fa-var-optin-monster: \f23c; +$fa-var-osi: \f41a; +$fa-var-outdent: \f03b; +$fa-var-page4: \f3d7; +$fa-var-pagelines: \f18c; +$fa-var-paint-brush: \f1fc; +$fa-var-palfed: \f3d8; +$fa-var-paper-plane: \f1d8; +$fa-var-paperclip: \f0c6; +$fa-var-paragraph: \f1dd; +$fa-var-paste: \f0ea; +$fa-var-patreon: \f3d9; +$fa-var-pause: \f04c; +$fa-var-pause-circle: \f28b; +$fa-var-paw: \f1b0; +$fa-var-paypal: \f1ed; +$fa-var-pen-square: \f14b; +$fa-var-pencil-alt: \f303; +$fa-var-percent: \f295; +$fa-var-periscope: \f3da; +$fa-var-phabricator: \f3db; +$fa-var-phoenix-framework: \f3dc; +$fa-var-phone: \f095; +$fa-var-phone-square: \f098; +$fa-var-phone-volume: \f2a0; +$fa-var-pied-piper: \f2ae; +$fa-var-pied-piper-alt: \f1a8; +$fa-var-pied-piper-pp: \f1a7; +$fa-var-pinterest: \f0d2; +$fa-var-pinterest-p: \f231; +$fa-var-pinterest-square: \f0d3; +$fa-var-plane: \f072; +$fa-var-play: \f04b; +$fa-var-play-circle: \f144; +$fa-var-playstation: \f3df; +$fa-var-plug: \f1e6; +$fa-var-plus: \f067; +$fa-var-plus-circle: \f055; +$fa-var-plus-square: \f0fe; +$fa-var-podcast: \f2ce; +$fa-var-pound-sign: \f154; +$fa-var-power-off: \f011; +$fa-var-print: \f02f; +$fa-var-product-hunt: \f288; +$fa-var-pushed: \f3e1; +$fa-var-puzzle-piece: \f12e; +$fa-var-python: \f3e2; +$fa-var-qq: \f1d6; +$fa-var-qrcode: \f029; +$fa-var-question: \f128; +$fa-var-question-circle: \f059; +$fa-var-quora: \f2c4; +$fa-var-quote-left: \f10d; +$fa-var-quote-right: \f10e; +$fa-var-random: \f074; +$fa-var-ravelry: \f2d9; +$fa-var-react: \f41b; +$fa-var-rebel: \f1d0; +$fa-var-recycle: \f1b8; +$fa-var-red-river: \f3e3; +$fa-var-reddit: \f1a1; +$fa-var-reddit-alien: \f281; +$fa-var-reddit-square: \f1a2; +$fa-var-redo: \f01e; +$fa-var-redo-alt: \f2f9; +$fa-var-registered: \f25d; +$fa-var-rendact: \f3e4; +$fa-var-renren: \f18b; +$fa-var-reply: \f3e5; +$fa-var-reply-all: \f122; +$fa-var-replyd: \f3e6; +$fa-var-resolving: \f3e7; +$fa-var-retweet: \f079; +$fa-var-road: \f018; +$fa-var-rocket: \f135; +$fa-var-rocketchat: \f3e8; +$fa-var-rockrms: \f3e9; +$fa-var-rss: \f09e; +$fa-var-rss-square: \f143; +$fa-var-ruble-sign: \f158; +$fa-var-rupee-sign: \f156; +$fa-var-safari: \f267; +$fa-var-sass: \f41e; +$fa-var-save: \f0c7; +$fa-var-schlix: \f3ea; +$fa-var-scribd: \f28a; +$fa-var-search: \f002; +$fa-var-search-minus: \f010; +$fa-var-search-plus: \f00e; +$fa-var-searchengin: \f3eb; +$fa-var-sellcast: \f2da; +$fa-var-sellsy: \f213; +$fa-var-server: \f233; +$fa-var-servicestack: \f3ec; +$fa-var-share: \f064; +$fa-var-share-alt: \f1e0; +$fa-var-share-alt-square: \f1e1; +$fa-var-share-square: \f14d; +$fa-var-shekel-sign: \f20b; +$fa-var-shield-alt: \f3ed; +$fa-var-ship: \f21a; +$fa-var-shirtsinbulk: \f214; +$fa-var-shopping-bag: \f290; +$fa-var-shopping-basket: \f291; +$fa-var-shopping-cart: \f07a; +$fa-var-shower: \f2cc; +$fa-var-sign-in-alt: \f2f6; +$fa-var-sign-language: \f2a7; +$fa-var-sign-out-alt: \f2f5; +$fa-var-signal: \f012; +$fa-var-simplybuilt: \f215; +$fa-var-sistrix: \f3ee; +$fa-var-sitemap: \f0e8; +$fa-var-skyatlas: \f216; +$fa-var-skype: \f17e; +$fa-var-slack: \f198; +$fa-var-slack-hash: \f3ef; +$fa-var-sliders-h: \f1de; +$fa-var-slideshare: \f1e7; +$fa-var-smile: \f118; +$fa-var-snapchat: \f2ab; +$fa-var-snapchat-ghost: \f2ac; +$fa-var-snapchat-square: \f2ad; +$fa-var-snowflake: \f2dc; +$fa-var-sort: \f0dc; +$fa-var-sort-alpha-down: \f15d; +$fa-var-sort-alpha-up: \f15e; +$fa-var-sort-amount-down: \f160; +$fa-var-sort-amount-up: \f161; +$fa-var-sort-down: \f0dd; +$fa-var-sort-numeric-down: \f162; +$fa-var-sort-numeric-up: \f163; +$fa-var-sort-up: \f0de; +$fa-var-soundcloud: \f1be; +$fa-var-space-shuttle: \f197; +$fa-var-speakap: \f3f3; +$fa-var-spinner: \f110; +$fa-var-spotify: \f1bc; +$fa-var-square: \f0c8; +$fa-var-stack-exchange: \f18d; +$fa-var-stack-overflow: \f16c; +$fa-var-star: \f005; +$fa-var-star-half: \f089; +$fa-var-staylinked: \f3f5; +$fa-var-steam: \f1b6; +$fa-var-steam-square: \f1b7; +$fa-var-steam-symbol: \f3f6; +$fa-var-step-backward: \f048; +$fa-var-step-forward: \f051; +$fa-var-stethoscope: \f0f1; +$fa-var-sticker-mule: \f3f7; +$fa-var-sticky-note: \f249; +$fa-var-stop: \f04d; +$fa-var-stop-circle: \f28d; +$fa-var-stopwatch: \f2f2; +$fa-var-strava: \f428; +$fa-var-street-view: \f21d; +$fa-var-strikethrough: \f0cc; +$fa-var-stripe: \f429; +$fa-var-stripe-s: \f42a; +$fa-var-studiovinari: \f3f8; +$fa-var-stumbleupon: \f1a4; +$fa-var-stumbleupon-circle: \f1a3; +$fa-var-subscript: \f12c; +$fa-var-subway: \f239; +$fa-var-suitcase: \f0f2; +$fa-var-sun: \f185; +$fa-var-superpowers: \f2dd; +$fa-var-superscript: \f12b; +$fa-var-supple: \f3f9; +$fa-var-sync: \f021; +$fa-var-sync-alt: \f2f1; +$fa-var-table: \f0ce; +$fa-var-tablet: \f10a; +$fa-var-tablet-alt: \f3fa; +$fa-var-tachometer-alt: \f3fd; +$fa-var-tag: \f02b; +$fa-var-tags: \f02c; +$fa-var-tasks: \f0ae; +$fa-var-taxi: \f1ba; +$fa-var-telegram: \f2c6; +$fa-var-telegram-plane: \f3fe; +$fa-var-tencent-weibo: \f1d5; +$fa-var-terminal: \f120; +$fa-var-text-height: \f034; +$fa-var-text-width: \f035; +$fa-var-th: \f00a; +$fa-var-th-large: \f009; +$fa-var-th-list: \f00b; +$fa-var-themeisle: \f2b2; +$fa-var-thermometer-empty: \f2cb; +$fa-var-thermometer-full: \f2c7; +$fa-var-thermometer-half: \f2c9; +$fa-var-thermometer-quarter: \f2ca; +$fa-var-thermometer-three-quarters: \f2c8; +$fa-var-thumbs-down: \f165; +$fa-var-thumbs-up: \f164; +$fa-var-thumbtack: \f08d; +$fa-var-ticket-alt: \f3ff; +$fa-var-times: \f00d; +$fa-var-times-circle: \f057; +$fa-var-tint: \f043; +$fa-var-toggle-off: \f204; +$fa-var-toggle-on: \f205; +$fa-var-trademark: \f25c; +$fa-var-train: \f238; +$fa-var-transgender: \f224; +$fa-var-transgender-alt: \f225; +$fa-var-trash: \f1f8; +$fa-var-trash-alt: \f2ed; +$fa-var-tree: \f1bb; +$fa-var-trello: \f181; +$fa-var-tripadvisor: \f262; +$fa-var-trophy: \f091; +$fa-var-truck: \f0d1; +$fa-var-tty: \f1e4; +$fa-var-tumblr: \f173; +$fa-var-tumblr-square: \f174; +$fa-var-tv: \f26c; +$fa-var-twitch: \f1e8; +$fa-var-twitter: \f099; +$fa-var-twitter-square: \f081; +$fa-var-typo3: \f42b; +$fa-var-uber: \f402; +$fa-var-uikit: \f403; +$fa-var-umbrella: \f0e9; +$fa-var-underline: \f0cd; +$fa-var-undo: \f0e2; +$fa-var-undo-alt: \f2ea; +$fa-var-uniregistry: \f404; +$fa-var-universal-access: \f29a; +$fa-var-university: \f19c; +$fa-var-unlink: \f127; +$fa-var-unlock: \f09c; +$fa-var-unlock-alt: \f13e; +$fa-var-untappd: \f405; +$fa-var-upload: \f093; +$fa-var-usb: \f287; +$fa-var-user: \f007; +$fa-var-user-circle: \f2bd; +$fa-var-user-md: \f0f0; +$fa-var-user-plus: \f234; +$fa-var-user-secret: \f21b; +$fa-var-user-times: \f235; +$fa-var-users: \f0c0; +$fa-var-ussunnah: \f407; +$fa-var-utensil-spoon: \f2e5; +$fa-var-utensils: \f2e7; +$fa-var-vaadin: \f408; +$fa-var-venus: \f221; +$fa-var-venus-double: \f226; +$fa-var-venus-mars: \f228; +$fa-var-viacoin: \f237; +$fa-var-viadeo: \f2a9; +$fa-var-viadeo-square: \f2aa; +$fa-var-viber: \f409; +$fa-var-video: \f03d; +$fa-var-vimeo: \f40a; +$fa-var-vimeo-square: \f194; +$fa-var-vimeo-v: \f27d; +$fa-var-vine: \f1ca; +$fa-var-vk: \f189; +$fa-var-vnv: \f40b; +$fa-var-volume-down: \f027; +$fa-var-volume-off: \f026; +$fa-var-volume-up: \f028; +$fa-var-vuejs: \f41f; +$fa-var-weibo: \f18a; +$fa-var-weixin: \f1d7; +$fa-var-whatsapp: \f232; +$fa-var-whatsapp-square: \f40c; +$fa-var-wheelchair: \f193; +$fa-var-whmcs: \f40d; +$fa-var-wifi: \f1eb; +$fa-var-wikipedia-w: \f266; +$fa-var-window-close: \f410; +$fa-var-window-maximize: \f2d0; +$fa-var-window-minimize: \f2d1; +$fa-var-window-restore: \f2d2; +$fa-var-windows: \f17a; +$fa-var-won-sign: \f159; +$fa-var-wordpress: \f19a; +$fa-var-wordpress-simple: \f411; +$fa-var-wpbeginner: \f297; +$fa-var-wpexplorer: \f2de; +$fa-var-wpforms: \f298; +$fa-var-wrench: \f0ad; +$fa-var-xbox: \f412; +$fa-var-xing: \f168; +$fa-var-xing-square: \f169; +$fa-var-y-combinator: \f23b; +$fa-var-yahoo: \f19e; +$fa-var-yandex: \f413; +$fa-var-yandex-international: \f414; +$fa-var-yelp: \f1e9; +$fa-var-yen-sign: \f157; +$fa-var-yoast: \f2b1; +$fa-var-youtube: \f167; diff --git a/docs/style/fa/fa-brands.scss b/docs/style/fa/fa-brands.scss new file mode 100644 index 000000000..ab89c020a --- /dev/null +++ b/docs/style/fa/fa-brands.scss @@ -0,0 +1,21 @@ +/*! + * Font Awesome Free 5.0.2 by @fontawesome - http://fontawesome.com + * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +@import 'variables'; + +@font-face { + font-family: 'Font Awesome 5 Brands'; + font-style: normal; + font-weight: normal; + src: url('#{$fa-font-path}/fa-brands-400.eot'); + src: url('#{$fa-font-path}/fa-brands-400.eot?#iefix') format('embedded-opentype'), + url('#{$fa-font-path}/fa-brands-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-brands-400.woff') format('woff'), + url('#{$fa-font-path}/fa-brands-400.ttf') format('truetype'), + url('#{$fa-font-path}/fa-brands-400.svg#fontawesome') format('svg'); +} + +.fab { + font-family: 'Font Awesome 5 Brands'; +} diff --git a/docs/style/fa/fa-regular.scss b/docs/style/fa/fa-regular.scss new file mode 100644 index 000000000..4f2882924 --- /dev/null +++ b/docs/style/fa/fa-regular.scss @@ -0,0 +1,22 @@ +/*! + * Font Awesome Free 5.0.2 by @fontawesome - http://fontawesome.com + * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +@import 'variables'; + +@font-face { + font-family: 'Font Awesome 5 Free'; + font-style: normal; + font-weight: 400; + src: url('#{$fa-font-path}/fa-regular-400.eot'); + src: url('#{$fa-font-path}/fa-regular-400.eot?#iefix') format('embedded-opentype'), + url('#{$fa-font-path}/fa-regular-400.woff2') format('woff2'), + url('#{$fa-font-path}/fa-regular-400.woff') format('woff'), + url('#{$fa-font-path}/fa-regular-400.ttf') format('truetype'), + url('#{$fa-font-path}/fa-regular-400.svg#fontawesome') format('svg'); +} + +.far { + font-family: 'Font Awesome 5 Free'; + font-weight: 400; +} diff --git a/docs/style/fa/fa-solid.scss b/docs/style/fa/fa-solid.scss new file mode 100644 index 000000000..bb0df52ab --- /dev/null +++ b/docs/style/fa/fa-solid.scss @@ -0,0 +1,23 @@ +/*! + * Font Awesome Free 5.0.2 by @fontawesome - http://fontawesome.com + * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +@import 'variables'; + +@font-face { + font-family: 'Font Awesome 5 Free'; + font-style: normal; + font-weight: 900; + src: url('#{$fa-font-path}/fa-solid-900.eot'); + src: url('#{$fa-font-path}/fa-solid-900.eot?#iefix') format('embedded-opentype'), + url('#{$fa-font-path}/fa-solid-900.woff2') format('woff2'), + url('#{$fa-font-path}/fa-solid-900.woff') format('woff'), + url('#{$fa-font-path}/fa-solid-900.ttf') format('truetype'), + url('#{$fa-font-path}/fa-solid-900.svg#fontawesome') format('svg'); +} + +.fa, +.fas { + font-family: 'Font Awesome 5 Free'; + font-weight: 900; +} diff --git a/docs/style/fa/fontawesome.scss b/docs/style/fa/fontawesome.scss new file mode 100644 index 000000000..04eb879a4 --- /dev/null +++ b/docs/style/fa/fontawesome.scss @@ -0,0 +1,16 @@ +/*! + * Font Awesome Free 5.0.2 by @fontawesome - http://fontawesome.com + * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + */ +@import 'variables'; +@import 'mixins'; +@import 'core'; +@import 'larger'; +@import 'fixed-width'; +@import 'list'; +@import 'bordered-pulled'; +@import 'animated'; +@import 'rotated-flipped'; +@import 'stacked'; +@import 'icons'; +@import 'screen-reader'; diff --git a/docs/style/style.scss b/docs/style/style.scss new file mode 100644 index 000000000..2c66a4c94 --- /dev/null +++ b/docs/style/style.scss @@ -0,0 +1,62 @@ +@import "./syntax"; + +$primary: #C93312; +$warning-invert: #FFFFFF; +$family-sans-serif: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif, 'Font Awesome 5 Free', 'Font Awesome 5 Brands' !default; +@import "../node_modules/bulma/sass/utilities/_all"; +@import "../node_modules/bulma/sass/base/_all"; +@import "../node_modules/bulma/sass/grid/_all"; +@import "../node_modules/bulma/sass/elements/_all"; +@import "../node_modules/bulma/sass/components/_all"; +@import "../node_modules/bulma/sass/layout/_all"; + +.sidebody { + height: 100vh; + overflow-x: hidden; + overflow-y: scroll; +} + +.example { + .highlight { + margin: 0; + } + .path { + font-style: italic; + width: 100%; + text-align: right; + } + margin-bottom: 1em; +} + + +.sidebar { + .version { + padding: 1em; + } + .brand { + background-color: #303030; + color: #c0c0c0; + padding: 1em; + top: 0; + } + .menu { + padding: 1em; + } +} + +.mainbody { + padding: 3em; +} + +code { + color: #1a9f1a; + font-size: 0.875em; + font-weight: normal; +} + +.content { + h2 { + padding-top: 1em; + border-top: 1px solid #c0c0c0; + } +} diff --git a/docs/style/syntax.css b/docs/style/syntax.css new file mode 100644 index 000000000..33ce367c9 --- /dev/null +++ b/docs/style/syntax.css @@ -0,0 +1,59 @@ +/* Background */ .chroma { color: #f8f8f2; background-color: #272822 } +/* Error */ .chroma .err { color: #960050; background-color: #1e0010 } +/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; } +/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: 100%; overflow: auto; display: block; } +/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc } +/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; display: block; } +/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; } +/* Keyword */ .chroma .k { color: #66d9ef } +/* KeywordConstant */ .chroma .kc { color: #66d9ef } +/* KeywordDeclaration */ .chroma .kd { color: #66d9ef } +/* KeywordNamespace */ .chroma .kn { color: #f92672 } +/* KeywordPseudo */ .chroma .kp { color: #66d9ef } +/* KeywordReserved */ .chroma .kr { color: #66d9ef } +/* KeywordType */ .chroma .kt { color: #66d9ef } +/* NameAttribute */ .chroma .na { color: #a6e22e } +/* NameClass */ .chroma .nc { color: #a6e22e } +/* NameConstant */ .chroma .no { color: #66d9ef } +/* NameDecorator */ .chroma .nd { color: #a6e22e } +/* NameException */ .chroma .ne { color: #a6e22e } +/* NameFunction */ .chroma .nf { color: #a6e22e } +/* NameOther */ .chroma .nx { color: #a6e22e } +/* NameTag */ .chroma .nt { color: #f92672 } +/* Literal */ .chroma .l { color: #ae81ff } +/* LiteralDate */ .chroma .ld { color: #e6db74 } +/* LiteralString */ .chroma .s { color: #e6db74 } +/* LiteralStringAffix */ .chroma .sa { color: #e6db74 } +/* LiteralStringBacktick */ .chroma .sb { color: #e6db74 } +/* LiteralStringChar */ .chroma .sc { color: #e6db74 } +/* LiteralStringDelimiter */ .chroma .dl { color: #e6db74 } +/* LiteralStringDoc */ .chroma .sd { color: #e6db74 } +/* LiteralStringDouble */ .chroma .s2 { color: #e6db74 } +/* LiteralStringEscape */ .chroma .se { color: #ae81ff } +/* LiteralStringHeredoc */ .chroma .sh { color: #e6db74 } +/* LiteralStringInterpol */ .chroma .si { color: #e6db74 } +/* LiteralStringOther */ .chroma .sx { color: #e6db74 } +/* LiteralStringRegex */ .chroma .sr { color: #e6db74 } +/* LiteralStringSingle */ .chroma .s1 { color: #e6db74 } +/* LiteralStringSymbol */ .chroma .ss { color: #e6db74 } +/* LiteralNumber */ .chroma .m { color: #ae81ff } +/* LiteralNumberBin */ .chroma .mb { color: #ae81ff } +/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff } +/* LiteralNumberHex */ .chroma .mh { color: #ae81ff } +/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff } +/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff } +/* LiteralNumberOct */ .chroma .mo { color: #ae81ff } +/* Operator */ .chroma .o { color: #f92672 } +/* OperatorWord */ .chroma .ow { color: #f92672 } +/* Comment */ .chroma .c { color: #75715e } +/* CommentHashbang */ .chroma .ch { color: #75715e } +/* CommentMultiline */ .chroma .cm { color: #75715e } +/* CommentSingle */ .chroma .c1 { color: #75715e } +/* CommentSpecial */ .chroma .cs { color: #75715e } +/* CommentPreproc */ .chroma .cp { color: #75715e } +/* CommentPreprocFile */ .chroma .cpf { color: #75715e } +/* GenericDeleted */ .chroma .gd { color: #f92672 } +/* GenericEmph */ .chroma .ge { font-style: italic } +/* GenericInserted */ .chroma .gi { color: #a6e22e } +/* GenericStrong */ .chroma .gs { font-weight: bold } +/* GenericSubheading */ .chroma .gu { color: #75715e } diff --git a/docs/transparent.rst b/docs/transparent.rst deleted file mode 100644 index 889079afd..000000000 --- a/docs/transparent.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. _transparent: - -==================== -Transparent Proxying -==================== - -When a transparent proxy is used, traffic is redirected into a proxy at the -network layer, without any client configuration being required. This makes -transparent proxying ideal for those situations where you can't change client -behaviour - proxy-oblivious Android applications being a common example. - -To set up transparent proxying, we need two new components. The first is a -redirection mechanism that transparently reroutes a TCP connection destined for -a server on the Internet to a listening proxy server. This usually takes the -form of a firewall on the same host as the proxy server - iptables_ on Linux -or pf_ on OSX. When the proxy receives a redirected connection, it sees a vanilla -HTTP request, without a host specification. This is where the second new component -comes in - a host module that allows us to query the redirector for the original -destination of the TCP connection. - -At the moment, mitmproxy supports transparent proxying on OSX Lion and above, -and all current flavors of Linux. - -Fully transparent mode -====================== - -By default mitmproxy will use its own local ip address for its server-side connections. -In case this isn't desired, the --spoof-source-address argument can be used to -use the client's ip address for server-side connections. The following config is -required for this mode to work:: - - CLIENT_NET=192.168.1.0/24 - TABLE_ID=100 - MARK=1 - - echo "$TABLE_ID mitmproxy" >> /etc/iproute2/rt_tables - iptables -t mangle -A PREROUTING -d $CLIENT_NET -j MARK --set-mark $MARK - iptables -t nat -A PREROUTING -p tcp -s $CLIENT_NET --match multiport --dports 80,443 -j REDIRECT --to-port 8080 - - ip rule add fwmark $MARK lookup $TABLE_ID - ip route add local $CLIENT_NET dev lo table $TABLE_ID - -This mode does require root privileges though. There's a wrapper in the examples directory -called 'mitmproxy_shim.c', which will enable you to use this mode with dropped priviliges. -It can be used as follows:: - - gcc examples/complex/full_transparency_shim.c -o mitmproxy_shim -lcap - sudo chown root:root mitmproxy_shim - sudo chmod u+s mitmproxy_shim - ./mitmproxy_shim $(which mitmproxy) -T --spoof-source-address - -.. _iptables: http://www.netfilter.org/ -.. _pf: https://en.wikipedia.org/wiki/PF_\(firewall\) diff --git a/docs/transparent/linux.rst b/docs/transparent/linux.rst deleted file mode 100644 index 14f6a1658..000000000 --- a/docs/transparent/linux.rst +++ /dev/null @@ -1,49 +0,0 @@ -.. _linux: - -Linux -===== - -On Linux, mitmproxy integrates with the iptables redirection mechanism to -achieve transparent mode. - - 1. :ref:`Install the mitmproxy certificate on the test device ` - - 2. Enable IP forwarding: - - >>> sysctl -w net.ipv4.ip_forward=1 - >>> sysctl -w net.ipv6.conf.all.forwarding=1 - - You may also want to consider enabling this permanently in ``/etc/sysctl.conf`` or newly created ``/etc/sysctl.d/mitmproxy.conf``, see `here `__. - - 3. If your target machine is on the same physical network and you configured it to use a custom - gateway, disable ICMP redirects: - - >>> sysctl -w net.ipv4.conf.all.send_redirects=0 - -    You may also want to consider enabling this permanently in ``/etc/sysctl.conf`` or a newly created ``/etc/sysctl.d/mitmproxy.conf``, see `here `__. - - 4. Create an iptables ruleset that redirects the desired traffic to the - mitmproxy port. Details will differ according to your setup, but the - ruleset should look something like this: - - .. code-block:: none - - iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080 - iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8080 - ip6tables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080 - ip6tables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8080 - -    You may also want to consider enabling this permanently with the ``iptables-persistent`` package, see `here `__. - - 5. Fire up mitmproxy. You probably want a command like this: - - >>> mitmproxy -T --host - - The ``-T`` flag turns on transparent mode, and the ``--host`` - argument tells mitmproxy to use the value of the Host header for URL display. - - 6. Finally, configure your test device to use the host on which mitmproxy is - running as the default gateway. - - -For a detailed walkthrough, have a look at the :ref:`transparent-dhcp` tutorial. diff --git a/docs/transparent/openbsd.rst b/docs/transparent/openbsd.rst deleted file mode 100644 index 3d315f7ce..000000000 --- a/docs/transparent/openbsd.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. _openbsd: - -OpenBSD -======= - - 1. :ref:`Install the mitmproxy certificate on the test device ` - - 2. Enable IP forwarding: - - >>> sudo sysctl -w net.inet.ip.forwarding=1 - - 3. Place the following two lines in **/etc/pf.conf**: - - .. code-block:: none - - mitm_if = "re2" - pass in quick proto tcp from $mitm_if to port { 80, 443 } divert-to 127.0.0.1 port 8080 - - These rules tell pf to divert all traffic from ``$mitm_if`` destined for - port 80 or 443 to the local mitmproxy instance running on port 8080. You - should replace ``$mitm_if`` value with the interface on which your test - device will appear. - - 4. Configure pf with the rules: - - >>> doas pfctl -f /etc/pf.conf - - 5. And now enable it: - - >>> doas pfctl -e - - 6. Fire up mitmproxy. You probably want a command like this: - - >>> mitmproxy -T --host - - The ``-T`` flag turns on transparent mode, and the ``--host`` - argument tells mitmproxy to use the value of the Host header for URL display. - - 7. Finally, configure your test device to use the host on which mitmproxy is - running as the default gateway. - -.. note:: - - Note that the **divert-to** rules in the pf.conf given above only apply to - inbound traffic. **This means that they will NOT redirect traffic coming - from the box running pf itself.** We can't distinguish between an outbound - connection from a non-mitmproxy app, and an outbound connection from - mitmproxy itself - if you want to intercept your traffic, you should use an - external host to run mitmproxy. Nonetheless, pf is flexible to cater for a - range of creative possibilities, like intercepting traffic emanating from - VMs. See the **pf.conf** man page for more. - -.. _pf: http://man.openbsd.org/OpenBSD-current/man5/pf.conf.5 diff --git a/docs/transparent/osx.rst b/docs/transparent/osx.rst deleted file mode 100644 index 5d4ec6120..000000000 --- a/docs/transparent/osx.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. _osx: - -OSX -=== - -OSX Lion integrated the pf_ packet filter from the OpenBSD project, -which mitmproxy uses to implement transparent mode on OSX. -Note that this means we don't support transparent mode for earlier versions of OSX. - - 1. :ref:`Install the mitmproxy certificate on the test device ` - - 2. Enable IP forwarding: - - >>> sudo sysctl -w net.inet.ip.forwarding=1 - - 3. Place the following two lines in a file called, say, **pf.conf**: - - .. code-block:: none - - rdr on en0 inet proto tcp to any port {80, 443} -> 127.0.0.1 port 8080 - - These rules tell pf to redirect all traffic destined for port 80 or 443 - to the local mitmproxy instance running on port 8080. You should - replace ``en2`` with the interface on which your test device will appear. - - 4. Configure pf with the rules: - - >>> sudo pfctl -f pf.conf - - 5. And now enable it: - - >>> sudo pfctl -e - - 6. Configure sudoers to allow mitmproxy to access pfctl. Edit the file - **/etc/sudoers** on your system as root. Add the following line to the end - of the file: - - .. code-block:: none - - ALL ALL=NOPASSWD: /sbin/pfctl -s state - - Note that this allows any user on the system to run the command - ``/sbin/pfctl -s state`` as root without a password. This only allows - inspection of the state table, so should not be an undue security risk. If - you're special feel free to tighten the restriction up to the user running - mitmproxy. - - 7. Fire up mitmproxy. You probably want a command like this: - - >>> mitmproxy -T --host - - The ``-T`` flag turns on transparent mode, and the ``--host`` - argument tells mitmproxy to use the value of the Host header for URL display. - - 8. Finally, configure your test device to use the host on which mitmproxy is - running as the default gateway. - -.. note:: - - Note that the **rdr** rules in the pf.conf given above only apply to inbound - traffic. **This means that they will NOT redirect traffic coming from the box - running pf itself.** We can't distinguish between an outbound connection from a - non-mitmproxy app, and an outbound connection from mitmproxy itself - if you - want to intercept your OSX traffic, you should use an external host to run - mitmproxy. Nonetheless, pf is flexible to cater for a range of creative - possibilities, like intercepting traffic emanating from VMs. See the - **pf.conf** man page for more. - -.. _pf: https://en.wikipedia.org/wiki/PF_\(firewall\) diff --git a/docs/tutorials/30second.rst b/docs/tutorials/30second.rst deleted file mode 100644 index 4c8bf3267..000000000 --- a/docs/tutorials/30second.rst +++ /dev/null @@ -1,66 +0,0 @@ -.. _30second: - -Client playback: a 30 second example -==================================== - -My local cafe is serviced by a rickety and unreliable wireless network, -generously sponsored with ratepayers' money by our city council. After -connecting, you are redirected to an SSL-protected page that prompts you for a -username and password. Once you've entered your details, you are free to enjoy -the intermittent dropouts, treacle-like speeds and incorrectly configured -transparent proxy. - -I tend to automate this kind of thing at the first opportunity, on the theory -that time spent now will be more than made up in the long run. In this case, I -might use Firebug_ to ferret out the form post -parameters and target URL, then fire up an editor to write a little script -using Python's urllib_ to simulate a submission. -That's a lot of futzing about. With mitmproxy we can do the job -in literally 30 seconds, without having to worry about any of the details. -Here's how. - -1. Run mitmdump to record our HTTP conversation to a file. ----------------------------------------------------------- - ->>> mitmdump -w wireless-login - -2. Point your browser at the mitmdump instance. ------------------------------------------------ - -I use a tiny Firefox addon called `Toggle Proxy`_ to switch quickly to and from mitmproxy. -I'm assuming you've already :ref:`configured -your browser with mitmproxy's SSL certificate -authority `. - -3. Log in as usual. -------------------- - -And that's it! You now have a serialized version of the login process in the -file wireless-login, and you can replay it at any time like this: - ->>> mitmdump -c wireless-login - -Embellishments --------------- - -We're really done at this point, but there are a couple of embellishments we -could make if we wanted. I use wicd_ to -automatically join wireless networks I frequent, and it lets me specify a -command to run after connecting. I used the client replay command above and -voila! - totally hands-free wireless network startup. - -We might also want to prune requests that download CSS, JS, images and so -forth. These add only a few moments to the time it takes to replay, but they're -not really needed and I somehow feel compelled to trim them anyway. So, we fire up -the mitmproxy console tool on our serialized conversation, like so: - ->>> mitmproxy -r wireless-login - -We can now go through and manually delete (using the :kbd:`d` keyboard shortcut) -everything we want to trim. When we're done, we use :kbd:`w` to save the -conversation back to the file. - -.. _Firebug: https://getfirebug.com/ -.. _urllib: https://docs.python.org/library/urllib.html -.. _Toggle Proxy: https://addons.mozilla.org/en-us/firefox/addon/toggle-proxy-51740/ -.. _wicd: https://launchpad.net/wicd diff --git a/docs/tutorials/gamecenter.rst b/docs/tutorials/gamecenter.rst deleted file mode 100644 index d0d73b736..000000000 --- a/docs/tutorials/gamecenter.rst +++ /dev/null @@ -1,128 +0,0 @@ -.. _gamecenter: - -Setting highscores on Apple's GameCenter -======================================== - -The setup ---------- - -In this tutorial, I'm going to show you how simple it is to creatively -interfere with Apple Game Center traffic using mitmproxy. To set things up, -:ref:`install the mitmproxy root certificate `. Then -start mitmproxy on your desktop, and configure the iPhone to use it as a proxy. - - -Taking a look at the Game Center traffic ----------------------------------------- - -Lets take a first look at the Game Center traffic. The game I'll use in this -tutorial is `Super Mega Worm`_ - a great little retro-apocalyptic sidescroller for the iPhone: - -.. image:: supermega.png - :align: center - - -After finishing a game (take your time), watch the traffic flowing through -mitmproxy: - -.. image:: one.png - :align: center - -We see a bunch of things we might expect - initialisation, the retrieval of -leaderboards and so forth. Then, right at the end, there's a POST to this -tantalising URL: - -.. code-block:: none - - https://service.gc.apple.com/WebObjects/GKGameStatsService.woa/wa/submitScore - -The contents of the submission are particularly interesting: - -.. code-block:: xml - - - - - scores - - - category - SMW_Adv_USA1 - context - 0 - score-value - 55 - timestamp - 1363515361321 - - - - - - -This is a `property list`_, containing an identifier for the game, -a score (55, in this case), and a timestamp. Looks pretty simple to mess with. - -Modifying and replaying the score submission --------------------------------------------- - -Lets edit the score submission. First, select it in mitmproxy, then press -:kbd:`enter` to view it. Make sure you're viewing the request, not the response - -you can use :kbd:`tab` to flick between the two. Now press :kbd:`e` for edit. You'll -be prompted for the part of the request you want to change - press :kbd:`r` for -raw body. Your preferred editor (taken from the EDITOR environment variable) will -now fire up. Lets bump the score up to something a bit more ambitious: - -.. code-block:: xml - - - - - scores - - - category - SMW_Adv_USA1 - context - 0 - score-value - 2200272667 - timestamp - 1363515361321 - - - - - - -Save the file and exit your editor. - -The final step is to replay this modified request. Simply press :kbd:`r` for replay. - - -The glorious result and some intrigue -------------------------------------- - -.. image:: leaderboard.png - :align: center - -And that's it - according to the records, I am the greatest Super Mega Worm -player of all time. - -There's a curious addendum to this tale. When I first wrote this tutorial, all -the top competitors' scores were the same: 2,147,483,647 (this is no longer the -case, because there are now so many fellow cheaters using this tutorial). If -you think that number seems familiar, you're right: it's 2^31-1, the maximum -value you can fit into a signed 32-bit int. Now let me tell you another -peculiar thing about Super Mega Worm - at the end of every game, it submits -your highest previous score to the Game Center, not your current score. This -means that it stores your highscore somewhere, and I'm guessing that it reads -that stored score back into a signed integer. So, if you _were_ to cheat by the -relatively pedestrian means of modifying the saved score on your jailbroken -phone, then 2^31-1 might well be the maximum score you could get. Then again, -if the game itself stores its score in a signed 32-bit int, you could get the -same score through perfect play, effectively beating the game. So, which is it -in this case? I'll leave that for you to decide. - -.. _Super Mega Worm: https://itunes.apple.com/us/app/super-mega-worm/id388541990?mt=8 -.. _property list: https://en.wikipedia.org/wiki/Property_list diff --git a/docs/tutorials/transparent-dhcp.rst b/docs/tutorials/transparent-dhcp.rst deleted file mode 100644 index d993707de..000000000 --- a/docs/tutorials/transparent-dhcp.rst +++ /dev/null @@ -1,101 +0,0 @@ -.. _transparent-dhcp: - -Transparently proxify virtual machines -====================================== - -This walkthrough illustrates how to set up transparent proxying with mitmproxy. -We use VirtualBox VMs with an Ubuntu proxy machine in this example, -but the general *Internet <--> Proxy VM <--> (Virtual) Internal Network* setup can be applied to -other setups. - -1. Configure Proxy VM ---------------------- - -On the proxy machine, **eth0** is connected to the internet. **eth1** is connected to the internal -network that will be proxified and configured to use a static ip (192.168.3.1). - -VirtualBox configuration -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: transparent-dhcp/step1_vbox_eth0.png - -.. image:: transparent-dhcp/step1_vbox_eth1.png - -VM Network Configuration -^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: transparent-dhcp/step1_proxy.png - :align: center - -2. Configure DHCP and DNS -------------------------- - -We use dnsmasq to provide DHCP and DNS in our internal network. -Dnsmasq is a lightweight server designed to provide DNS (and optionally -DHCP and TFTP) services to a small-scale network. - -- Before we get to that, we need to fix some Ubuntu quirks: - **Ubuntu >12.04** runs an internal dnsmasq instance (listening on loopback only) by default - `[1] `_. For our use case, this needs - to be disabled by changing ``dns=dnsmasq`` to ``#dns=dnsmasq`` in - **/etc/NetworkManager/NetworkManager.conf** and - - if on Ubuntu 16.04 or newer running: - - >>> sudo systemctl restart NetworkManager - - if on Ubuntu 12.04 or 14.04 running: - - >>> sudo restart network-manager - - afterwards. -- Now, dnsmasq can be be installed and configured: - - >>> sudo apt-get install dnsmasq - - Replace **/etc/dnsmasq.conf** with the following configuration: - - .. code-block:: none - - # Listen for DNS requests on the internal network - interface=eth1 - # Act as a DHCP server, assign IP addresses to clients - dhcp-range=192.168.3.10,192.168.3.100,96h - # Broadcast gateway and dns server information - dhcp-option=option:router,192.168.3.1 - dhcp-option=option:dns-server,192.168.3.1 - - Apply changes: - - if on Ubuntu 16.04 or newer: - - >>> sudo systemctl restart dnsmasq - - if on Ubuntu 12.04 or 14.04: - - >>> sudo service dnsmasq restart - - Your **proxied machine** in the internal virtual network should now receive an IP address via DHCP: - - .. image:: transparent-dhcp/step2_proxied_vm.png - -3. Redirect traffic to mitmproxy ------------------------------------------- - -To redirect traffic to mitmproxy, we need to add two iptables rules: - -.. code-block:: none - - sudo iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 80 -j REDIRECT --to-port 8080 - sudo iptables -t nat -A PREROUTING -i eth1 -p tcp --dport 443 -j REDIRECT --to-port 8080 - -4. Run mitmproxy ----------------- - -Finally, we can run mitmproxy in transparent mode with - ->>> mitmproxy -T - -The proxied machine cannot to leak any data outside of HTTP or DNS requests. -If required, you can now :ref:`install the mitmproxy certificates on the proxied machine -`. diff --git a/docs/yarn.lock b/docs/yarn.lock new file mode 100644 index 000000000..5f5b2ee6c --- /dev/null +++ b/docs/yarn.lock @@ -0,0 +1,1203 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +ajv@^5.1.0: + version "5.5.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.1.tgz#b38bb8876d9e86bee994956a04e721e88b248eb2" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +aproba@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +async-foreach@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.2.1, aws4@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +boom@4.x.x: + version "4.3.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" + dependencies: + hoek "4.x.x" + +boom@5.x.x: + version "5.2.0" + resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" + dependencies: + hoek "4.x.x" + +brace-expansion@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +bulma@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.6.1.tgz#5f21a77c0c06f7d80051c06628c23516081bd649" + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +camelcase@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +chalk@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +cliui@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@^2.9.0: + version "2.12.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cross-spawn@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" + dependencies: + lru-cache "^4.0.1" + which "^1.2.9" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +cryptiles@3.x.x: + version "3.1.2" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" + dependencies: + boom "5.x.x" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +decamelize@^1.1.1, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +error-ex@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +extend@~3.0.0, extend@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fast-deep-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +form-data@~2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fstream@^1.0.0, fstream@^1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gaze@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.2.tgz#847224677adb8870d679257ed3388fdb61e40105" + dependencies: + globule "^1.0.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + +get-caller-file@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +glob@^6.0.4: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.1.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globule@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.0.tgz#1dc49c6822dd9e8a2fa00ba2a295006e8664bd09" + dependencies: + glob "~7.1.1" + lodash "~4.17.4" + minimatch "~3.0.2" + +graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + +har-validator@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hawk@~6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" + dependencies: + boom "4.x.x" + cryptiles "3.x.x" + hoek "4.x.x" + sntp "2.x.x" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +hoek@4.x.x: + version "4.2.0" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d" + +hosted-git-info@^2.1.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +in-publish@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + dependencies: + repeating "^2.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@~2.0.0, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-my-json-valid@^2.12.4: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +js-base64@^2.1.8: + version "2.4.0" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.0.tgz#9e566fee624751a1d720c966cd6226d29d4025aa" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +lodash.assign@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" + +lodash.clonedeep@^4.3.2: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + +lodash.mergewith@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55" + +lodash@^4.0.0, lodash@~4.17.4: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lru-cache@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +meow@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +mime-db@~1.30.0: + version "1.30.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" + +mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.7: + version "2.1.17" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a" + dependencies: + mime-db "~1.30.0" + +"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.1.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +nan@^2.3.2: + version "2.8.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a" + +node-gyp@^3.3.1: + version "3.6.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + minimatch "^3.0.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "2" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + +node-sass@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e" + dependencies: + async-foreach "^0.1.3" + chalk "^1.1.1" + cross-spawn "^3.0.0" + gaze "^1.0.0" + get-stdin "^4.0.1" + glob "^7.0.3" + in-publish "^2.0.0" + lodash.assign "^4.2.0" + lodash.clonedeep "^4.3.2" + lodash.mergewith "^4.6.0" + meow "^3.7.0" + mkdirp "^0.5.1" + nan "^2.3.2" + node-gyp "^3.3.1" + npmlog "^4.0.0" + request "~2.79.0" + sass-graph "^2.2.4" + stdout-stream "^1.4.0" + "true-case-path" "^1.0.2" + +"nopt@2 || 3": + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1, oauth-sign@~0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.0.1, object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@0: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + dependencies: + error-ex "^1.2.0" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@~6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" + +qs@~6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +readable-stream@^2.0.1, readable-stream@^2.0.6: + version "2.3.3" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +request@2: + version "2.83.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +request@~2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +rimraf@2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +sass-graph@^2.2.4: + version "2.2.4" + resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" + dependencies: + glob "^7.0.0" + lodash "^4.0.0" + scss-tokenizer "^0.2.3" + yargs "^7.0.0" + +scss-tokenizer@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" + dependencies: + js-base64 "^2.1.8" + source-map "^0.4.2" + +"semver@2 || 3 || 4 || 5": + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + +semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +sntp@2.x.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" + dependencies: + hoek "4.x.x" + +source-map@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +spdx-correct@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-1.0.2.tgz#4b3073d933ff51f3912f03ac5519498a4150db40" + dependencies: + spdx-license-ids "^1.0.2" + +spdx-expression-parse@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz#9bdf2f20e1f40ed447fbe273266191fced51626c" + +spdx-license-ids@^1.0.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +sshpk@^1.7.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +stdout-stream@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.0.tgz#a2c7c8587e54d9427ea9edb3ac3f2cd522df378b" + dependencies: + readable-stream "^2.0.1" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +stringstream@~0.0.4, stringstream@~0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + dependencies: + is-utf8 "^0.2.0" + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + dependencies: + get-stdin "^4.0.1" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +tar@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +tough-cookie@~2.3.0, tough-cookie@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" + dependencies: + punycode "^1.4.1" + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + +"true-case-path@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.2.tgz#7ec91130924766c7f573be3020c34f8fdfd00d62" + dependencies: + glob "^6.0.4" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +uuid@^3.0.0, uuid@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + +validate-npm-package-license@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc" + dependencies: + spdx-correct "~1.0.0" + spdx-expression-parse "~1.0.0" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +which-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" + +which@1, which@^1.2.9: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + dependencies: + string-width "^1.0.2" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +xtend@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + dependencies: + camelcase "^3.0.0" + +yargs@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^5.0.0" diff --git a/examples/addons/addheader.py b/examples/addons/addheader.py new file mode 100644 index 000000000..f4b29268e --- /dev/null +++ b/examples/addons/addheader.py @@ -0,0 +1,13 @@ + +class AddHeader: + def __init__(self): + self.num = 0 + + def response(self, flow): + self.num = self.num + 1 + flow.response.headers["count"] = str(self.num) + + +addons = [ + AddHeader() +] diff --git a/examples/addons/anatomy.py b/examples/addons/anatomy.py new file mode 100644 index 000000000..c60afeaa4 --- /dev/null +++ b/examples/addons/anatomy.py @@ -0,0 +1,15 @@ +from mitmproxy import ctx + + +class Counter: + def __init__(self): + self.num = 0 + + def request(self, flow): + self.num = self.num + 1 + ctx.log.info("We've seen %d flows" % self.num) + + +addons = [ + Counter() +] diff --git a/examples/addons/commands-flows.py b/examples/addons/commands-flows.py new file mode 100644 index 000000000..cebc8f9da --- /dev/null +++ b/examples/addons/commands-flows.py @@ -0,0 +1,21 @@ +import typing + +from mitmproxy import command +from mitmproxy import ctx +from mitmproxy import flow + + +class MyAddon: + def __init__(self): + self.num = 0 + + @command.command("myaddon.addheader") + def addheader(self, flows: typing.Sequence[flow.Flow]) -> None: + for f in flows: + f.request.headers["myheader"] = "value" + ctx.log.alert("done") + + +addons = [ + MyAddon() +] diff --git a/examples/addons/commands-paths.py b/examples/addons/commands-paths.py new file mode 100644 index 000000000..f37a0fbc5 --- /dev/null +++ b/examples/addons/commands-paths.py @@ -0,0 +1,32 @@ +import typing + +from mitmproxy import command +from mitmproxy import ctx +from mitmproxy import flow +from mitmproxy import types + + +class MyAddon: + def __init__(self): + self.num = 0 + + @command.command("myaddon.histogram") + def histogram( + self, + flows: typing.Sequence[flow.Flow], + path: types.Path, + ) -> None: + totals = {} + for f in flows: + totals[f.request.host] = totals.setdefault(f.request.host, 0) + 1 + + fp = open(path, "w+") + for cnt, dom in sorted([(v, k) for (k, v) in totals.items()]): + fp.write("%s: %s\n" % (cnt, dom)) + + ctx.log.alert("done") + + +addons = [ + MyAddon() +] diff --git a/examples/addons/commands-simple.py b/examples/addons/commands-simple.py new file mode 100644 index 000000000..c9cd63414 --- /dev/null +++ b/examples/addons/commands-simple.py @@ -0,0 +1,17 @@ +from mitmproxy import command +from mitmproxy import ctx + + +class MyAddon: + def __init__(self): + self.num = 0 + + @command.command("myaddon.inc") + def inc(self) -> None: + self.num += 1 + ctx.log.info("num = %s" % self.num) + + +addons = [ + MyAddon() +] diff --git a/examples/addons/events.py b/examples/addons/events.py new file mode 100644 index 000000000..936649546 --- /dev/null +++ b/examples/addons/events.py @@ -0,0 +1,179 @@ +import typing + +import mitmproxy.addonmanager +import mitmproxy.connections +import mitmproxy.http +import mitmproxy.log +import mitmproxy.tcp +import mitmproxy.websocket +import mitmproxy.proxy.protocol + + +class Events: + # HTTP lifecycle + def http_connect(self, flow: mitmproxy.http.HTTPFlow): + """ + An HTTP CONNECT request was received. Setting a non 2xx response on + the flow will return the response to the client abort the + connection. CONNECT requests and responses do not generate the usual + HTTP handler events. CONNECT requests are only valid in regular and + upstream proxy modes. + """ + + def requestheaders(self, flow: mitmproxy.http.HTTPFlow): + """ + HTTP request headers were successfully read. At this point, the body + is empty. + """ + + def request(self, flow: mitmproxy.http.HTTPFlow): + """ + The full HTTP request has been read. + """ + + def responseheaders(self, flow: mitmproxy.http.HTTPFlow): + """ + HTTP response headers were successfully read. At this point, the body + is empty. + """ + + def response(self, flow: mitmproxy.http.HTTPFlow): + """ + The full HTTP response has been read. + """ + + def error(self, flow: mitmproxy.http.HTTPFlow): + """ + An HTTP error has occurred, e.g. invalid server responses, or + interrupted connections. This is distinct from a valid server HTTP + error response, which is simply a response with an HTTP error code. + """ + + # TCP lifecycle + def tcp_start(self, flow: mitmproxy.tcp.TCPFlow): + """ + A TCP connection has started. + """ + + def tcp_message(self, flow: mitmproxy.tcp.TCPFlow): + """ + A TCP connection has received a message. The most recent message + will be flow.messages[-1]. The message is user-modifiable. + """ + + def tcp_error(self, flow: mitmproxy.tcp.TCPFlow): + """ + A TCP error has occurred. + """ + + def tcp_end(self, flow: mitmproxy.tcp.TCPFlow): + """ + A TCP connection has ended. + """ + + # Websocket lifecycle + def websocket_handshake(self, flow: mitmproxy.http.HTTPFlow): + """ + Called when a client wants to establish a WebSocket connection. The + WebSocket-specific headers can be manipulated to alter the + handshake. The flow object is guaranteed to have a non-None request + attribute. + """ + + def websocket_start(self, flow: mitmproxy.websocket.WebsocketFlow): + """ + A websocket connection has commenced. + """ + + def websocket_message(self, flow: mitmproxy.websocket.WebsocketFlow): + """ + Called when a WebSocket message is received from the client or + server. The most recent message will be flow.messages[-1]. The + message is user-modifiable. Currently there are two types of + messages, corresponding to the BINARY and TEXT frame types. + """ + + def websocket_error(self, flow: mitmproxy.websocket.WebsocketFlow): + """ + A websocket connection has had an error. + """ + + def websocket_end(self, flow: mitmproxy.websocket.WebsocketFlow): + """ + A websocket connection has ended. + """ + + # Network lifecycle + def clientconnect(self, layer: mitmproxy.proxy.protocol.Layer): + """ + A client has connected to mitmproxy. Note that a connection can + correspond to multiple HTTP requests. + """ + + def clientdisconnect(self, layer: mitmproxy.proxy.protocol.Layer): + """ + A client has disconnected from mitmproxy. + """ + + def serverconnect(self, conn: mitmproxy.connections.ServerConnection): + """ + Mitmproxy has connected to a server. Note that a connection can + correspond to multiple requests. + """ + + def serverdisconnect(self, conn: mitmproxy.connections.ServerConnection): + """ + Mitmproxy has disconnected from a server. + """ + + def next_layer(self, layer: mitmproxy.proxy.protocol.Layer): + """ + Network layers are being switched. You may change which layer will + be used by returning a new layer object from this event. + """ + + # General lifecycle + def configure(self, updated: typing.Set[str]): + """ + Called when configuration changes. The updated argument is a + set-like object containing the keys of all changed options. This + event is called during startup with all options in the updated set. + """ + + def done(self): + """ + Called when the addon shuts down, either by being removed from the + mitmproxy instance, or when mitmproxy itself shuts down. + """ + + def load(self, entry: mitmproxy.addonmanager.Loader): + """ + Called when an addon is first loaded. This event receives a Loader + object, which contains methods for adding options and commands. This + method is where the addon configures itself. + """ + + def log(self, entry: mitmproxy.log.LogEntry): + """ + Called whenver a new log entry is created through the mitmproxy + context. Be careful not to log from this event, which will cause an + infinite loop! + """ + + def running(self): + """ + Called when the proxy is completely up and running. At this point, + you can expect the proxy to be bound to a port, and all addons to be + loaded. + """ + + def tick(self): + """ + A regular ticker - called approximately once every 100ms. + """ + + def update(self, flows: typing.Sequence[mitmproxy.flow.Flow]): + """ + Update is called when one or more flow objects have been modified, + usually from a different addon. + """ diff --git a/examples/addons/options-configure.py b/examples/addons/options-configure.py new file mode 100644 index 000000000..c7638e87f --- /dev/null +++ b/examples/addons/options-configure.py @@ -0,0 +1,28 @@ +import typing + +from mitmproxy import ctx +from mitmproxy import exceptions + + +class AddHeader: + def load(self, loader): + loader.add_option( + name = "addheader", + typespec = typing.Optional[int], + default = None, + help = "Add a header to responses", + ) + + def configure(self, updates): + if "addheader" in updates: + if ctx.options.addheader is not None and ctx.options.addheader > 100: + raise exceptions.OptionsError("addheader must be <= 100") + + def response(self, flow): + if ctx.options.addheader is not None: + flow.response.headers["addheader"] = str(ctx.options.addheader) + + +addons = [ + AddHeader() +] diff --git a/examples/addons/options-simple.py b/examples/addons/options-simple.py new file mode 100644 index 000000000..0acefb3f7 --- /dev/null +++ b/examples/addons/options-simple.py @@ -0,0 +1,24 @@ +from mitmproxy import ctx + + +class AddHeader: + def __init__(self): + self.num = 0 + + def load(self, loader): + loader.add_option( + name = "addheader", + typespec = bool, + default = False, + help = "Add a count header to responses", + ) + + def response(self, flow): + if ctx.options.addheader: + self.num = self.num + 1 + flow.response.headers["count"] = str(self.num) + + +addons = [ + AddHeader() +] diff --git a/examples/addons/scripting.py b/examples/addons/scripting.py new file mode 100644 index 000000000..8b23680ec --- /dev/null +++ b/examples/addons/scripting.py @@ -0,0 +1,3 @@ + +def request(flow): + flow.request.headers["myheader"] = "value" diff --git a/setup.py b/setup.py index 71fe5fd05..1f82dbcef 100644 --- a/setup.py +++ b/setup.py @@ -96,12 +96,8 @@ setup( "pytest-timeout>=1.2.1,<2", "pytest-xdist>=1.22,<2", "pytest>=3.3,<4", - "rstcheck>=2.2, <4.0", - "sphinx_rtd_theme>=0.1.9, <0.3", - "sphinx-autobuild>=0.5.2, <0.8", - "sphinx>=1.7,<1.8", - "sphinxcontrib-documentedlist>=0.5.0, <0.7", "tox>=2.3, <3", + "rstcheck>=2.2, <4.0", ], 'examples': [ "beautifulsoup4>=4.4.1, <4.7", diff --git a/tox.ini b/tox.ini index d4ec25434..e90455c1c 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py35, py36, docs, lint +envlist = py35, py36, lint skipsdist = True toxworkdir={env:TOX_WORK_DIR:.tox} @@ -17,10 +17,6 @@ commands = {posargs} {env:CI_COMMANDS:python -c ""} -[testenv:docs] -changedir = docs -commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html - [testenv:lint] commands = mitmdump --version