├── .appveyor.yml ├── .codecov.yml ├── .env ├── .gitattributes ├── .gitignore ├── .landscape.yml ├── .travis.yml ├── CHANGELOG ├── CONTRIBUTORS ├── LICENSE ├── MANIFEST.in ├── README.rst ├── dev.ps1 ├── dev.sh ├── docs ├── .gitignore ├── Makefile ├── _static │ └── theme_overrides.css ├── _templates │ └── page.html ├── certinstall-webapp.png ├── certinstall.rst ├── conf.py ├── config.rst ├── custom-routing.txt ├── dev │ ├── addingviews.html │ ├── architecture.rst │ ├── sslkeylogfile.rst │ └── testing.rst ├── favicon.ico ├── features │ ├── anticache.rst │ ├── clientreplay.rst │ ├── filters.rst │ ├── passthrough.rst │ ├── proxyauth.rst │ ├── replacements.rst │ ├── responsestreaming.rst │ ├── reverseproxy.rst │ ├── serverreplay.rst │ ├── setheaders.rst │ ├── socksproxy.rst │ ├── sticky.rst │ ├── tcpproxy.rst │ ├── upstreamcerts.rst │ └── upstreamproxy.rst ├── howmitmproxy.rst ├── index.rst ├── install.rst ├── introduction.rst ├── mitmdump.rst ├── mitmproxy-docs.png ├── mitmproxy.rst ├── modd.conf ├── modes.rst ├── pathod │ ├── intro.rst │ ├── language.rst │ ├── library.rst │ └── test.rst ├── schematics │ ├── _explicit.graffle │ │ ├── data.plist │ │ ├── image3.icns │ │ └── image6.tiff │ ├── _explicit_https.graffle │ │ ├── data.plist │ │ ├── image3.icns │ │ └── image6.tiff │ ├── _transparent.graffle │ │ ├── data.plist │ │ ├── image3.icns │ │ └── image6.tiff │ ├── _transparent_https.graffle │ │ ├── data.plist │ │ ├── image3.icns │ │ └── image6.tiff │ ├── architecture.pdf │ ├── architecture.png │ ├── architecture.vsdx │ ├── how-mitmproxy-works-explicit-https.png │ ├── how-mitmproxy-works-explicit.png │ ├── how-mitmproxy-works-transparent-https.png │ ├── how-mitmproxy-works-transparent.png │ ├── proxy-modes-flowchart.png │ ├── proxy-modes-regular.png │ ├── proxy-modes-reverse.png │ ├── proxy-modes-transparent-1.png │ ├── proxy-modes-transparent-2.png │ ├── proxy-modes-transparent-3.png │ ├── proxy-modes-transparent-wrong.png │ ├── proxy-modes-upstream.png │ ├── proxy-modes.pdf │ └── proxy-modes.vsdx ├── screenshots │ ├── firefox3-import.jpg │ ├── firefox3-trust.jpg │ ├── firefox3.jpg │ ├── ios-gateway.png │ ├── ios-installed.png │ ├── ios-manual.png │ ├── ios-profile.png │ ├── ios-reverse.png │ ├── ios-warning.png │ ├── mitmproxy-flowview.png │ ├── mitmproxy-intercept-filt.png │ ├── mitmproxy-intercept-mid.png │ ├── mitmproxy-intercept-options.png │ ├── mitmproxy-intercept-result.png │ ├── mitmproxy-kveditor-editmode.png │ ├── mitmproxy-kveditor.png │ ├── mitmproxy.png │ ├── osx-addcert-alwaystrust.png │ ├── win7-certstore-trustedroot.png │ ├── win7-certstore.png │ ├── win7-wizard.png │ └── winpythoninstaller.jpg ├── scripting │ ├── api.rst │ ├── events.rst │ └── overview.rst ├── transparent.rst ├── transparent │ ├── linux.rst │ └── osx.rst └── tutorials │ ├── 30second.rst │ ├── gamecenter.rst │ ├── leaderboard.png │ ├── one.png │ ├── supermega.png │ ├── transparent-dhcp.rst │ └── transparent-dhcp │ ├── step1_proxy.png │ ├── step1_vbox_eth0.png │ ├── step1_vbox_eth1.png │ └── step2_proxied_vm.png ├── examples ├── README ├── add_header.py ├── arguments.py ├── change_upstream_proxy.py ├── classes.py ├── custom_contentviews.py ├── dns_spoofing.py ├── dup_and_replay.py ├── fail_with_500.py ├── flowbasic ├── flowfilter.py ├── flowwriter.py ├── full_transparency_shim.c ├── har_dump.py ├── iframe_injector.py ├── logging.py ├── mitmproxywrapper.py ├── modify_form.py ├── modify_querystring.py ├── nonblocking.py ├── pathod │ ├── libpathod_pathoc.py │ ├── test_context.py │ ├── test_setup.py │ └── test_setupall.py ├── proxapp.py ├── read_dumpfile ├── redirect_requests.py ├── remote_debug.py ├── sslstrip.py ├── stickycookies ├── stream.py ├── stream_modify.py ├── stub.py ├── tcp_message.py ├── tls_passthrough.py └── upsidedownternet.py ├── issue_template.md ├── mitmproxy ├── __init__.py ├── addonmanager.py ├── addons │ ├── __init__.py │ ├── anticache.py │ ├── anticomp.py │ ├── clientplayback.py │ ├── dumper.py │ ├── filestreamer.py │ ├── onboarding.py │ ├── onboardingapp │ │ ├── __init__.py │ │ ├── app.py │ │ ├── static │ │ │ ├── bootstrap.min.css │ │ │ ├── fontawesome │ │ │ │ ├── css │ │ │ │ │ ├── font-awesome.css │ │ │ │ │ └── font-awesome.min.css │ │ │ │ └── fonts │ │ │ │ │ ├── FontAwesome.otf │ │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ │ ├── fontawesome-webfont.svg │ │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ │ └── fontawesome-webfont.woff │ │ │ └── mitmproxy.css │ │ └── templates │ │ │ ├── frame.html │ │ │ ├── index.html │ │ │ └── layout.html │ ├── replace.py │ ├── script.py │ ├── serverplayback.py │ ├── setheaders.py │ ├── state.py │ ├── stickyauth.py │ ├── stickycookie.py │ ├── streambodies.py │ ├── termlog.py │ └── wsgiapp.py ├── certs.py ├── connections.py ├── contentviews.py ├── contrib │ ├── README │ ├── __init__.py │ ├── tls │ │ ├── __init__.py │ │ ├── _constructs.py │ │ └── utils.py │ ├── tnetstring.py │ └── wbxml │ │ ├── ASCommandResponse.py │ │ ├── ASWBXML.py │ │ ├── ASWBXMLByteQueue.py │ │ ├── ASWBXMLCodePage.py │ │ ├── GlobalTokens.py │ │ ├── InvalidDataException.py │ │ └── __init__.py ├── controller.py ├── ctx.py ├── events.py ├── exceptions.py ├── export.py ├── flow.py ├── flowfilter.py ├── http.py ├── io.py ├── io_compat.py ├── log.py ├── master.py ├── net │ ├── __init__.py │ ├── check.py │ ├── http │ │ ├── __init__.py │ │ ├── authentication.py │ │ ├── cookies.py │ │ ├── encoding.py │ │ ├── headers.py │ │ ├── http1 │ │ │ ├── __init__.py │ │ │ ├── assemble.py │ │ │ └── read.py │ │ ├── http2 │ │ │ ├── __init__.py │ │ │ ├── framereader.py │ │ │ └── utils.py │ │ ├── message.py │ │ ├── multipart.py │ │ ├── request.py │ │ ├── response.py │ │ ├── status_codes.py │ │ ├── url.py │ │ └── user_agents.py │ ├── socks.py │ ├── tcp.py │ ├── websockets │ │ ├── __init__.py │ │ ├── frame.py │ │ ├── masker.py │ │ └── utils.py │ └── wsgi.py ├── options.py ├── optmanager.py ├── platform │ ├── __init__.py │ ├── linux.py │ ├── osx.py │ ├── pf.py │ └── windows.py ├── proxy │ ├── __init__.py │ ├── config.py │ ├── modes │ │ ├── __init__.py │ │ ├── http_proxy.py │ │ ├── reverse_proxy.py │ │ ├── socks_proxy.py │ │ └── transparent_proxy.py │ ├── protocol │ │ ├── __init__.py │ │ ├── base.py │ │ ├── http.py │ │ ├── http1.py │ │ ├── http2.py │ │ ├── http_replay.py │ │ ├── rawtcp.py │ │ ├── tls.py │ │ └── websockets.py │ ├── root_context.py │ └── server.py ├── script │ ├── __init__.py │ └── concurrent.py ├── stateobject.py ├── tcp.py ├── test │ └── tutils.py ├── tools │ ├── __init__.py │ ├── cmdline.py │ ├── console │ │ ├── __init__.py │ │ ├── common.py │ │ ├── flowdetailview.py │ │ ├── flowlist.py │ │ ├── flowview.py │ │ ├── grideditor │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── col_bytes.py │ │ │ ├── col_subgrid.py │ │ │ ├── col_text.py │ │ │ └── editors.py │ │ ├── help.py │ │ ├── master.py │ │ ├── options.py │ │ ├── palettepicker.py │ │ ├── palettes.py │ │ ├── pathedit.py │ │ ├── searchable.py │ │ ├── select.py │ │ ├── signals.py │ │ ├── statusbar.py │ │ ├── tabs.py │ │ └── window.py │ ├── dump.py │ ├── main.py │ └── web │ │ ├── __init__.py │ │ ├── app.py │ │ ├── master.py │ │ ├── static │ │ ├── app.css │ │ ├── app.js │ │ ├── fonts │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ └── fontawesome-webfont.woff │ │ ├── images │ │ │ ├── chrome-devtools │ │ │ │ ├── LICENSE │ │ │ │ ├── resourceCSSIcon.png │ │ │ │ ├── resourceDocumentIcon.png │ │ │ │ ├── resourceJSIcon.png │ │ │ │ └── resourcePlainIcon.png │ │ │ ├── favicon.ico │ │ │ ├── resourceExecutableIcon.png │ │ │ ├── resourceFlashIcon.png │ │ │ ├── resourceImageIcon.png │ │ │ ├── resourceJavaIcon.png │ │ │ ├── resourceNotModifiedIcon.png │ │ │ └── resourceRedirectIcon.png │ │ ├── vendor.css │ │ └── vendor.js │ │ └── templates │ │ └── index.html ├── types │ ├── __init__.py │ ├── basethread.py │ ├── bidi.py │ ├── multidict.py │ └── serializable.py ├── utils │ ├── __init__.py │ ├── bits.py │ ├── data.py │ ├── debug.py │ ├── human.py │ ├── strutils.py │ ├── typecheck.py │ └── version_check.py └── version.py ├── pathod ├── __init__.py ├── language │ ├── __init__.py │ ├── actions.py │ ├── base.py │ ├── exceptions.py │ ├── generators.py │ ├── http.py │ ├── http2.py │ ├── message.py │ ├── websockets.py │ └── writer.py ├── log.py ├── pathoc.py ├── pathoc_cmdline.py ├── pathod.py ├── pathod_cmdline.py ├── protocols │ ├── __init__.py │ ├── http.py │ ├── http2.py │ └── websockets.py ├── test.py └── utils.py ├── release ├── .gitignore ├── README.md ├── rtool.pem ├── rtool.py ├── setup.py └── specs │ ├── icon.ico │ ├── mitmdump │ ├── mitmdump.spec │ ├── mitmproxy │ ├── mitmproxy.spec │ ├── mitmweb │ ├── mitmweb.spec │ ├── pathoc │ ├── pathoc.spec │ ├── pathod │ └── pathod.spec ├── requirements.txt ├── setup.cfg ├── setup.py ├── test ├── __init__.py ├── mitmproxy │ ├── __init__.py │ ├── addons │ │ ├── __init__.py │ │ ├── test_anticache.py │ │ ├── test_anticomp.py │ │ ├── test_clientplayback.py │ │ ├── test_dumper.py │ │ ├── test_filestreamer.py │ │ ├── test_onboarding.py │ │ ├── test_replace.py │ │ ├── test_script.py │ │ ├── test_serverplayback.py │ │ ├── test_setheaders.py │ │ ├── test_state.py │ │ ├── test_stickyauth.py │ │ ├── test_stickycookie.py │ │ ├── test_streambodies.py │ │ ├── test_termlog.py │ │ └── test_wsgiapp.py │ ├── completion │ │ ├── aaa │ │ ├── aab │ │ ├── aac │ │ └── bbb │ │ │ └── Readme.md │ ├── console │ │ ├── __init__.py │ │ ├── test_common.py │ │ ├── test_help.py │ │ ├── test_master.py │ │ ├── test_palettes.py │ │ └── test_pathedit.py │ ├── data │ │ ├── 1.css │ │ ├── addonscripts │ │ │ ├── addon.py │ │ │ ├── concurrent_decorator.py │ │ │ ├── concurrent_decorator_err.py │ │ │ ├── duplicate_flow.py │ │ │ ├── error.py │ │ │ ├── recorder.py │ │ │ ├── stream_modify.py │ │ │ └── tcp_stream_modify.py │ │ ├── amf01 │ │ ├── amf02 │ │ ├── amf03 │ │ ├── clientcert │ │ │ ├── .gitignore │ │ │ ├── 127.0.0.1.pem │ │ │ ├── client.cnf │ │ │ ├── client.pem │ │ │ └── make │ │ ├── confdir │ │ │ ├── mitmproxy-ca-cert.cer │ │ │ ├── mitmproxy-ca-cert.p12 │ │ │ ├── mitmproxy-ca-cert.pem │ │ │ └── mitmproxy-ca.pem │ │ ├── dercert │ │ ├── dumpfile-010 │ │ ├── dumpfile-011 │ │ ├── har_extractor.har │ │ ├── htpasswd │ │ ├── htpasswd.invalid │ │ ├── image-err1.jpg │ │ ├── image.gif │ │ ├── image.ico │ │ ├── image.jpg │ │ ├── image.png │ │ ├── no_common_name.pem │ │ ├── pf01 │ │ ├── pf02 │ │ ├── protobuf01 │ │ ├── replace │ │ ├── scripts │ │ │ └── all.py │ │ ├── servercert │ │ │ ├── 9da13359.0 │ │ │ ├── self-signed.pem │ │ │ ├── trusted-leaf.pem │ │ │ └── trusted-root.pem │ │ ├── test_flow_export │ │ │ ├── locust_get.py │ │ │ ├── locust_patch.py │ │ │ ├── locust_post.py │ │ │ ├── locust_task_get.py │ │ │ ├── locust_task_patch.py │ │ │ ├── locust_task_post.py │ │ │ ├── python_get.py │ │ │ ├── python_patch.py │ │ │ ├── python_post.py │ │ │ └── python_post_json.py │ │ └── testkey.pem │ ├── fuzzing │ │ ├── .env │ │ ├── README │ │ ├── client_patterns │ │ ├── go_proxy │ │ ├── reverse_patterns │ │ ├── straight_stream │ │ ├── straight_stream_patterns │ │ └── straight_stream_ssl │ ├── mastertest.py │ ├── mock_urwid.py │ ├── net │ │ ├── __init__.py │ │ ├── data │ │ │ ├── clientcert │ │ │ │ ├── .gitignore │ │ │ │ ├── client.cnf │ │ │ │ ├── client.pem │ │ │ │ └── make │ │ │ ├── dercert │ │ │ ├── dhparam.pem │ │ │ ├── htpasswd │ │ │ ├── server.crt │ │ │ ├── server.key │ │ │ ├── text_cert │ │ │ ├── text_cert_2 │ │ │ ├── text_cert_weird1 │ │ │ └── verificationcerts │ │ │ │ ├── 9da13359.0 │ │ │ │ ├── generate.py │ │ │ │ ├── self-signed.crt │ │ │ │ ├── self-signed.key │ │ │ │ ├── trusted-leaf.crt │ │ │ │ ├── trusted-leaf.key │ │ │ │ ├── trusted-root.crt │ │ │ │ ├── trusted-root.key │ │ │ │ └── trusted-root.srl │ │ ├── http │ │ │ ├── __init__.py │ │ │ ├── http1 │ │ │ │ ├── __init__.py │ │ │ │ ├── test_assemble.py │ │ │ │ └── test_read.py │ │ │ ├── http2 │ │ │ │ ├── __init__.py │ │ │ │ └── test_framereader.py │ │ │ ├── test_authentication.py │ │ │ ├── test_cookies.py │ │ │ ├── test_encoding.py │ │ │ ├── test_headers.py │ │ │ ├── test_message.py │ │ │ ├── test_multipart.py │ │ │ ├── test_request.py │ │ │ ├── test_response.py │ │ │ ├── test_status_codes.py │ │ │ ├── test_url.py │ │ │ └── test_user_agents.py │ │ ├── test_check.py │ │ ├── test_imports.py │ │ ├── test_socks.py │ │ ├── test_tcp.py │ │ ├── test_wsgi.py │ │ ├── tools │ │ │ └── getcertnames │ │ ├── tservers.py │ │ └── websockets │ │ │ ├── __init__.py │ │ │ ├── test_frame.py │ │ │ ├── test_masker.py │ │ │ └── test_utils.py │ ├── protocol │ │ ├── __init__.py │ │ ├── test_http1.py │ │ ├── test_http2.py │ │ └── test_websockets.py │ ├── script │ │ ├── __init__.py │ │ └── test_concurrent.py │ ├── test_addonmanager.py │ ├── test_certs.py │ ├── test_cmdline.py │ ├── test_contentview.py │ ├── test_contrib_tnetstring.py │ ├── test_controller.py │ ├── test_custom_contentview.py │ ├── test_dump.py │ ├── test_examples.py │ ├── test_flow.py │ ├── test_flow_export.py │ ├── test_flow_format_compat.py │ ├── test_flowfilter.py │ ├── test_fuzzing.py │ ├── test_optmanager.py │ ├── test_platform_pf.py │ ├── test_proxy.py │ ├── test_proxy_config.py │ ├── test_server.py │ ├── test_stateobject.py │ ├── test_types_bidi.py │ ├── test_types_multidict.py │ ├── test_types_serializable.py │ ├── test_web_app.py │ ├── test_web_master.py │ ├── tools │ │ ├── 1024example │ │ ├── ab.exe │ │ ├── bench.py │ │ ├── benchtool.py │ │ ├── getcert │ │ ├── inspect_dumpfile.py │ │ ├── memoryleak.py │ │ ├── passive_close.py │ │ └── testpatt │ ├── tservers.py │ ├── tutils.py │ └── utils │ │ ├── __init__.py │ │ ├── test_data.py │ │ ├── test_debug.py │ │ ├── test_human.py │ │ ├── test_strutils.py │ │ ├── test_typecheck.py │ │ └── test_version_check.py └── pathod │ ├── __init__.py │ ├── data │ ├── clientcert │ │ ├── .gitignore │ │ ├── client.cnf │ │ ├── client.pem │ │ └── make │ ├── file │ ├── request │ ├── response │ └── testkey.pem │ ├── scripts │ ├── generate.sh │ └── openssl.cnf │ ├── test_language_actions.py │ ├── test_language_base.py │ ├── test_language_generators.py │ ├── test_language_http.py │ ├── test_language_http2.py │ ├── test_language_websocket.py │ ├── test_language_writer.py │ ├── test_log.py │ ├── test_pathoc.py │ ├── test_pathoc_cmdline.py │ ├── test_pathod.py │ ├── test_pathod_cmdline.py │ ├── test_protocols_http2.py │ ├── test_test.py │ ├── test_utils.py │ └── tutils.py ├── tox.ini └── web ├── .babelrc ├── .editorconfig ├── .eslintrc.yml ├── README ├── conf.js ├── gulpfile.js ├── package.json └── src ├── css ├── app.less ├── codemirror.less ├── contentview.less ├── dropdown.less ├── eventlog.less ├── flowdetail.less ├── flowtable.less ├── flowview.less ├── footer.less ├── header.less ├── layout.less ├── prompt.less ├── sprites.less ├── tabs.less ├── vendor-bootstrap-variables.less ├── vendor-bootstrap.less └── vendor.less ├── fonts ├── FontAwesome.otf ├── README ├── font-awesome.css ├── fontawesome-webfont.eot ├── fontawesome-webfont.svg ├── fontawesome-webfont.ttf └── fontawesome-webfont.woff ├── images ├── chrome-devtools │ ├── LICENSE │ ├── resourceCSSIcon.png │ ├── resourceDocumentIcon.png │ ├── resourceJSIcon.png │ └── resourcePlainIcon.png ├── favicon.ico ├── resourceExecutableIcon.png ├── resourceFlashIcon.png ├── resourceImageIcon.png ├── resourceJavaIcon.png ├── resourceNotModifiedIcon.png └── resourceRedirectIcon.png ├── js ├── __tests__ │ └── ducks │ │ ├── flowViewSpec.js │ │ ├── flowsSpec.js │ │ ├── tutils.js │ │ ├── ui │ │ ├── flowSpec.js │ │ └── headerSpec.js │ │ └── utils │ │ ├── listSpec.js │ │ └── viewSpec.js ├── actions.js ├── app.jsx ├── components │ ├── ContentView.jsx │ ├── ContentView │ │ ├── CodeEditor.jsx │ │ ├── ContentLoader.jsx │ │ ├── ContentViewOptions.jsx │ │ ├── ContentViews.jsx │ │ ├── DownloadContentButton.jsx │ │ ├── MetaViews.jsx │ │ ├── ShowFullContentButton.jsx │ │ ├── UploadContentButton.jsx │ │ └── ViewSelector.jsx │ ├── EventLog.jsx │ ├── EventLog │ │ └── EventList.jsx │ ├── FlowTable.jsx │ ├── FlowTable │ │ ├── FlowColumns.jsx │ │ ├── FlowRow.jsx │ │ └── FlowTableHead.jsx │ ├── FlowView.jsx │ ├── FlowView │ │ ├── Details.jsx │ │ ├── Headers.jsx │ │ ├── Messages.jsx │ │ ├── Nav.jsx │ │ └── ToggleEdit.jsx │ ├── Footer.jsx │ ├── Header.jsx │ ├── Header │ │ ├── FileMenu.jsx │ │ ├── FilterDocs.jsx │ │ ├── FilterInput.jsx │ │ ├── FlowMenu.jsx │ │ ├── MainMenu.jsx │ │ ├── OptionMenu.jsx │ │ └── ViewMenu.jsx │ ├── MainView.jsx │ ├── Prompt.jsx │ ├── ProxyApp.jsx │ ├── ValueEditor │ │ ├── ValidateEditor.jsx │ │ └── ValueEditor.jsx │ ├── common │ │ ├── Button.jsx │ │ ├── Dropdown.jsx │ │ ├── FileChooser.jsx │ │ ├── Splitter.jsx │ │ ├── ToggleButton.jsx │ │ └── ToggleInputButton.jsx │ └── helpers │ │ ├── AutoScroll.js │ │ └── VirtualScroll.js ├── dispatcher.js ├── ducks │ ├── README.md │ ├── app.js │ ├── eventLog.js │ ├── flowView.js │ ├── flows.js │ ├── index.js │ ├── msgQueue.js │ ├── settings.js │ ├── ui │ │ ├── flow.js │ │ ├── header.js │ │ ├── index.js │ │ └── keyboard.js │ ├── utils │ │ ├── list.js │ │ └── view.js │ └── websocket.js ├── filt │ ├── filt.js │ └── filt.peg ├── flow │ └── utils.js └── utils.js └── templates └── index.html /.codecov.yml: -------------------------------------------------------------------------------- 1 | comment: off 2 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | DIR="$( dirname "${BASH_SOURCE[0]}" )" 2 | ACTIVATE_DIR="$(if [ -f "$DIR/venv/bin/activate" ]; then echo 'bin'; else echo 'Scripts'; fi;)" 3 | if [ -z "$VIRTUAL_ENV" ] && [ -f "$DIR/venv/$ACTIVATE_DIR/activate" ]; then 4 | echo "Activating mitmproxy virtualenv..." 5 | source "$DIR/venv/$ACTIVATE_DIR/activate" 6 | fi 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | mitmproxy/web/static/**/* -diff 2 | web/src/js/filt/filt.js -diff 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | MANIFEST 3 | */tmp 4 | /venv* 5 | *.py[cdo] 6 | *.swp 7 | *.swo 8 | *.egg-info/ 9 | .coverage* 10 | .idea 11 | .cache/ 12 | .tox*/ 13 | build/ 14 | 15 | # UI 16 | 17 | node_modules 18 | bower_components 19 | *.map 20 | sslkeylogfile.log 21 | .tox/ 22 | -------------------------------------------------------------------------------- /.landscape.yml: -------------------------------------------------------------------------------- 1 | ignore-paths: 2 | - docs 3 | - examples 4 | - mitmproxy/contrib 5 | - web 6 | max-line-length: 140 7 | pylint: 8 | options: 9 | dummy-variables-rgx: _$|.+_$|dummy_.+ 10 | disable: 11 | - missing-docstring 12 | - protected-access 13 | - too-few-public-methods 14 | - too-many-arguments 15 | - too-many-instance-attributes 16 | - too-many-locals 17 | - too-many-public-methods 18 | - too-many-return-statements 19 | - too-many-statements 20 | - unpacking-non-sequence 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Aldo Cortesi. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | graft mitmproxy 2 | graft pathod 3 | recursive-exclude * *.pyc *.pyo *.swo *.swp *.map 4 | -------------------------------------------------------------------------------- /dev.ps1: -------------------------------------------------------------------------------- 1 | $ErrorActionPreference = "Stop" 2 | $VENV = ".\venv" 3 | 4 | virtualenv $VENV --always-copy 5 | & $VENV\Scripts\activate.ps1 6 | 7 | python -m pip install --disable-pip-version-check -U pip 8 | cmd /c "pip install -r requirements.txt 2>&1" 9 | 10 | echo @" 11 | 12 | * Created virtualenv environment in $VENV. 13 | * Installed all dependencies into the virtualenv. 14 | * Activated virtualenv environment. 15 | 16 | "@ -------------------------------------------------------------------------------- /dev.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -x 4 | 5 | PYVERSION=3.5 6 | VENV="venv$PYVERSION" 7 | 8 | echo "Creating dev environment in $VENV using Python $PYVERSION" 9 | 10 | python$PYVERSION -m virtualenv "$VENV" --always-copy 11 | . "$VENV/bin/activate" 12 | pip$PYVERSION install -U pip setuptools 13 | pip$PYVERSION install -r requirements.txt 14 | 15 | echo "" 16 | echo "* Virtualenv created in $VENV and all dependencies installed." 17 | echo "* You can now activate the $(python --version) virtualenv with this command: \`. $VENV/bin/activate\`" 18 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | _build/ 2 | -------------------------------------------------------------------------------- /docs/_static/theme_overrides.css: -------------------------------------------------------------------------------- 1 | 2 | /* override table width restrictions */ 3 | .wy-table-responsive table td, .wy-table-responsive table th { 4 | white-space: normal; 5 | } 6 | 7 | .wy-table-responsive > table > tbody > tr > td { 8 | vertical-align: top !important; 9 | } 10 | 11 | .wy-table-responsive { 12 | margin-bottom: 24px; 13 | max-width: 100%; 14 | overflow: visible; 15 | } 16 | 17 | .wy-menu-vertical header, .wy-menu-vertical p.caption { 18 | color: #e0e0e0; 19 | } 20 | 21 | .code-block-caption { 22 | height: 1.5em; 23 | } 24 | 25 | .code-block-caption .caption-text { 26 | font-size: 0.8em; 27 | float: right; 28 | } 29 | 30 | .code-block-caption .headerlink { 31 | display: none !important; 32 | } 33 | 34 | .function .headerlink { 35 | display: none !important; 36 | } 37 | 38 | dl .reference.internal { 39 | display: none !important; 40 | } 41 | 42 | dl .headerlink { 43 | display: none !important; 44 | } 45 | -------------------------------------------------------------------------------- /docs/_templates/page.html: -------------------------------------------------------------------------------- 1 | {% extends "!page.html" %} 2 | {% block sidebartitle %} 3 | 4 | 8 | 9 | {{ super() }} 10 | {% endblock %} -------------------------------------------------------------------------------- /docs/certinstall-webapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/certinstall-webapp.png -------------------------------------------------------------------------------- /docs/custom-routing.txt: -------------------------------------------------------------------------------- 1 | # Adapted from http://tldp.org/HOWTO/TransparentProxy-6.html (6.2 Second method) 2 | # Note that the choice of firewall mark (3) and routing table (2) was fairly arbitrary. 3 | # If you are already using policy routing or firewall marking for some other purpose, 4 | # make sure you choose unique numbers here. Otherwise, don't worry about it. 5 | 6 | 7 | 8 | # On the router, run 9 | 10 | PROXY_IP=192.168.1.100 11 | TARGET_IP=192.168.1.110 12 | 13 | iptables -t mangle -A PREROUTING -j ACCEPT -p tcp -m multiport --dports 80,443 -s ! $TARGET_IP 14 | # Alternative to MITM the whole network: 15 | # iptables -t mangle -A PREROUTING -j ACCEPT -p tcp -m multiport --dports 80,443 -s $PROXY_IP 16 | iptables -t mangle -A PREROUTING -j MARK --set-mark 3 -p tcp -m multiport --dports 80,443 17 | ip rule add fwmark 3 table 2 18 | ip route add default via $PROXY_IP dev br0 table 2 19 | 20 | 21 | 22 | # On the proxy machine, run 23 | 24 | iptables -A PREROUTING -t nat -i eth0 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-port 8080 25 | -------------------------------------------------------------------------------- /docs/dev/architecture.rst: -------------------------------------------------------------------------------- 1 | .. _architecture: 2 | 3 | Architecture 4 | ============ 5 | 6 | To give you a better understanding of how mitmproxy works, mitmproxy's 7 | high-level architecture is detailed in the following graphic: 8 | 9 | .. image:: ../schematics/architecture.png 10 | 11 | :download:`architecture.pdf <../schematics/architecture.pdf>` 12 | 13 | Please don't refrain from asking any further 14 | questions on the mailing list, the Slack channel or the GitHub issue tracker. 15 | -------------------------------------------------------------------------------- /docs/dev/sslkeylogfile.rst: -------------------------------------------------------------------------------- 1 | .. _sslkeylogfile: 2 | 3 | TLS Master Secrets 4 | ================== 5 | 6 | The SSL master keys can be logged by mitmproxy so that external programs can decrypt TLS 7 | connections both from and to the proxy. Key logging is enabled by setting the environment variable 8 | :envvar:`SSLKEYLOGFILE` so that it points to a writable text file. 9 | Recent versions of WireShark can use these log files to decrypt packets. 10 | You can specify the key file path in WireShark via 11 | 12 | :samp:`Edit -> Preferences -> Protocols -> SSL -> (Pre)-Master-Secret log filename`. 13 | 14 | Note that :envvar:`SSLKEYLOGFILE` is respected by other programs as well, e.g. Firefox and Chrome. 15 | If this creates any issues, you can set :envvar:`MITMPROXY_SSLKEYLOGFILE` alternatively. 16 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/favicon.ico -------------------------------------------------------------------------------- /docs/features/anticache.rst: -------------------------------------------------------------------------------- 1 | .. _anticache: 2 | 3 | Anticache 4 | ========= 5 | When the ``--anticache`` option is passed to mitmproxy, it removes headers 6 | (``if-none-match`` and ``if-modified-since``) that might elicit a 7 | ``304 not modified`` response from the server. This is useful when you want to make 8 | sure you capture an HTTP exchange in its totality. It's also often used during 9 | :ref:`clientreplay`, when you want to make sure the server responds with complete data. 10 | 11 | 12 | ================== ====================== 13 | command-line ``--anticache`` 14 | mitmproxy shortcut :kbd:`o` then :kbd:`a` 15 | ================== ====================== 16 | -------------------------------------------------------------------------------- /docs/features/clientreplay.rst: -------------------------------------------------------------------------------- 1 | .. _clientreplay: 2 | 3 | Client-side replay 4 | ================== 5 | 6 | Client-side replay does what it says on the tin: you provide a previously saved 7 | HTTP conversation, and mitmproxy replays the client requests one by one. Note 8 | that mitmproxy serializes the requests, waiting for a response from the server 9 | before starting the next request. This might differ from the recorded 10 | conversation, where requests may have been made concurrently. 11 | 12 | You may want to use client-side replay in conjunction with the 13 | :ref:`anticache` option, to make sure the server responds with complete data. 14 | 15 | ================== =========== 16 | command-line ``-c path`` 17 | mitmproxy shortcut :kbd:`R` then :kbd:`c` 18 | ================== =========== 19 | -------------------------------------------------------------------------------- /docs/features/filters.rst: -------------------------------------------------------------------------------- 1 | .. _filters: 2 | 3 | Filter expressions 4 | ================== 5 | 6 | Many commands in :program:`mitmproxy` and :program:`mitmdump` take a filter expression. 7 | Filter expressions consist of the following operators: 8 | 9 | .. documentedlist:: 10 | :header: "Expression" "Description" 11 | :listobject: mitmproxy.flowfilter.help 12 | 13 | - Regexes are Python-style 14 | - Regexes can be specified as quoted strings 15 | - Header matching (~h, ~hq, ~hs) is against a string of the form "name: value". 16 | - Strings with no operators are matched against the request URL. 17 | - The default binary operator is &. 18 | 19 | Examples 20 | -------- 21 | 22 | URL containing "google.com": 23 | 24 | .. code-block:: none 25 | 26 | google\.com 27 | 28 | Requests whose body contains the string "test": 29 | 30 | .. code-block:: none 31 | 32 | ~q ~b test 33 | 34 | Anything but requests with a text/html content type: 35 | 36 | .. code-block:: none 37 | 38 | !(~q & ~t "text/html") 39 | -------------------------------------------------------------------------------- /docs/features/proxyauth.rst: -------------------------------------------------------------------------------- 1 | .. _proxyauth: 2 | 3 | Proxy Authentication 4 | ==================== 5 | 6 | 7 | Asks the user for authentication before they are permitted to use the proxy. 8 | Authentication headers are stripped from the flows, so they are not passed to 9 | upstream servers. For now, only HTTP Basic authentication is supported. The 10 | proxy auth options are not compatible with the transparent, socks or reverse proxy 11 | mode. 12 | 13 | ================== ====================== 14 | command-line ``--nonanonymous``, 15 | ``--singleuser USER``, 16 | ``--htpasswd PATH`` 17 | ================== ====================== 18 | -------------------------------------------------------------------------------- /docs/features/setheaders.rst: -------------------------------------------------------------------------------- 1 | .. _setheaders: 2 | 3 | Set Headers 4 | =========== 5 | 6 | This feature lets you specify a set of headers to be added to requests or 7 | responses, based on a filter pattern. You can specify these either on the 8 | command-line, or through an interactive editor in mitmproxy. 9 | 10 | Example: Set the **Host** header to "example.com" for all requests. 11 | 12 | .. code-block:: none 13 | 14 | mitmdump -R http://example.com --setheader :~q:Host:example.com 15 | 16 | ================== ======================= 17 | command-line ``--setheader PATTERN`` 18 | mitmproxy shortcut :kbd:`o` then :kbd:`H` 19 | ================== ======================= 20 | -------------------------------------------------------------------------------- /docs/features/socksproxy.rst: -------------------------------------------------------------------------------- 1 | .. _socksproxy: 2 | 3 | SOCKS Mode 4 | ========== 5 | 6 | In this mode, mitmproxy acts as a SOCKS5 proxy server. 7 | 8 | ================== =========== 9 | command-line ``--socks`` 10 | ================== =========== 11 | -------------------------------------------------------------------------------- /docs/features/tcpproxy.rst: -------------------------------------------------------------------------------- 1 | .. _tcpproxy: 2 | 3 | TCP Proxy 4 | ========= 5 | 6 | WebSockets or other non-HTTP protocols are not supported by mitmproxy yet. However, you can exempt 7 | hostnames from processing, so that mitmproxy acts as a generic TCP forwarder. 8 | This feature is closely related to the :ref:`passthrough` functionality, 9 | but differs in two important aspects: 10 | 11 | - The raw TCP messages are printed to the event log. 12 | - SSL connections will be intercepted. 13 | 14 | Please note that message interception or modification are not possible yet. 15 | If you are not interested in the raw TCP messages, you should use the ignore domains feature. 16 | 17 | How it works 18 | ------------ 19 | 20 | ================== ====================== 21 | command-line ``--tcp HOST`` 22 | mitmproxy shortcut :kbd:`o` then :kbd:`T` 23 | ================== ====================== 24 | 25 | For a detailed description how the hostname pattern works, please look at the :ref:`passthrough` 26 | feature. 27 | 28 | .. seealso:: 29 | 30 | - :ref:`passthrough` 31 | - :ref:`responsestreaming` 32 | -------------------------------------------------------------------------------- /docs/features/upstreamcerts.rst: -------------------------------------------------------------------------------- 1 | .. _upstreamcerts: 2 | 3 | Upstream Certificates 4 | ===================== 5 | 6 | When mitmproxy receives a connection destined for an SSL-protected service, it 7 | freezes the connection before reading its request data, and makes a connection 8 | to the upstream server to "sniff" the contents of its SSL certificate. The 9 | information gained - the **Common Name** and **Subject Alternative Names** - is 10 | then used to generate the interception certificate, which is sent to the client 11 | so the connection can continue. 12 | 13 | This rather intricate little dance lets us seamlessly generate correct 14 | certificates even if the client has specified only an IP address rather than the 15 | hostname. It also means that we don't need to sniff additional data to generate 16 | certs in transparent mode. 17 | 18 | Upstream cert sniffing is on by default, and can optionally be turned off. 19 | 20 | ================== ====================== 21 | command-line ``--no-upstream-cert`` 22 | mitmproxy shortcut :kbd:`o` then :kbd:`U` 23 | ================== ====================== 24 | -------------------------------------------------------------------------------- /docs/features/upstreamproxy.rst: -------------------------------------------------------------------------------- 1 | .. _upstreamproxy: 2 | 3 | Upstream proxy mode 4 | =================== 5 | 6 | In this mode, mitmproxy accepts proxy requests and unconditionally forwards all 7 | requests to a specified upstream proxy server. This is in contrast to :ref:`reverseproxy`, 8 | in which mitmproxy forwards ordinary HTTP requests to an upstream server. 9 | 10 | ================== ============================= 11 | command-line ``-U http://hostname[:port]`` 12 | ================== ============================= 13 | -------------------------------------------------------------------------------- /docs/introduction.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | **mitmproxy** is an interactive man-in-the-middle proxy for HTTP and HTTPS 5 | with a console interface. 6 | 7 | **mitmdump** is the command-line version of mitmproxy. Think tcpdump for HTTP. 8 | 9 | Documentation, tutorials and distribution packages can be found on the 10 | mitmproxy website: `mitmproxy.org `_ 11 | 12 | 13 | .. rubric:: Features 14 | 15 | - Intercept HTTP & HTTPS requests and responses and modify them on the fly 16 | - Save complete HTTP conversations for later replay and analysis 17 | - Replay the client-side of an HTTP conversations 18 | - Replay HTTP responses of a previously recorded server 19 | - Reverse proxy mode to forward traffic to a specified server 20 | - Transparent proxy mode on OSX and Linux 21 | - Make scripted changes to HTTP traffic using Python 22 | - SSL/TLS certificates for interception are generated on the fly 23 | - And much, much more... 24 | -------------------------------------------------------------------------------- /docs/mitmproxy-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/mitmproxy-docs.png -------------------------------------------------------------------------------- /docs/modd.conf: -------------------------------------------------------------------------------- 1 | @build = ./_build 2 | 3 | ** !_build/** ../mitmproxy/**/*.py { 4 | prep: sphinx-build -W -d @build/doctrees -b html . @build/html 5 | daemon: devd -m @build/html 6 | } 7 | -------------------------------------------------------------------------------- /docs/pathod/library.rst: -------------------------------------------------------------------------------- 1 | .. _library: 2 | 3 | pathod library 4 | ============== 5 | 6 | Behind the pathod and pathoc command-line tools lurks the **pathod** library, a 7 | powerful way to manipulate and serve HTTP requests and responses from code. The 8 | canonical documentation for the library is in the code, and can be accessed 9 | using pydoc. 10 | 11 | 12 | .. literalinclude:: ../../examples/pathod/libpathod_pathoc.py 13 | :caption: examples/pathod/libpathod_pathoc.py 14 | :language: python 15 | -------------------------------------------------------------------------------- /docs/pathod/test.rst: -------------------------------------------------------------------------------- 1 | .. _test: 2 | 3 | pathod.test 4 | =========== 5 | 6 | The **pathod.test** module is a light, flexible testing layer for HTTP clients. 7 | It works by firing up a Pathod instance in a separate thread, letting you use 8 | Pathod's full abilities to generate responses, and then query Pathod's internal 9 | logs to establish what happened. All the mechanics of startup, shutdown, finding 10 | free ports and so forth are taken care of for you. 11 | 12 | The canonical docs can be accessed using pydoc: 13 | 14 | >>> pydoc pathod.test 15 | 16 | The remainder of this page demonstrates some common interaction patterns using 17 | nose. These examples are 18 | also applicable with only minor modification to most commonly used Python testing 19 | engines. 20 | 21 | 22 | Context Manager 23 | --------------- 24 | 25 | .. literalinclude:: ../../examples/pathod/test_context.py 26 | :caption: examples/pathod/test_context.py 27 | :language: python 28 | 29 | 30 | One instance per test 31 | --------------------- 32 | 33 | .. literalinclude:: ../../examples/pathod/test_setup.py 34 | :caption: examples/pathod/test_setup.py 35 | :language: python 36 | -------------------------------------------------------------------------------- /docs/schematics/_explicit.graffle/image3.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/_explicit.graffle/image3.icns -------------------------------------------------------------------------------- /docs/schematics/_explicit.graffle/image6.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/_explicit.graffle/image6.tiff -------------------------------------------------------------------------------- /docs/schematics/_explicit_https.graffle/image3.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/_explicit_https.graffle/image3.icns -------------------------------------------------------------------------------- /docs/schematics/_explicit_https.graffle/image6.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/_explicit_https.graffle/image6.tiff -------------------------------------------------------------------------------- /docs/schematics/_transparent.graffle/image3.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/_transparent.graffle/image3.icns -------------------------------------------------------------------------------- /docs/schematics/_transparent.graffle/image6.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/_transparent.graffle/image6.tiff -------------------------------------------------------------------------------- /docs/schematics/_transparent_https.graffle/image3.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/_transparent_https.graffle/image3.icns -------------------------------------------------------------------------------- /docs/schematics/_transparent_https.graffle/image6.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/_transparent_https.graffle/image6.tiff -------------------------------------------------------------------------------- /docs/schematics/architecture.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/architecture.pdf -------------------------------------------------------------------------------- /docs/schematics/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/architecture.png -------------------------------------------------------------------------------- /docs/schematics/architecture.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/architecture.vsdx -------------------------------------------------------------------------------- /docs/schematics/how-mitmproxy-works-explicit-https.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/how-mitmproxy-works-explicit-https.png -------------------------------------------------------------------------------- /docs/schematics/how-mitmproxy-works-explicit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/how-mitmproxy-works-explicit.png -------------------------------------------------------------------------------- /docs/schematics/how-mitmproxy-works-transparent-https.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/how-mitmproxy-works-transparent-https.png -------------------------------------------------------------------------------- /docs/schematics/how-mitmproxy-works-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/how-mitmproxy-works-transparent.png -------------------------------------------------------------------------------- /docs/schematics/proxy-modes-flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes-flowchart.png -------------------------------------------------------------------------------- /docs/schematics/proxy-modes-regular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes-regular.png -------------------------------------------------------------------------------- /docs/schematics/proxy-modes-reverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes-reverse.png -------------------------------------------------------------------------------- /docs/schematics/proxy-modes-transparent-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes-transparent-1.png -------------------------------------------------------------------------------- /docs/schematics/proxy-modes-transparent-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes-transparent-2.png -------------------------------------------------------------------------------- /docs/schematics/proxy-modes-transparent-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes-transparent-3.png -------------------------------------------------------------------------------- /docs/schematics/proxy-modes-transparent-wrong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes-transparent-wrong.png -------------------------------------------------------------------------------- /docs/schematics/proxy-modes-upstream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes-upstream.png -------------------------------------------------------------------------------- /docs/schematics/proxy-modes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes.pdf -------------------------------------------------------------------------------- /docs/schematics/proxy-modes.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/schematics/proxy-modes.vsdx -------------------------------------------------------------------------------- /docs/screenshots/firefox3-import.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/firefox3-import.jpg -------------------------------------------------------------------------------- /docs/screenshots/firefox3-trust.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/firefox3-trust.jpg -------------------------------------------------------------------------------- /docs/screenshots/firefox3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/firefox3.jpg -------------------------------------------------------------------------------- /docs/screenshots/ios-gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/ios-gateway.png -------------------------------------------------------------------------------- /docs/screenshots/ios-installed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/ios-installed.png -------------------------------------------------------------------------------- /docs/screenshots/ios-manual.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/ios-manual.png -------------------------------------------------------------------------------- /docs/screenshots/ios-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/ios-profile.png -------------------------------------------------------------------------------- /docs/screenshots/ios-reverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/ios-reverse.png -------------------------------------------------------------------------------- /docs/screenshots/ios-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/ios-warning.png -------------------------------------------------------------------------------- /docs/screenshots/mitmproxy-flowview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/mitmproxy-flowview.png -------------------------------------------------------------------------------- /docs/screenshots/mitmproxy-intercept-filt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/mitmproxy-intercept-filt.png -------------------------------------------------------------------------------- /docs/screenshots/mitmproxy-intercept-mid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/mitmproxy-intercept-mid.png -------------------------------------------------------------------------------- /docs/screenshots/mitmproxy-intercept-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/mitmproxy-intercept-options.png -------------------------------------------------------------------------------- /docs/screenshots/mitmproxy-intercept-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/mitmproxy-intercept-result.png -------------------------------------------------------------------------------- /docs/screenshots/mitmproxy-kveditor-editmode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/mitmproxy-kveditor-editmode.png -------------------------------------------------------------------------------- /docs/screenshots/mitmproxy-kveditor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/mitmproxy-kveditor.png -------------------------------------------------------------------------------- /docs/screenshots/mitmproxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/mitmproxy.png -------------------------------------------------------------------------------- /docs/screenshots/osx-addcert-alwaystrust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/osx-addcert-alwaystrust.png -------------------------------------------------------------------------------- /docs/screenshots/win7-certstore-trustedroot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/win7-certstore-trustedroot.png -------------------------------------------------------------------------------- /docs/screenshots/win7-certstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/win7-certstore.png -------------------------------------------------------------------------------- /docs/screenshots/win7-wizard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/win7-wizard.png -------------------------------------------------------------------------------- /docs/screenshots/winpythoninstaller.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/screenshots/winpythoninstaller.jpg -------------------------------------------------------------------------------- /docs/scripting/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | 4 | API 5 | === 6 | 7 | - Errors 8 | - `mitmproxy.flow.Error <#mitmproxy.flow.Error>`_ 9 | - HTTP 10 | - `mitmproxy.http.HTTPRequest <#mitmproxy.http.HTTPRequest>`_ 11 | - `mitmproxy.http.HTTPResponse <#mitmproxy.http.HTTPResponse>`_ 12 | - `mitmproxy.http.HTTPFlow <#mitmproxy.http.HTTPFlow>`_ 13 | - Logging 14 | - `mitmproxy.log.Log <#mitmproxy.controller.Log>`_ 15 | - `mitmproxy.log.LogEntry <#mitmproxy.controller.LogEntry>`_ 16 | 17 | 18 | Errors 19 | ------ 20 | 21 | .. autoclass:: mitmproxy.flow.Error 22 | :inherited-members: 23 | 24 | HTTP 25 | ---- 26 | 27 | .. autoclass:: mitmproxy.http.HTTPRequest 28 | :inherited-members: 29 | 30 | .. autoclass:: mitmproxy.http.HTTPResponse 31 | :inherited-members: 32 | 33 | .. autoclass:: mitmproxy.http.HTTPFlow 34 | :inherited-members: 35 | 36 | Logging 37 | -------- 38 | 39 | .. autoclass:: mitmproxy.log.Log 40 | :inherited-members: 41 | .. autoclass:: mitmproxy.log.LogEntry 42 | :inherited-members: 43 | -------------------------------------------------------------------------------- /docs/tutorials/leaderboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/tutorials/leaderboard.png -------------------------------------------------------------------------------- /docs/tutorials/one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/tutorials/one.png -------------------------------------------------------------------------------- /docs/tutorials/supermega.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/tutorials/supermega.png -------------------------------------------------------------------------------- /docs/tutorials/transparent-dhcp/step1_proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/tutorials/transparent-dhcp/step1_proxy.png -------------------------------------------------------------------------------- /docs/tutorials/transparent-dhcp/step1_vbox_eth0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/tutorials/transparent-dhcp/step1_vbox_eth0.png -------------------------------------------------------------------------------- /docs/tutorials/transparent-dhcp/step1_vbox_eth1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/tutorials/transparent-dhcp/step1_vbox_eth1.png -------------------------------------------------------------------------------- /docs/tutorials/transparent-dhcp/step2_proxied_vm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/docs/tutorials/transparent-dhcp/step2_proxied_vm.png -------------------------------------------------------------------------------- /examples/add_header.py: -------------------------------------------------------------------------------- 1 | def response(flow): 2 | flow.response.headers["newheader"] = "foo" 3 | -------------------------------------------------------------------------------- /examples/arguments.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | 3 | 4 | class Replacer: 5 | def __init__(self, src, dst): 6 | self.src, self.dst = src, dst 7 | 8 | def response(self, flow): 9 | flow.response.replace(self.src, self.dst) 10 | 11 | 12 | def start(): 13 | parser = argparse.ArgumentParser() 14 | parser.add_argument("src", type=str) 15 | parser.add_argument("dst", type=str) 16 | args = parser.parse_args() 17 | return Replacer(args.src, args.dst) 18 | -------------------------------------------------------------------------------- /examples/change_upstream_proxy.py: -------------------------------------------------------------------------------- 1 | # This scripts demonstrates how mitmproxy can switch to a second/different upstream proxy 2 | # in upstream proxy mode. 3 | # 4 | # Usage: mitmdump -U http://default-upstream-proxy.local:8080/ -s change_upstream_proxy.py 5 | # 6 | # If you want to change the target server, you should modify flow.request.host and flow.request.port 7 | 8 | 9 | def proxy_address(flow): 10 | # Poor man's loadbalancing: route every second domain through the alternative proxy. 11 | if hash(flow.request.host) % 2 == 1: 12 | return ("localhost", 8082) 13 | else: 14 | return ("localhost", 8081) 15 | 16 | 17 | def request(flow): 18 | if flow.request.method == "CONNECT": 19 | # If the decision is done by domain, one could also modify the server address here. 20 | # We do it after CONNECT here to have the request data available as well. 21 | return 22 | address = proxy_address(flow) 23 | if flow.live: 24 | flow.live.change_upstream_proxy_server(address) 25 | -------------------------------------------------------------------------------- /examples/classes.py: -------------------------------------------------------------------------------- 1 | class AddHeader: 2 | def response(self, flow): 3 | flow.response.headers["newheader"] = "foo" 4 | 5 | 6 | def start(): 7 | return AddHeader() 8 | -------------------------------------------------------------------------------- /examples/dup_and_replay.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import ctx 2 | 3 | 4 | def request(flow): 5 | f = ctx.master.state.duplicate_flow(flow) 6 | f.request.path = "/changed" 7 | ctx.master.replay_request(f, block=True, run_scripthooks=False) 8 | -------------------------------------------------------------------------------- /examples/fail_with_500.py: -------------------------------------------------------------------------------- 1 | def response(flow): 2 | flow.response.status_code = 500 3 | flow.response.content = b"" 4 | -------------------------------------------------------------------------------- /examples/flowbasic: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | This example shows how to build a proxy based on mitmproxy's Flow 4 | primitives. 5 | 6 | Heads Up: In the majority of cases, you want to use inline scripts. 7 | 8 | Note that request and response messages are not automatically replied to, 9 | so we need to implement handlers to do this. 10 | """ 11 | from mitmproxy import controller, options, master 12 | from mitmproxy.proxy import ProxyServer, ProxyConfig 13 | 14 | 15 | class MyMaster(master.Master): 16 | def run(self): 17 | try: 18 | master.Master.run(self) 19 | except KeyboardInterrupt: 20 | self.shutdown() 21 | 22 | @controller.handler 23 | def request(self, f): 24 | print("request", f) 25 | 26 | @controller.handler 27 | def response(self, f): 28 | print("response", f) 29 | 30 | @controller.handler 31 | def error(self, f): 32 | print("error", f) 33 | 34 | @controller.handler 35 | def log(self, l): 36 | print("log", l.msg) 37 | 38 | opts = options.Options(cadir="~/.mitmproxy/") 39 | config = ProxyConfig(opts) 40 | server = ProxyServer(config) 41 | m = MyMaster(opts, server) 42 | m.run() 43 | -------------------------------------------------------------------------------- /examples/flowfilter.py: -------------------------------------------------------------------------------- 1 | # This scripts demonstrates how to use mitmproxy's filter pattern in scripts. 2 | # Usage: mitmdump -s "flowfilter.py FILTER" 3 | 4 | import sys 5 | from mitmproxy import flowfilter 6 | 7 | 8 | class Filter: 9 | def __init__(self, spec): 10 | self.filter = flowfilter.parse(spec) 11 | 12 | def response(self, flow): 13 | if flowfilter.match(self.filter, flow): 14 | print("Flow matches filter:") 15 | print(flow) 16 | 17 | 18 | def start(): 19 | if len(sys.argv) != 2: 20 | raise ValueError("Usage: -s 'filt.py FILTER'") 21 | return Filter(sys.argv[1]) 22 | -------------------------------------------------------------------------------- /examples/flowwriter.py: -------------------------------------------------------------------------------- 1 | import random 2 | import sys 3 | from mitmproxy import io 4 | 5 | 6 | class Writer: 7 | def __init__(self, path): 8 | if path == "-": 9 | f = sys.stdout 10 | else: 11 | f = open(path, "wb") 12 | self.w = io.FlowWriter(f) 13 | 14 | def response(self, flow): 15 | if random.choice([True, False]): 16 | self.w.add(flow) 17 | 18 | 19 | def start(): 20 | if len(sys.argv) != 2: 21 | raise ValueError('Usage: -s "flowriter.py filename"') 22 | return Writer(sys.argv[1]) 23 | -------------------------------------------------------------------------------- /examples/iframe_injector.py: -------------------------------------------------------------------------------- 1 | # Usage: mitmdump -s "iframe_injector.py url" 2 | # (this script works best with --anticache) 3 | import sys 4 | from bs4 import BeautifulSoup 5 | 6 | 7 | class Injector: 8 | def __init__(self, iframe_url): 9 | self.iframe_url = iframe_url 10 | 11 | def response(self, flow): 12 | if flow.request.host in self.iframe_url: 13 | return 14 | html = BeautifulSoup(flow.response.content, "lxml") 15 | if html.body: 16 | iframe = html.new_tag( 17 | "iframe", 18 | src=self.iframe_url, 19 | frameborder=0, 20 | height=0, 21 | width=0) 22 | html.body.insert(0, iframe) 23 | flow.response.content = str(html).encode("utf8") 24 | 25 | 26 | def start(): 27 | if len(sys.argv) != 2: 28 | raise ValueError('Usage: -s "iframe_injector.py url"') 29 | return Injector(sys.argv[1]) 30 | -------------------------------------------------------------------------------- /examples/logging.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import ctx 2 | 3 | 4 | def start(): 5 | ctx.log.info("This is some informative text.") 6 | ctx.log.error("This is an error.") 7 | -------------------------------------------------------------------------------- /examples/modify_form.py: -------------------------------------------------------------------------------- 1 | def request(flow): 2 | if flow.request.urlencoded_form: 3 | flow.request.urlencoded_form["mitmproxy"] = "rocks" 4 | else: 5 | # This sets the proper content type and overrides the body. 6 | flow.request.urlencoded_form = [ 7 | ("foo", "bar") 8 | ] 9 | -------------------------------------------------------------------------------- /examples/modify_querystring.py: -------------------------------------------------------------------------------- 1 | def request(flow): 2 | flow.request.query["mitmproxy"] = "rocks" 3 | -------------------------------------------------------------------------------- /examples/nonblocking.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from mitmproxy.script import concurrent 4 | 5 | 6 | @concurrent # Remove this and see what happens 7 | def request(flow): 8 | # You don't want to use mitmproxy.ctx from a different thread 9 | print("handle request: %s%s" % (flow.request.host, flow.request.path)) 10 | time.sleep(5) 11 | print("start request: %s%s" % (flow.request.host, flow.request.path)) 12 | -------------------------------------------------------------------------------- /examples/pathod/libpathod_pathoc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from pathod import pathoc 3 | 4 | p = pathoc.Pathoc(("google.com", 80)) 5 | p.connect() 6 | print(p.request("get:/")) 7 | print(p.request("get:/foo")) 8 | -------------------------------------------------------------------------------- /examples/pathod/test_context.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from pathod import test 3 | 4 | 5 | def test_simple(): 6 | """ 7 | Testing the requests module with 8 | a pathod context manager. 9 | """ 10 | # Start pathod in a separate thread 11 | with test.Daemon() as d: 12 | # Get a URL for a pathod spec 13 | url = d.p("200:b@100") 14 | # ... and request it 15 | r = requests.put(url) 16 | 17 | # Check the returned data 18 | assert r.status_code == 200 19 | assert len(r.content) == 100 20 | 21 | # Check pathod's internal log 22 | log = d.last_log()["request"] 23 | assert log["method"] == "PUT" 24 | -------------------------------------------------------------------------------- /examples/pathod/test_setup.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from pathod import test 3 | 4 | 5 | class Test: 6 | """ 7 | Testing the requests module with 8 | a pathod instance started for 9 | each test. 10 | """ 11 | 12 | def setup(self): 13 | self.d = test.Daemon() 14 | 15 | def teardown(self): 16 | self.d.shutdown() 17 | 18 | def test_simple(self): 19 | # Get a URL for a pathod spec 20 | url = self.d.p("200:b@100") 21 | # ... and request it 22 | r = requests.put(url) 23 | 24 | # Check the returned data 25 | assert r.status_code == 200 26 | assert len(r.content) == 100 27 | 28 | # Check pathod's internal log 29 | log = self.d.last_log()["request"] 30 | assert log["method"] == "PUT" 31 | -------------------------------------------------------------------------------- /examples/pathod/test_setupall.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from pathod import test 3 | 4 | 5 | class Test: 6 | """ 7 | Testing the requests module with 8 | a single pathod instance started 9 | for the test suite. 10 | """ 11 | 12 | @classmethod 13 | def setup_class(cls): 14 | cls.d = test.Daemon() 15 | 16 | @classmethod 17 | def teardown_class(cls): 18 | cls.d.shutdown() 19 | 20 | def setup(self): 21 | # Clear the pathod logs between tests 22 | self.d.clear_log() 23 | 24 | def test_simple(self): 25 | # Get a URL for a pathod spec 26 | url = self.d.p("200:b@100") 27 | # ... and request it 28 | r = requests.put(url) 29 | 30 | # Check the returned data 31 | assert r.status_code == 200 32 | assert len(r.content) == 100 33 | 34 | # Check pathod's internal log 35 | log = self.d.last_log()["request"] 36 | assert log["method"] == "PUT" 37 | 38 | def test_two(self): 39 | assert not self.d.log() 40 | -------------------------------------------------------------------------------- /examples/proxapp.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example shows how to graft a WSGI app onto mitmproxy. In this 3 | instance, we're using the Flask framework (http://flask.pocoo.org/) to expose 4 | a single simplest-possible page. 5 | """ 6 | from flask import Flask 7 | from mitmproxy.addons import wsgiapp 8 | 9 | app = Flask("proxapp") 10 | 11 | 12 | @app.route('/') 13 | def hello_world(): 14 | return 'Hello World!' 15 | 16 | 17 | def start(): 18 | # Host app at the magic domain "proxapp" on port 80. Requests to this 19 | # domain and port combination will now be routed to the WSGI app instance. 20 | return wsgiapp.WSGIApp(app, "proxapp", 80) 21 | 22 | # SSL works too, but the magic domain needs to be resolvable from the mitmproxy machine due to mitmproxy's design. 23 | # mitmproxy will connect to said domain and use serve its certificate (unless --no-upstream-cert is set) 24 | # but won't send any data. 25 | # mitmproxy.ctx.master.apps.add(app, "example.com", 443) 26 | -------------------------------------------------------------------------------- /examples/read_dumpfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Simple script showing how to read a mitmproxy dump file 4 | # 5 | 6 | from mitmproxy import flow 7 | from mitmproxy.exceptions import FlowReadException 8 | import pprint 9 | import sys 10 | 11 | with open(sys.argv[1], "rb") as logfile: 12 | freader = io.FlowReader(logfile) 13 | pp = pprint.PrettyPrinter(indent=4) 14 | try: 15 | for f in freader.stream(): 16 | print(f) 17 | print(f.request.host) 18 | pp.pprint(f.get_state()) 19 | print("") 20 | except FlowReadException as e: 21 | print("Flow file corrupted: {}".format(e)) 22 | -------------------------------------------------------------------------------- /examples/redirect_requests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This example shows two ways to redirect flows to other destinations. 3 | """ 4 | from mitmproxy import http 5 | 6 | 7 | def request(flow): 8 | # pretty_host takes the "Host" header of the request into account, 9 | # which is useful in transparent mode where we usually only have the IP 10 | # otherwise. 11 | 12 | # Method 1: Answer with a locally generated response 13 | if flow.request.pretty_host.endswith("example.com"): 14 | flow.response = http.HTTPResponse.make(200, b"Hello World", {"Content-Type": "text/html"}) 15 | 16 | # Method 2: Redirect the request to a different server 17 | if flow.request.pretty_host.endswith("example.org"): 18 | flow.request.host = "mitmproxy.org" 19 | -------------------------------------------------------------------------------- /examples/remote_debug.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script enables remote debugging of the mitmproxy *UI* with PyCharm. 3 | For general debugging purposes, it is easier to just debug mitmdump within PyCharm. 4 | 5 | Usage: 6 | - pip install pydevd on the mitmproxy machine 7 | - Open the Run/Debug Configuration dialog box in PyCharm, and select the Python Remote Debug configuration type. 8 | - Debugging works in the way that mitmproxy connects to the debug server on startup. 9 | Specify host and port that mitmproxy can use to reach your PyCharm instance on startup. 10 | - Adjust this inline script accordingly. 11 | - Start debug server in PyCharm 12 | - Set breakpoints 13 | - Start mitmproxy -s remote_debug.py 14 | """ 15 | 16 | 17 | def start(): 18 | import pydevd 19 | pydevd.settrace("localhost", port=5678, stdoutToServer=True, stderrToServer=True) 20 | -------------------------------------------------------------------------------- /examples/stream.py: -------------------------------------------------------------------------------- 1 | def responseheaders(flow): 2 | """ 3 | Enables streaming for all responses. 4 | """ 5 | flow.response.stream = True 6 | -------------------------------------------------------------------------------- /examples/stream_modify.py: -------------------------------------------------------------------------------- 1 | """ 2 | This inline script modifies a streamed response. 3 | If you do not need streaming, see the modify_response_body example. 4 | Be aware that content replacement isn't trivial: 5 | - If the transfer encoding isn't chunked, you cannot simply change the content length. 6 | - If you want to replace all occurences of "foobar", make sure to catch the cases 7 | where one chunk ends with [...]foo" and the next starts with "bar[...]. 8 | """ 9 | 10 | 11 | def modify(chunks): 12 | """ 13 | chunks is a generator that can be used to iterate over all chunks. 14 | """ 15 | for chunk in chunks: 16 | yield chunk.replace("foo", "bar") 17 | 18 | 19 | def responseheaders(flow): 20 | flow.response.stream = modify 21 | -------------------------------------------------------------------------------- /examples/tcp_message.py: -------------------------------------------------------------------------------- 1 | """ 2 | tcp_message Inline Script Hook API Demonstration 3 | ------------------------------------------------ 4 | 5 | * modifies packets containing "foo" to "bar" 6 | * prints various details for each packet. 7 | 8 | example cmdline invocation: 9 | mitmdump -T --host --tcp ".*" -q -s examples/tcp_message.py 10 | """ 11 | from mitmproxy.utils import strutils 12 | 13 | 14 | def tcp_message(tcp_msg): 15 | modified_msg = tcp_msg.message.replace("foo", "bar") 16 | 17 | is_modified = False if modified_msg == tcp_msg.message else True 18 | tcp_msg.message = modified_msg 19 | 20 | print( 21 | "[tcp_message{}] from {} {} to {} {}:\r\n{}".format( 22 | " (modified)" if is_modified else "", 23 | "client" if tcp_msg.sender == tcp_msg.client_conn else "server", 24 | tcp_msg.sender.address, 25 | "server" if tcp_msg.receiver == tcp_msg.server_conn else "client", 26 | tcp_msg.receiver.address, strutils.bytes_to_escaped_str(tcp_msg.message)) 27 | ) 28 | -------------------------------------------------------------------------------- /examples/upsidedownternet.py: -------------------------------------------------------------------------------- 1 | import io 2 | from PIL import Image 3 | 4 | 5 | def response(flow): 6 | if flow.response.headers.get("content-type", "").startswith("image"): 7 | try: 8 | s = io.StringIO(flow.response.content) 9 | img = Image.open(s).rotate(180) 10 | s2 = io.StringIO() 11 | img.save(s2, "png") 12 | flow.response.content = s2.getvalue() 13 | flow.response.headers["content-type"] = "image/png" 14 | except: # Unknown image types etc. 15 | pass 16 | -------------------------------------------------------------------------------- /issue_template.md: -------------------------------------------------------------------------------- 1 | ##### Steps to reproduce the problem: 2 | 3 | 1. 4 | 2. 5 | 3. 6 | 7 | 8 | ##### Any other comments? What have you tried so far? 9 | 10 | 11 | 12 | ##### System information 13 | 14 | 15 | 21 | -------------------------------------------------------------------------------- /mitmproxy/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/__init__.py -------------------------------------------------------------------------------- /mitmproxy/addons/__init__.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.addons import anticache 2 | from mitmproxy.addons import anticomp 3 | from mitmproxy.addons import clientplayback 4 | from mitmproxy.addons import filestreamer 5 | from mitmproxy.addons import onboarding 6 | from mitmproxy.addons import replace 7 | from mitmproxy.addons import script 8 | from mitmproxy.addons import setheaders 9 | from mitmproxy.addons import serverplayback 10 | from mitmproxy.addons import stickyauth 11 | from mitmproxy.addons import stickycookie 12 | from mitmproxy.addons import streambodies 13 | 14 | 15 | def default_addons(): 16 | return [ 17 | onboarding.Onboarding(), 18 | anticache.AntiCache(), 19 | anticomp.AntiComp(), 20 | stickyauth.StickyAuth(), 21 | stickycookie.StickyCookie(), 22 | script.ScriptLoader(), 23 | filestreamer.FileStreamer(), 24 | streambodies.StreamBodies(), 25 | replace.Replace(), 26 | setheaders.SetHeaders(), 27 | serverplayback.ServerPlayback(), 28 | clientplayback.ClientPlayback(), 29 | ] 30 | -------------------------------------------------------------------------------- /mitmproxy/addons/anticache.py: -------------------------------------------------------------------------------- 1 | class AntiCache: 2 | def __init__(self): 3 | self.enabled = False 4 | 5 | def configure(self, options, updated): 6 | self.enabled = options.anticache 7 | 8 | def request(self, flow): 9 | if self.enabled: 10 | flow.request.anticache() 11 | -------------------------------------------------------------------------------- /mitmproxy/addons/anticomp.py: -------------------------------------------------------------------------------- 1 | class AntiComp: 2 | def __init__(self): 3 | self.enabled = False 4 | 5 | def configure(self, options, updated): 6 | self.enabled = options.anticomp 7 | 8 | def request(self, flow): 9 | if self.enabled: 10 | flow.request.anticomp() 11 | -------------------------------------------------------------------------------- /mitmproxy/addons/onboarding.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.addons import wsgiapp 2 | from mitmproxy.addons.onboardingapp import app 3 | 4 | 5 | class Onboarding(wsgiapp.WSGIApp): 6 | def __init__(self): 7 | super().__init__(app.Adapter(app.application), None, None) 8 | self.enabled = False 9 | 10 | def configure(self, options, updated): 11 | self.host = options.app_host 12 | self.port = options.app_port 13 | self.enabled = options.app 14 | 15 | def request(self, f): 16 | if self.enabled: 17 | super().request(f) 18 | -------------------------------------------------------------------------------- /mitmproxy/addons/onboardingapp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/addons/onboardingapp/__init__.py -------------------------------------------------------------------------------- /mitmproxy/addons/onboardingapp/static/fontawesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/addons/onboardingapp/static/fontawesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /mitmproxy/addons/onboardingapp/static/fontawesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/addons/onboardingapp/static/fontawesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /mitmproxy/addons/onboardingapp/static/fontawesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/addons/onboardingapp/static/fontawesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /mitmproxy/addons/onboardingapp/static/fontawesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/addons/onboardingapp/static/fontawesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /mitmproxy/addons/onboardingapp/static/mitmproxy.css: -------------------------------------------------------------------------------- 1 | 2 | #certbank div { 3 | text-align: center; 4 | 5 | 6 | } 7 | 8 | .fronttable { 9 | } 10 | 11 | .bigtitle { 12 | font-weight: bold; 13 | font-size: 50px; 14 | line-height: 55px; 15 | text-align: center; 16 | display: table; 17 | height: 300px; 18 | } 19 | 20 | .bigtitle>div { 21 | display: table-cell; 22 | vertical-align: middle; 23 | } 24 | 25 | section { 26 | margin-top: 50px; 27 | } 28 | 29 | .example { 30 | margin-top: 10px; 31 | margin-bottom: 10px; 32 | } 33 | 34 | .innerlink { 35 | text-decoration: none; 36 | border-bottom:1px dotted; 37 | margin-bottom: 15px; 38 | } 39 | 40 | .masthead { 41 | padding: 50px 0 60px; 42 | text-align: center; 43 | 44 | } 45 | 46 | .header { 47 | font-size: 1.5em; 48 | } 49 | -------------------------------------------------------------------------------- /mitmproxy/addons/onboardingapp/templates/frame.html: -------------------------------------------------------------------------------- 1 | {% extends "layout.html" %} 2 | {% block content %} 3 |
4 |
5 | {% block body %} 6 | {% end %} 7 |
8 |
9 | {% end %} 10 | -------------------------------------------------------------------------------- /mitmproxy/addons/onboardingapp/templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "frame.html" %} 2 | {% block body %} 3 | 4 |
5 |

Click to install the mitmproxy certificate:

6 |
7 |
8 |
9 | 10 |

Apple

11 |
12 |
13 | 14 |

Windows

15 |
16 |
17 | 18 |

Android

19 |
20 |
21 | 22 |

Other

23 |
24 |
25 | 26 |
27 |
28 | Other mitmproxy users cannot intercept your connection. 29 |
30 |
31 | This page is served by your local mitmproxy instance. The certificate you are about to install has been uniquely generated on mitmproxy's first run and is not shared 32 | between mitmproxy installations. 33 |
34 | 35 | {% end %} 36 | -------------------------------------------------------------------------------- /mitmproxy/addons/onboardingapp/templates/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | mitmproxy 12 | 13 | 14 | 15 | 16 | 17 | 18 | 25 | 26 |
27 | {% block content %} 28 | {% end %} 29 |
30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /mitmproxy/addons/stickyauth.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import exceptions 2 | from mitmproxy import flowfilter 3 | 4 | 5 | class StickyAuth: 6 | def __init__(self): 7 | self.flt = None 8 | self.hosts = {} 9 | 10 | def configure(self, options, updated): 11 | if options.stickyauth: 12 | flt = flowfilter.parse(options.stickyauth) 13 | if not flt: 14 | raise exceptions.OptionsError( 15 | "stickyauth: invalid filter expression: %s" % options.stickyauth 16 | ) 17 | self.flt = flt 18 | 19 | def request(self, flow): 20 | host = flow.request.host 21 | if "authorization" in flow.request.headers: 22 | self.hosts[host] = flow.request.headers["authorization"] 23 | elif flowfilter.match(self.flt, flow): 24 | if host in self.hosts: 25 | flow.request.headers["authorization"] = self.hosts[host] 26 | -------------------------------------------------------------------------------- /mitmproxy/addons/termlog.py: -------------------------------------------------------------------------------- 1 | import click 2 | 3 | from mitmproxy import log 4 | 5 | 6 | class TermLog: 7 | def __init__(self): 8 | self.options = None 9 | 10 | def configure(self, options, updated): 11 | self.options = options 12 | 13 | def log(self, e): 14 | if self.options.verbosity >= log.log_tier(e.level): 15 | click.secho( 16 | e.msg, 17 | file=self.options.tfile, 18 | fg=dict(error="red", warn="yellow").get(e.level), 19 | dim=(e.level == "debug"), 20 | err=(e.level == "error") 21 | ) 22 | -------------------------------------------------------------------------------- /mitmproxy/addons/wsgiapp.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import ctx 2 | from mitmproxy import exceptions 3 | 4 | from mitmproxy.net import wsgi 5 | from mitmproxy import version 6 | 7 | 8 | class WSGIApp: 9 | """ 10 | An addon that hosts a WSGI app withing mitproxy, at a specified 11 | hostname and port. 12 | """ 13 | def __init__(self, app, host, port): 14 | self.app, self.host, self.port = app, host, port 15 | 16 | def serve(self, app, flow): 17 | """ 18 | Serves app on flow, and prevents further handling of the flow. 19 | """ 20 | app = wsgi.WSGIAdaptor( 21 | app, 22 | flow.request.pretty_host, 23 | flow.request.port, 24 | version.MITMPROXY 25 | ) 26 | err = app.serve( 27 | flow, 28 | flow.client_conn.wfile, 29 | **{"mitmproxy.master": ctx.master} 30 | ) 31 | if err: 32 | ctx.log.error("Error in wsgi app. %s" % err) 33 | flow.reply.kill() 34 | raise exceptions.AddonHalt() 35 | 36 | def request(self, f): 37 | if (f.request.pretty_host, f.request.port) == (self.host, self.port): 38 | self.serve(self.app, f) 39 | -------------------------------------------------------------------------------- /mitmproxy/contrib/README: -------------------------------------------------------------------------------- 1 | 2 | Contribs: 3 | 4 | jsbeautifier, git checkout 25/03/12, MIT license 5 | - Removed test directories 6 | - Disabled packers through a single-line modification (see "# CORTESI" 7 | comment) 8 | 9 | wbxml 10 | - https://github.com/davidpshaw/PyWBXMLDecoder 11 | 12 | tls, BSD license 13 | - https://github.com/mhils/tls/tree/mitmproxy 14 | - limited to required files. -------------------------------------------------------------------------------- /mitmproxy/contrib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/contrib/__init__.py -------------------------------------------------------------------------------- /mitmproxy/contrib/tls/__init__.py: -------------------------------------------------------------------------------- 1 | # This file is dual licensed under the terms of the Apache License, Version 2 | # 2.0, and the BSD License. See the LICENSE file in the root of this repository 3 | # for complete details. 4 | 5 | -------------------------------------------------------------------------------- /mitmproxy/contrib/tls/utils.py: -------------------------------------------------------------------------------- 1 | # This file is dual licensed under the terms of the Apache License, Version 2 | # 2.0, and the BSD License. See the LICENSE file in the root of this repository 3 | # for complete details. 4 | 5 | 6 | import construct 7 | 8 | class _UBInt24(construct.Adapter): 9 | def _encode(self, obj, context): 10 | return bytes( 11 | (obj & 0xFF0000) >> 16, 12 | (obj & 0x00FF00) >> 8, 13 | obj & 0x0000FF 14 | ) 15 | 16 | def _decode(self, obj, context): 17 | obj = bytearray(obj) 18 | return (obj[0] << 16 | obj[1] << 8 | obj[2]) 19 | 20 | 21 | def UBInt24(name): # noqa 22 | return _UBInt24(construct.Bytes(name, 3)) 23 | -------------------------------------------------------------------------------- /mitmproxy/contrib/wbxml/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/contrib/wbxml/__init__.py -------------------------------------------------------------------------------- /mitmproxy/ctx.py: -------------------------------------------------------------------------------- 1 | master = None # type: "mitmproxy.master.Master" 2 | log = None # type: "mitmproxy.log.Log" 3 | -------------------------------------------------------------------------------- /mitmproxy/log.py: -------------------------------------------------------------------------------- 1 | 2 | class LogEntry: 3 | def __init__(self, msg, level): 4 | self.msg = msg 5 | self.level = level 6 | 7 | 8 | class Log: 9 | """ 10 | The central logger, exposed to scripts as mitmproxy.ctx.log. 11 | """ 12 | def __init__(self, master): 13 | self.master = master 14 | 15 | def debug(self, txt): 16 | """ 17 | Log with level debug. 18 | """ 19 | self(txt, "debug") 20 | 21 | def info(self, txt): 22 | """ 23 | Log with level info. 24 | """ 25 | self(txt, "info") 26 | 27 | def warn(self, txt): 28 | """ 29 | Log with level warn. 30 | """ 31 | self(txt, "warn") 32 | 33 | def error(self, txt): 34 | """ 35 | Log with level error. 36 | """ 37 | self(txt, "error") 38 | 39 | def __call__(self, text, level="info"): 40 | self.master.add_log(text, level) 41 | 42 | 43 | def log_tier(level): 44 | return dict(error=0, warn=1, info=2, debug=3).get(level) 45 | -------------------------------------------------------------------------------- /mitmproxy/net/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/net/__init__.py -------------------------------------------------------------------------------- /mitmproxy/net/check.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | _label_valid = re.compile(b"(?!-)[A-Z\d-]{1,63}(? bool: 7 | """ 8 | Checks if a hostname is valid. 9 | """ 10 | try: 11 | host.decode("idna") 12 | except ValueError: 13 | return False 14 | if len(host) > 255: 15 | return False 16 | if host and host[-1:] == b".": 17 | host = host[:-1] 18 | return all(_label_valid.match(x) for x in host.split(b".")) 19 | 20 | 21 | def is_valid_port(port): 22 | return 0 <= port <= 65535 23 | -------------------------------------------------------------------------------- /mitmproxy/net/http/__init__.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.net.http.request import Request 2 | from mitmproxy.net.http.response import Response 3 | from mitmproxy.net.http.message import Message 4 | from mitmproxy.net.http.headers import Headers, parse_content_type 5 | from mitmproxy.net.http.message import decoded 6 | from mitmproxy.net.http import http1, http2, status_codes, multipart 7 | 8 | __all__ = [ 9 | "Request", 10 | "Response", 11 | "Message", 12 | "Headers", "parse_content_type", 13 | "decoded", 14 | "http1", "http2", "status_codes", "multipart", 15 | ] 16 | -------------------------------------------------------------------------------- /mitmproxy/net/http/http1/__init__.py: -------------------------------------------------------------------------------- 1 | from .read import ( 2 | read_request, read_request_head, 3 | read_response, read_response_head, 4 | read_body, 5 | connection_close, 6 | expected_http_body_size, 7 | ) 8 | from .assemble import ( 9 | assemble_request, assemble_request_head, 10 | assemble_response, assemble_response_head, 11 | assemble_body, 12 | ) 13 | 14 | 15 | __all__ = [ 16 | "read_request", "read_request_head", 17 | "read_response", "read_response_head", 18 | "read_body", 19 | "connection_close", 20 | "expected_http_body_size", 21 | "assemble_request", "assemble_request_head", 22 | "assemble_response", "assemble_response_head", 23 | "assemble_body", 24 | ] 25 | -------------------------------------------------------------------------------- /mitmproxy/net/http/http2/__init__.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.net.http.http2.framereader import read_raw_frame, parse_frame 2 | from mitmproxy.net.http.http2.utils import parse_headers 3 | 4 | __all__ = [ 5 | "read_raw_frame", 6 | "parse_frame", 7 | "parse_headers", 8 | ] 9 | -------------------------------------------------------------------------------- /mitmproxy/net/http/http2/framereader.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | 3 | import hyperframe 4 | from mitmproxy import exceptions 5 | 6 | 7 | def read_raw_frame(rfile): 8 | header = rfile.safe_read(9) 9 | length = int(codecs.encode(header[:3], 'hex_codec'), 16) 10 | 11 | if length == 4740180: 12 | raise exceptions.HttpException("Length field looks more like HTTP/1.1:\n{}".format(rfile.read(-1))) 13 | 14 | body = rfile.safe_read(length) 15 | return [header, body] 16 | 17 | 18 | def parse_frame(header, body=None): 19 | if body is None: 20 | body = header[9:] 21 | header = header[:9] 22 | 23 | frame, length = hyperframe.frame.Frame.parse_frame_header(header) 24 | frame.parse_body(memoryview(body)) 25 | return frame 26 | -------------------------------------------------------------------------------- /mitmproxy/net/http/http2/utils.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.net.http import url 2 | 3 | 4 | def parse_headers(headers): 5 | authority = headers.get(':authority', '').encode() 6 | method = headers.get(':method', 'GET').encode() 7 | scheme = headers.get(':scheme', 'https').encode() 8 | path = headers.get(':path', '/').encode() 9 | 10 | headers.pop(":method", None) 11 | headers.pop(":scheme", None) 12 | headers.pop(":path", None) 13 | 14 | host = None 15 | port = None 16 | 17 | if path == b'*' or path.startswith(b"/"): 18 | first_line_format = "relative" 19 | elif method == b'CONNECT': # pragma: no cover 20 | raise NotImplementedError("CONNECT over HTTP/2 is not implemented.") 21 | else: # pragma: no cover 22 | first_line_format = "absolute" 23 | # FIXME: verify if path or :host contains what we need 24 | scheme, host, port, _ = url.parse(path) 25 | 26 | if authority: 27 | host, _, port = authority.partition(b':') 28 | 29 | if not host: 30 | host = b'localhost' 31 | 32 | if not port: 33 | port = 443 if scheme == b'https' else 80 34 | 35 | port = int(port) 36 | 37 | return first_line_format, method, scheme, host, port, path 38 | -------------------------------------------------------------------------------- /mitmproxy/net/http/multipart.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from mitmproxy.net.http import headers 4 | 5 | 6 | def decode(hdrs, content): 7 | """ 8 | Takes a multipart boundary encoded string and returns list of (key, value) tuples. 9 | """ 10 | v = hdrs.get("content-type") 11 | if v: 12 | v = headers.parse_content_type(v) 13 | if not v: 14 | return [] 15 | try: 16 | boundary = v[2]["boundary"].encode("ascii") 17 | except (KeyError, UnicodeError): 18 | return [] 19 | 20 | rx = re.compile(br'\bname="([^"]+)"') 21 | r = [] 22 | 23 | for i in content.split(b"--" + boundary): 24 | parts = i.splitlines() 25 | if len(parts) > 1 and parts[0][0:2] != b"--": 26 | match = rx.search(parts[1]) 27 | if match: 28 | key = match.group(1) 29 | value = b"".join(parts[3 + parts[2:].index(b""):]) 30 | r.append((key, value)) 31 | return r 32 | return [] 33 | -------------------------------------------------------------------------------- /mitmproxy/net/websockets/__init__.py: -------------------------------------------------------------------------------- 1 | from .frame import FrameHeader 2 | from .frame import Frame 3 | from .frame import OPCODE 4 | from .frame import CLOSE_REASON 5 | from .masker import Masker 6 | from .utils import MAGIC 7 | from .utils import VERSION 8 | from .utils import client_handshake_headers 9 | from .utils import server_handshake_headers 10 | from .utils import check_handshake 11 | from .utils import check_client_version 12 | from .utils import create_server_nonce 13 | from .utils import get_extensions 14 | from .utils import get_protocol 15 | from .utils import get_client_key 16 | from .utils import get_server_accept 17 | 18 | __all__ = [ 19 | "FrameHeader", 20 | "Frame", 21 | "OPCODE", 22 | "CLOSE_REASON", 23 | "Masker", 24 | "MAGIC", 25 | "VERSION", 26 | "client_handshake_headers", 27 | "server_handshake_headers", 28 | "check_handshake", 29 | "check_client_version", 30 | "create_server_nonce", 31 | "get_extensions", 32 | "get_protocol", 33 | "get_client_key", 34 | "get_server_accept", 35 | ] 36 | -------------------------------------------------------------------------------- /mitmproxy/net/websockets/masker.py: -------------------------------------------------------------------------------- 1 | class Masker: 2 | """ 3 | Data sent from the server must be masked to prevent malicious clients 4 | from sending data over the wire in predictable patterns. 5 | 6 | Servers do not have to mask data they send to the client. 7 | https://tools.ietf.org/html/rfc6455#section-5.3 8 | """ 9 | 10 | def __init__(self, key): 11 | self.key = key 12 | self.offset = 0 13 | 14 | def mask(self, offset, data): 15 | result = bytearray(data) 16 | for i in range(len(data)): 17 | result[i] ^= self.key[offset % 4] 18 | offset += 1 19 | result = bytes(result) 20 | return result 21 | 22 | def __call__(self, data): 23 | ret = self.mask(self.offset, data) 24 | self.offset += len(ret) 25 | return ret 26 | -------------------------------------------------------------------------------- /mitmproxy/platform/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import re 3 | 4 | resolver = None 5 | 6 | if re.match(r"linux(?:2)?", sys.platform): 7 | from . import linux 8 | resolver = linux.Resolver 9 | elif sys.platform == "darwin": 10 | from . import osx 11 | resolver = osx.Resolver 12 | elif sys.platform.startswith("freebsd"): 13 | from . import osx 14 | resolver = osx.Resolver 15 | elif sys.platform == "win32": 16 | from . import windows 17 | resolver = windows.Resolver 18 | -------------------------------------------------------------------------------- /mitmproxy/platform/linux.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import struct 3 | 4 | # Python socket module does not have this constant 5 | SO_ORIGINAL_DST = 80 6 | 7 | 8 | class Resolver: 9 | 10 | def original_addr(self, csock): 11 | odestdata = csock.getsockopt(socket.SOL_IP, SO_ORIGINAL_DST, 16) 12 | _, port, a1, a2, a3, a4 = struct.unpack("!HHBBBBxxxxxxxx", odestdata) 13 | address = "%d.%d.%d.%d" % (a1, a2, a3, a4) 14 | return address, port 15 | -------------------------------------------------------------------------------- /mitmproxy/platform/pf.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | def lookup(address, port, s): 5 | """ 6 | Parse the pfctl state output s, to look up the destination host 7 | matching the client (address, port). 8 | 9 | Returns an (address, port) tuple, or None. 10 | """ 11 | s = s.decode() 12 | spec = "%s:%s" % (address, port) 13 | for i in s.split("\n"): 14 | if "ESTABLISHED:ESTABLISHED" in i and spec in i: 15 | s = i.split() 16 | if len(s) > 4: 17 | if sys.platform.startswith("freebsd"): 18 | # strip parentheses for FreeBSD pfctl 19 | s = s[3][1:-1].split(":") 20 | else: 21 | s = s[4].split(":") 22 | 23 | if len(s) == 2: 24 | return s[0], int(s[1]) 25 | raise RuntimeError("Could not resolve original destination.") 26 | -------------------------------------------------------------------------------- /mitmproxy/proxy/__init__.py: -------------------------------------------------------------------------------- 1 | from .config import ProxyConfig 2 | from .root_context import RootContext 3 | from .server import ProxyServer, DummyServer 4 | 5 | __all__ = [ 6 | "ProxyServer", "DummyServer", 7 | "ProxyConfig", 8 | "RootContext" 9 | ] 10 | -------------------------------------------------------------------------------- /mitmproxy/proxy/modes/__init__.py: -------------------------------------------------------------------------------- 1 | from .http_proxy import HttpProxy, HttpUpstreamProxy 2 | from .reverse_proxy import ReverseProxy 3 | from .socks_proxy import Socks5Proxy 4 | from .transparent_proxy import TransparentProxy 5 | 6 | __all__ = [ 7 | "HttpProxy", "HttpUpstreamProxy", 8 | "ReverseProxy", 9 | "Socks5Proxy", 10 | "TransparentProxy" 11 | ] 12 | -------------------------------------------------------------------------------- /mitmproxy/proxy/modes/http_proxy.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.proxy import protocol 2 | 3 | 4 | class HttpProxy(protocol.Layer, protocol.ServerConnectionMixin): 5 | 6 | def __call__(self): 7 | layer = self.ctx.next_layer(self) 8 | try: 9 | layer() 10 | finally: 11 | if self.server_conn: 12 | self.disconnect() 13 | 14 | 15 | class HttpUpstreamProxy(protocol.Layer, protocol.ServerConnectionMixin): 16 | 17 | def __init__(self, ctx, server_address): 18 | super().__init__(ctx, server_address=server_address) 19 | 20 | def __call__(self): 21 | layer = self.ctx.next_layer(self) 22 | try: 23 | layer() 24 | finally: 25 | if self.server_conn: 26 | self.disconnect() 27 | -------------------------------------------------------------------------------- /mitmproxy/proxy/modes/reverse_proxy.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.proxy import protocol 2 | 3 | 4 | class ReverseProxy(protocol.Layer, protocol.ServerConnectionMixin): 5 | 6 | def __init__(self, ctx, server_address, server_tls): 7 | super().__init__(ctx, server_address=server_address) 8 | self.server_tls = server_tls 9 | 10 | def __call__(self): 11 | layer = self.ctx.next_layer(self) 12 | try: 13 | layer() 14 | finally: 15 | if self.server_conn: 16 | self.disconnect() 17 | -------------------------------------------------------------------------------- /mitmproxy/proxy/modes/transparent_proxy.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import exceptions 2 | from mitmproxy import platform 3 | from mitmproxy.proxy import protocol 4 | 5 | 6 | class TransparentProxy(protocol.Layer, protocol.ServerConnectionMixin): 7 | 8 | def __init__(self, ctx): 9 | super().__init__(ctx) 10 | self.resolver = platform.resolver() 11 | 12 | def __call__(self): 13 | try: 14 | self.server_conn.address = self.resolver.original_addr(self.client_conn.connection) 15 | except Exception as e: 16 | raise exceptions.ProtocolException("Transparent mode failure: %s" % repr(e)) 17 | 18 | layer = self.ctx.next_layer(self) 19 | try: 20 | layer() 21 | finally: 22 | if self.server_conn: 23 | self.disconnect() 24 | -------------------------------------------------------------------------------- /mitmproxy/script/__init__.py: -------------------------------------------------------------------------------- 1 | from .concurrent import concurrent 2 | 3 | __all__ = [ 4 | "concurrent", 5 | ] 6 | -------------------------------------------------------------------------------- /mitmproxy/script/concurrent.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module provides a @concurrent decorator primitive to 3 | offload computations from mitmproxy's main master thread. 4 | """ 5 | 6 | from mitmproxy import events 7 | from mitmproxy.types import basethread 8 | 9 | 10 | class ScriptThread(basethread.BaseThread): 11 | name = "ScriptThread" 12 | 13 | 14 | def concurrent(fn): 15 | if fn.__name__ not in events.Events - {"start", "configure", "tick"}: 16 | raise NotImplementedError( 17 | "Concurrent decorator not supported for '%s' method." % fn.__name__ 18 | ) 19 | 20 | def _concurrent(obj): 21 | def run(): 22 | fn(obj) 23 | if obj.reply.state == "taken": 24 | if not obj.reply.has_message: 25 | obj.reply.ack() 26 | obj.reply.commit() 27 | obj.reply.take() 28 | ScriptThread( 29 | "script.concurrent (%s)" % fn.__name__, 30 | target=run 31 | ).start() 32 | return _concurrent 33 | -------------------------------------------------------------------------------- /mitmproxy/tools/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/__init__.py -------------------------------------------------------------------------------- /mitmproxy/tools/console/__init__.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.tools.console import master 2 | 3 | 4 | __all__ = ["master"] 5 | -------------------------------------------------------------------------------- /mitmproxy/tools/console/grideditor/__init__.py: -------------------------------------------------------------------------------- 1 | from .editors import * # noqa 2 | from . import base # noqa 3 | -------------------------------------------------------------------------------- /mitmproxy/tools/console/signals.py: -------------------------------------------------------------------------------- 1 | import blinker 2 | 3 | # Show a status message in the action bar 4 | sig_add_log = blinker.Signal() 5 | 6 | 7 | def add_log(e, level): 8 | sig_add_log.send( 9 | None, 10 | e=e, 11 | level=level 12 | ) 13 | 14 | # Show a status message in the action bar 15 | status_message = blinker.Signal() 16 | 17 | # Prompt for input 18 | status_prompt = blinker.Signal() 19 | 20 | # Prompt for a path 21 | status_prompt_path = blinker.Signal() 22 | 23 | # Prompt for a single keystroke 24 | status_prompt_onekey = blinker.Signal() 25 | 26 | # Call a callback in N seconds 27 | call_in = blinker.Signal() 28 | 29 | # Focus the body, footer or header of the main window 30 | focus = blinker.Signal() 31 | 32 | # Fired when settings change 33 | update_settings = blinker.Signal() 34 | 35 | # Fired when a flow changes 36 | flow_change = blinker.Signal() 37 | 38 | # Fired when the flow list or focus changes 39 | flowlist_change = blinker.Signal() 40 | 41 | # Pop and push view state onto a stack 42 | pop_view_state = blinker.Signal() 43 | push_view_state = blinker.Signal() 44 | replace_view_state = blinker.Signal() 45 | -------------------------------------------------------------------------------- /mitmproxy/tools/web/__init__.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.tools.web import master 2 | __all__ = ["master"] 3 | -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/chrome-devtools/resourceCSSIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/chrome-devtools/resourceCSSIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/chrome-devtools/resourceDocumentIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/chrome-devtools/resourceDocumentIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/chrome-devtools/resourceJSIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/chrome-devtools/resourceJSIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/chrome-devtools/resourcePlainIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/chrome-devtools/resourcePlainIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/favicon.ico -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/resourceExecutableIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/resourceExecutableIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/resourceFlashIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/resourceFlashIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/resourceImageIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/resourceImageIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/resourceJavaIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/resourceJavaIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/resourceNotModifiedIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/resourceNotModifiedIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/static/images/resourceRedirectIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/tools/web/static/images/resourceRedirectIcon.png -------------------------------------------------------------------------------- /mitmproxy/tools/web/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mitmproxy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | -------------------------------------------------------------------------------- /mitmproxy/types/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/types/__init__.py -------------------------------------------------------------------------------- /mitmproxy/types/basethread.py: -------------------------------------------------------------------------------- 1 | import time 2 | import threading 3 | 4 | 5 | class BaseThread(threading.Thread): 6 | def __init__(self, name, *args, **kwargs): 7 | super().__init__(name=name, *args, **kwargs) 8 | self._thread_started = time.time() 9 | 10 | def _threadinfo(self): 11 | return "%s - age: %is" % ( 12 | self.name, 13 | int(time.time() - self._thread_started) 14 | ) 15 | -------------------------------------------------------------------------------- /mitmproxy/types/bidi.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class BiDi: 4 | 5 | """ 6 | A wee utility class for keeping bi-directional mappings, like field 7 | constants in protocols. Names are attributes on the object, dict-like 8 | access maps values to names: 9 | 10 | CONST = BiDi(a=1, b=2) 11 | assert CONST.a == 1 12 | assert CONST.get_name(1) == "a" 13 | """ 14 | 15 | def __init__(self, **kwargs): 16 | self.names = kwargs 17 | self.values = {} 18 | for k, v in kwargs.items(): 19 | self.values[v] = k 20 | if len(self.names) != len(self.values): 21 | raise ValueError("Duplicate values not allowed.") 22 | 23 | def __getattr__(self, k): 24 | if k in self.names: 25 | return self.names[k] 26 | raise AttributeError("No such attribute: %s", k) 27 | 28 | def get_name(self, n, default=None): 29 | return self.values.get(n, default) 30 | -------------------------------------------------------------------------------- /mitmproxy/types/serializable.py: -------------------------------------------------------------------------------- 1 | import abc 2 | 3 | 4 | class Serializable(metaclass=abc.ABCMeta): 5 | """ 6 | Abstract Base Class that defines an API to save an object's state and restore it later on. 7 | """ 8 | 9 | @classmethod 10 | @abc.abstractmethod 11 | def from_state(cls, state): 12 | """ 13 | Create a new object from the given state. 14 | """ 15 | raise NotImplementedError() 16 | 17 | @abc.abstractmethod 18 | def get_state(self): 19 | """ 20 | Retrieve object state. 21 | """ 22 | raise NotImplementedError() 23 | 24 | @abc.abstractmethod 25 | def set_state(self, state): 26 | """ 27 | Set object state to the given state. 28 | """ 29 | raise NotImplementedError() 30 | 31 | def copy(self): 32 | return self.from_state(self.get_state()) 33 | -------------------------------------------------------------------------------- /mitmproxy/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/mitmproxy/utils/__init__.py -------------------------------------------------------------------------------- /mitmproxy/utils/bits.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def setbit(byte, offset, value): 4 | """ 5 | Set a bit in a byte to 1 if value is truthy, 0 if not. 6 | """ 7 | if value: 8 | return byte | (1 << offset) 9 | else: 10 | return byte & ~(1 << offset) 11 | 12 | 13 | def getbit(byte, offset): 14 | mask = 1 << offset 15 | return bool(byte & mask) 16 | -------------------------------------------------------------------------------- /mitmproxy/utils/data.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import importlib 3 | import inspect 4 | 5 | 6 | class Data: 7 | 8 | def __init__(self, name): 9 | m = importlib.import_module(name) 10 | dirname = os.path.dirname(inspect.getsourcefile(m)) 11 | self.dirname = os.path.abspath(dirname) 12 | 13 | def push(self, subpath): 14 | """ 15 | Change the data object to a path relative to the module. 16 | """ 17 | self.dirname = os.path.join(self.dirname, subpath) 18 | return self 19 | 20 | def path(self, path): 21 | """ 22 | Returns a path to the package data housed at 'path' under this 23 | module.Path can be a path to a file, or to a directory. 24 | 25 | This function will raise ValueError if the path does not exist. 26 | """ 27 | fullpath = os.path.join(self.dirname, path) 28 | if not os.path.exists(fullpath): 29 | raise ValueError("dataPath: %s does not exist." % fullpath) 30 | return fullpath 31 | 32 | 33 | pkg_data = Data(__name__).push("..") 34 | -------------------------------------------------------------------------------- /mitmproxy/version.py: -------------------------------------------------------------------------------- 1 | IVERSION = (0, 19) 2 | VERSION = ".".join(str(i) for i in IVERSION) 3 | PATHOD = "pathod " + VERSION 4 | MITMPROXY = "mitmproxy " + VERSION 5 | -------------------------------------------------------------------------------- /pathod/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/pathod/__init__.py -------------------------------------------------------------------------------- /pathod/language/exceptions.py: -------------------------------------------------------------------------------- 1 | class RenderError(Exception): 2 | pass 3 | 4 | 5 | class FileAccessDenied(RenderError): 6 | pass 7 | 8 | 9 | class ParseException(Exception): 10 | 11 | def __init__(self, msg, s, col): 12 | Exception.__init__(self) 13 | self.msg = msg 14 | self.s = s 15 | self.col = col 16 | 17 | def marked(self): 18 | return "%s\n%s" % (self.s, " " * (self.col - 1) + "^") 19 | 20 | def __str__(self): 21 | return "%s at char %s" % (self.msg, self.col) 22 | -------------------------------------------------------------------------------- /pathod/protocols/__init__.py: -------------------------------------------------------------------------------- 1 | from . import http, http2, websockets 2 | 3 | __all__ = [ 4 | "http", 5 | "http2", 6 | "websockets", 7 | ] 8 | -------------------------------------------------------------------------------- /pathod/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from mitmproxy.utils import data as mdata 4 | 5 | 6 | class MemBool: 7 | 8 | """ 9 | Truth-checking with a memory, for use in chained if statements. 10 | """ 11 | 12 | def __init__(self): 13 | self.v = None 14 | 15 | def __call__(self, v): 16 | self.v = v 17 | return bool(v) 18 | 19 | 20 | # FIXME: change this name 21 | data = mdata.Data(__name__) 22 | 23 | 24 | def daemonize(stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'): # pragma: no cover 25 | try: 26 | pid = os.fork() 27 | if pid > 0: 28 | sys.exit(0) 29 | except OSError as e: 30 | sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror)) 31 | sys.exit(1) 32 | os.chdir("/") 33 | os.umask(0) 34 | os.setsid() 35 | try: 36 | pid = os.fork() 37 | if pid > 0: 38 | sys.exit(0) 39 | except OSError as e: 40 | sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror)) 41 | sys.exit(1) 42 | si = open(stdin, 'rb') 43 | so = open(stdout, 'a+b') 44 | se = open(stderr, 'a+b', 0) 45 | os.dup2(si.fileno(), sys.stdin.fileno()) 46 | os.dup2(so.fileno(), sys.stdout.fileno()) 47 | os.dup2(se.fileno(), sys.stderr.fileno()) 48 | -------------------------------------------------------------------------------- /release/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /dist 3 | -------------------------------------------------------------------------------- /release/README.md: -------------------------------------------------------------------------------- 1 | # Release Checklist 2 | 3 | - Verify that all CI tests pass for current master 4 | - Tag the release, and push to Github 5 | - Wait for tag CI to complete 6 | - Download assets from snapshots.mitmproxy.org 7 | - Create release notice on Github 8 | - Upload wheel to pypi 9 | -------------------------------------------------------------------------------- /release/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name='mitmproxy-rtool', 5 | version="1.0", 6 | py_modules=["rtool"], 7 | install_requires=[ 8 | "click>=6.2, <7.0", 9 | "twine>=1.6.5, <1.8", 10 | "virtualenv>=14.0.5, <15.1", 11 | "wheel>=0.29.0, <0.30", 12 | "pysftp>=0.2.8, !=0.2.9, <0.3", 13 | ], 14 | entry_points={ 15 | "console_scripts": [ 16 | "rtool=rtool:cli", 17 | ], 18 | }, 19 | ) 20 | -------------------------------------------------------------------------------- /release/specs/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/release/specs/icon.ico -------------------------------------------------------------------------------- /release/specs/mitmdump: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from mitmproxy.tools.main import mitmdump 3 | mitmdump() 4 | -------------------------------------------------------------------------------- /release/specs/mitmdump.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | from PyInstaller.utils.hooks import collect_data_files 4 | 5 | a = Analysis(['mitmdump'], 6 | binaries=None, 7 | datas=collect_data_files("mitmproxy.addons.onboardingapp"), 8 | hiddenimports=[], 9 | hookspath=None, 10 | runtime_hooks=None, 11 | excludes=None) 12 | pyz = PYZ(a.pure, a.zipped_data) 13 | exe = EXE(pyz, 14 | a.scripts, 15 | a.binaries, 16 | a.zipfiles, 17 | a.datas, 18 | name='mitmdump', 19 | debug=False, 20 | strip=None, 21 | upx=True, 22 | console=True, 23 | icon='icon.ico' ) 24 | -------------------------------------------------------------------------------- /release/specs/mitmproxy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from mitmproxy.tools.main import mitmproxy 3 | mitmproxy() 4 | -------------------------------------------------------------------------------- /release/specs/mitmproxy.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | from PyInstaller.utils.hooks import collect_data_files 4 | 5 | a = Analysis(['mitmproxy'], 6 | binaries=None, 7 | datas=collect_data_files("mitmproxy.addons.onboardingapp"), 8 | hiddenimports=[], 9 | hookspath=None, 10 | runtime_hooks=None, 11 | excludes=None) 12 | pyz = PYZ(a.pure, a.zipped_data) 13 | exe = EXE(pyz, 14 | a.scripts, 15 | a.binaries, 16 | a.zipfiles, 17 | a.datas, 18 | name='mitmproxy', 19 | debug=False, 20 | strip=None, 21 | upx=True, 22 | console=True, 23 | icon='icon.ico' ) 24 | -------------------------------------------------------------------------------- /release/specs/mitmweb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from mitmproxy.tools.main import mitmweb 3 | 4 | mitmweb() 5 | -------------------------------------------------------------------------------- /release/specs/mitmweb.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | from PyInstaller.utils.hooks import collect_data_files 4 | 5 | a = Analysis(['mitmweb'], 6 | binaries=None, 7 | datas=collect_data_files("mitmproxy"), 8 | hiddenimports=[], 9 | hookspath=None, 10 | runtime_hooks=None, 11 | excludes=None) 12 | pyz = PYZ(a.pure, a.zipped_data) 13 | exe = EXE(pyz, 14 | a.scripts, 15 | a.binaries, 16 | a.zipfiles, 17 | a.datas, 18 | name='mitmweb', 19 | debug=False, 20 | strip=None, 21 | upx=True, 22 | console=True, 23 | icon='icon.ico' ) 24 | -------------------------------------------------------------------------------- /release/specs/pathoc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pathod import pathoc_cmdline as cmdline 4 | 5 | if __name__ == "__main__": 6 | cmdline.go_pathoc() 7 | -------------------------------------------------------------------------------- /release/specs/pathoc.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | from PyInstaller.utils.hooks import collect_data_files 4 | 5 | a = Analysis(['pathoc'], 6 | binaries=None, 7 | datas=None, 8 | hiddenimports=['_cffi_backend'], 9 | hookspath=None, 10 | runtime_hooks=None, 11 | excludes=None) 12 | pyz = PYZ(a.pure, a.zipped_data) 13 | exe = EXE(pyz, 14 | a.scripts, 15 | a.binaries, 16 | a.zipfiles, 17 | a.datas, 18 | name='pathoc', 19 | debug=False, 20 | strip=None, 21 | upx=True, 22 | console=True, 23 | icon='icon.ico' ) 24 | -------------------------------------------------------------------------------- /release/specs/pathod: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pathod import pathod_cmdline as cmdline 4 | 5 | if __name__ == "__main__": 6 | cmdline.go_pathod() 7 | -------------------------------------------------------------------------------- /release/specs/pathod.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | from PyInstaller.utils.hooks import collect_data_files 4 | 5 | a = Analysis(['pathod'], 6 | binaries=None, 7 | datas=collect_data_files("pathod"), 8 | hiddenimports=['_cffi_backend'], 9 | hookspath=None, 10 | runtime_hooks=None, 11 | excludes=None) 12 | pyz = PYZ(a.pure, a.zipped_data) 13 | exe = EXE(pyz, 14 | a.scripts, 15 | a.binaries, 16 | a.zipfiles, 17 | a.datas, 18 | name='pathod', 19 | debug=False, 20 | strip=None, 21 | upx=True, 22 | console=True, 23 | icon='icon.ico' ) 24 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | https://snapshots.mitmproxy.org/misc/lxml-3.6.0-cp35-cp35m-win32.whl; sys_platform == 'win32' and python_version == '3.5' 2 | -e .[dev,examples,contentviews] 3 | -e ./release 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 140 3 | max-complexity = 25 4 | ignore = E251,C901,W503 5 | exclude = mitmproxy/contrib/*,test/mitmproxy/data/* 6 | addons = file,open,basestring,xrange,unicode,long,cmp 7 | 8 | [tool:pytest] 9 | testpaths = test 10 | addopts = --capture=no --color=yes 11 | 12 | [coverage:run] 13 | branch = True 14 | omit = *contrib*, *tnetstring*, *platform*, *main.py 15 | 16 | [coverage:report] 17 | show_missing = True 18 | exclude_lines = 19 | pragma: no cover 20 | raise NotImplementedError() 21 | -------------------------------------------------------------------------------- /test/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/__init__.py: -------------------------------------------------------------------------------- 1 | # Silence third-party modules 2 | import logging 3 | logging.getLogger("hyper").setLevel(logging.WARNING) 4 | logging.getLogger("requests").setLevel(logging.WARNING) 5 | logging.getLogger("passlib").setLevel(logging.WARNING) 6 | logging.getLogger("PIL").setLevel(logging.WARNING) 7 | logging.getLogger("tornado").setLevel(logging.WARNING) 8 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/addons/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_anticache.py: -------------------------------------------------------------------------------- 1 | from .. import tutils, mastertest 2 | from mitmproxy.addons import anticache 3 | from mitmproxy import master 4 | from mitmproxy import options 5 | from mitmproxy import proxy 6 | 7 | 8 | class TestAntiCache(mastertest.MasterTest): 9 | def test_simple(self): 10 | o = options.Options(anticache = True) 11 | m = master.Master(o, proxy.DummyServer()) 12 | sa = anticache.AntiCache() 13 | m.addons.add(sa) 14 | 15 | f = tutils.tflow(resp=True) 16 | m.request(f) 17 | 18 | f = tutils.tflow(resp=True) 19 | f.request.headers["if-modified-since"] = "test" 20 | f.request.headers["if-none-match"] = "test" 21 | m.request(f) 22 | assert "if-modified-since" not in f.request.headers 23 | assert "if-none-match" not in f.request.headers 24 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_anticomp.py: -------------------------------------------------------------------------------- 1 | from .. import tutils, mastertest 2 | from mitmproxy.addons import anticomp 3 | from mitmproxy import master 4 | from mitmproxy import options 5 | from mitmproxy import proxy 6 | 7 | 8 | class TestAntiComp(mastertest.MasterTest): 9 | def test_simple(self): 10 | o = options.Options(anticomp = True) 11 | m = master.Master(o, proxy.DummyServer()) 12 | sa = anticomp.AntiComp() 13 | m.addons.add(sa) 14 | 15 | f = tutils.tflow(resp=True) 16 | m.request(f) 17 | 18 | f = tutils.tflow(resp=True) 19 | 20 | f.request.headers["Accept-Encoding"] = "foobar" 21 | m.request(f) 22 | assert f.request.headers["Accept-Encoding"] == "identity" 23 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_clientplayback.py: -------------------------------------------------------------------------------- 1 | import mock 2 | 3 | from mitmproxy.addons import clientplayback 4 | from mitmproxy import options 5 | 6 | from .. import tutils, mastertest 7 | 8 | 9 | class TestClientPlayback: 10 | def test_playback(self): 11 | cp = clientplayback.ClientPlayback() 12 | cp.configure(options.Options(), []) 13 | assert cp.count() == 0 14 | f = tutils.tflow(resp=True) 15 | cp.load([f]) 16 | assert cp.count() == 1 17 | RP = "mitmproxy.proxy.protocol.http_replay.RequestReplayThread" 18 | with mock.patch(RP) as rp: 19 | assert not cp.current 20 | with mastertest.mockctx(): 21 | cp.tick() 22 | rp.assert_called() 23 | assert cp.current 24 | 25 | cp.keepserving = False 26 | cp.flows = None 27 | cp.current = None 28 | with mock.patch("mitmproxy.master.Master.shutdown") as sd: 29 | with mastertest.mockctx(): 30 | cp.tick() 31 | sd.assert_called() 32 | 33 | def test_configure(self): 34 | cp = clientplayback.ClientPlayback() 35 | cp.configure( 36 | options.Options(), [] 37 | ) 38 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_filestreamer.py: -------------------------------------------------------------------------------- 1 | from .. import tutils, mastertest 2 | 3 | import os.path 4 | 5 | from mitmproxy.addons import filestreamer 6 | from mitmproxy import master 7 | from mitmproxy import io 8 | from mitmproxy import options 9 | from mitmproxy import proxy 10 | 11 | 12 | class TestStream(mastertest.MasterTest): 13 | def test_stream(self): 14 | with tutils.tmpdir() as tdir: 15 | p = os.path.join(tdir, "foo") 16 | 17 | def r(): 18 | r = io.FlowReader(open(p, "rb")) 19 | return list(r.stream()) 20 | 21 | o = options.Options( 22 | outfile = (p, "wb") 23 | ) 24 | m = master.Master(o, proxy.DummyServer()) 25 | sa = filestreamer.FileStreamer() 26 | 27 | m.addons.add(sa) 28 | f = tutils.tflow(resp=True) 29 | m.request(f) 30 | m.response(f) 31 | m.addons.remove(sa) 32 | 33 | assert r()[0].response 34 | 35 | m.options.outfile = (p, "ab") 36 | 37 | m.addons.add(sa) 38 | f = tutils.tflow() 39 | m.request(f) 40 | m.addons.remove(sa) 41 | assert not r()[1].response 42 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_onboarding.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.addons import onboarding 2 | from .. import tservers 3 | 4 | 5 | class TestApp(tservers.HTTPProxyTest): 6 | def addons(self): 7 | return [onboarding.Onboarding()] 8 | 9 | def test_basic(self): 10 | assert self.app("/").status_code == 200 11 | 12 | def test_cert(self): 13 | for ext in ["pem", "p12"]: 14 | resp = self.app("/cert/%s" % ext) 15 | assert resp.status_code == 200 16 | assert resp.content 17 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_state.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import proxy 2 | from mitmproxy import master 3 | from mitmproxy.addons import state 4 | 5 | from .. import tutils 6 | 7 | 8 | class TestState: 9 | def test_duplicate_flow(self): 10 | s = state.State() 11 | fm = master.Master(None, proxy.DummyServer()) 12 | fm.addons.add(s) 13 | f = tutils.tflow(resp=True) 14 | fm.load_flow(f) 15 | assert s.flow_count() == 1 16 | 17 | f2 = s.duplicate_flow(f) 18 | assert f2.response 19 | assert s.flow_count() == 2 20 | assert s.index(f2) == 1 21 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_stickyauth.py: -------------------------------------------------------------------------------- 1 | from .. import tutils, mastertest 2 | from mitmproxy.addons import stickyauth 3 | from mitmproxy import master 4 | from mitmproxy import options 5 | from mitmproxy import proxy 6 | 7 | 8 | class TestStickyAuth(mastertest.MasterTest): 9 | def test_simple(self): 10 | o = options.Options(stickyauth = ".*") 11 | m = master.Master(o, proxy.DummyServer()) 12 | sa = stickyauth.StickyAuth() 13 | m.addons.add(sa) 14 | 15 | f = tutils.tflow(resp=True) 16 | f.request.headers["authorization"] = "foo" 17 | m.request(f) 18 | 19 | assert "address" in sa.hosts 20 | 21 | f = tutils.tflow(resp=True) 22 | m.request(f) 23 | assert f.request.headers["authorization"] == "foo" 24 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_streambodies.py: -------------------------------------------------------------------------------- 1 | from .. import tutils, mastertest 2 | from mitmproxy import master 3 | from mitmproxy import options 4 | from mitmproxy import proxy 5 | 6 | from mitmproxy.addons import streambodies 7 | 8 | 9 | class TestStreamBodies(mastertest.MasterTest): 10 | def test_simple(self): 11 | o = options.Options(stream_large_bodies = 10) 12 | m = master.Master(o, proxy.DummyServer()) 13 | sa = streambodies.StreamBodies() 14 | m.addons.add(sa) 15 | 16 | f = tutils.tflow() 17 | f.request.content = b"" 18 | f.request.headers["Content-Length"] = "1024" 19 | assert not f.request.stream 20 | m.requestheaders(f) 21 | assert f.request.stream 22 | 23 | f = tutils.tflow(resp=True) 24 | f.response.content = b"" 25 | f.response.headers["Content-Length"] = "1024" 26 | assert not f.response.stream 27 | m.responseheaders(f) 28 | assert f.response.stream 29 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_termlog.py: -------------------------------------------------------------------------------- 1 | from .. import mastertest 2 | import io 3 | 4 | from mitmproxy.addons import termlog 5 | from mitmproxy import log 6 | from mitmproxy.tools import dump 7 | 8 | 9 | class TestTermLog(mastertest.MasterTest): 10 | def test_simple(self): 11 | t = termlog.TermLog() 12 | sio = io.StringIO() 13 | t.configure(dump.Options(tfile = sio, verbosity = 2), set([])) 14 | t.log(log.LogEntry("one", "info")) 15 | assert "one" in sio.getvalue() 16 | t.log(log.LogEntry("two", "debug")) 17 | assert "two" not in sio.getvalue() 18 | -------------------------------------------------------------------------------- /test/mitmproxy/addons/test_wsgiapp.py: -------------------------------------------------------------------------------- 1 | import flask 2 | 3 | from .. import tservers 4 | from mitmproxy.addons import wsgiapp 5 | 6 | tapp = flask.Flask(__name__) 7 | 8 | 9 | @tapp.route("/") 10 | def hello(): 11 | return "testapp" 12 | 13 | 14 | @tapp.route("/error") 15 | def error(): 16 | raise ValueError("An exception...") 17 | 18 | 19 | def errapp(environ, start_response): 20 | raise ValueError("errapp") 21 | 22 | 23 | class TestApp(tservers.HTTPProxyTest): 24 | def addons(self): 25 | return [ 26 | wsgiapp.WSGIApp(tapp, "testapp", 80), 27 | wsgiapp.WSGIApp(errapp, "errapp", 80) 28 | ] 29 | 30 | def test_simple(self): 31 | p = self.pathoc() 32 | with p.connect(): 33 | ret = p.request("get:'http://testapp/'") 34 | assert ret.status_code == 200 35 | 36 | def test_app_err(self): 37 | p = self.pathoc() 38 | with p.connect(): 39 | ret = p.request("get:'http://errapp/'") 40 | assert ret.status_code == 500 41 | assert b"ValueError" in ret.content 42 | -------------------------------------------------------------------------------- /test/mitmproxy/completion/aaa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/completion/aaa -------------------------------------------------------------------------------- /test/mitmproxy/completion/aab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/completion/aab -------------------------------------------------------------------------------- /test/mitmproxy/completion/aac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/completion/aac -------------------------------------------------------------------------------- /test/mitmproxy/completion/bbb/Readme.md: -------------------------------------------------------------------------------- 1 | 2 | This empty directory has been added so that we can hit [this line](https://codecov.io/gh/mitmproxy/mitmproxy/src/ba13fda10d3065a0c8dfd95d55680675b3bf08c2/mitmproxy/console/pathedit.py#L43) while testing pathedit completion. 3 | -------------------------------------------------------------------------------- /test/mitmproxy/console/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/console/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/console/test_common.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.tools.console import common 2 | from .. import tutils 3 | 4 | 5 | @tutils.skip_appveyor 6 | def test_format_flow(): 7 | f = tutils.tflow(resp=True) 8 | assert common.format_flow(f, True) 9 | assert common.format_flow(f, True, hostheader=True) 10 | assert common.format_flow(f, True, extended=True) 11 | -------------------------------------------------------------------------------- /test/mitmproxy/console/test_help.py: -------------------------------------------------------------------------------- 1 | import mitmproxy.tools.console.help as help 2 | from .. import tutils 3 | 4 | 5 | @tutils.skip_appveyor 6 | class TestHelp: 7 | 8 | def test_helptext(self): 9 | h = help.HelpView(None) 10 | assert h.helptext() 11 | 12 | def test_keypress(self): 13 | h = help.HelpView([1, 2, 3]) 14 | assert not h.keypress((0, 0), "q") 15 | assert not h.keypress((0, 0), "?") 16 | assert h.keypress((0, 0), "o") == "o" 17 | -------------------------------------------------------------------------------- /test/mitmproxy/console/test_palettes.py: -------------------------------------------------------------------------------- 1 | import mitmproxy.tools.console.palettes as palettes 2 | from .. import tutils 3 | 4 | 5 | @tutils.skip_appveyor 6 | class TestPalette: 7 | 8 | def test_helptext(self): 9 | for i in palettes.palettes.values(): 10 | assert i.palette(False) 11 | for i in palettes.palettes.values(): 12 | assert i.palette(True) 13 | -------------------------------------------------------------------------------- /test/mitmproxy/data/1.css: -------------------------------------------------------------------------------- 1 | body,html{height:100%}body{font-family:'Open Sans',sans-serif;font-size:1.5em;padding-top:80px} 2 | -------------------------------------------------------------------------------- /test/mitmproxy/data/addonscripts/addon.py: -------------------------------------------------------------------------------- 1 | event_log = [] 2 | 3 | 4 | class Addon: 5 | @property 6 | def event_log(self): 7 | return event_log 8 | 9 | def start(self): 10 | event_log.append("addonstart") 11 | 12 | def configure(self, options, updated): 13 | event_log.append("addonconfigure") 14 | 15 | 16 | def configure(options, updated): 17 | event_log.append("addonconfigure") 18 | 19 | 20 | def start(): 21 | event_log.append("scriptstart") 22 | return Addon() 23 | -------------------------------------------------------------------------------- /test/mitmproxy/data/addonscripts/concurrent_decorator.py: -------------------------------------------------------------------------------- 1 | import time 2 | from mitmproxy.script import concurrent 3 | 4 | @concurrent 5 | def request(flow): 6 | time.sleep(0.1) 7 | -------------------------------------------------------------------------------- /test/mitmproxy/data/addonscripts/concurrent_decorator_err.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.script import concurrent 2 | 3 | 4 | @concurrent 5 | def start(): 6 | pass 7 | -------------------------------------------------------------------------------- /test/mitmproxy/data/addonscripts/duplicate_flow.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import ctx 2 | 3 | 4 | def request(flow): 5 | f = ctx.master.state.duplicate_flow(flow) 6 | ctx.master.replay_request(f, block=True) 7 | -------------------------------------------------------------------------------- /test/mitmproxy/data/addonscripts/error.py: -------------------------------------------------------------------------------- 1 | def mkerr(): 2 | raise ValueError("Error!") 3 | 4 | 5 | def request(flow): 6 | mkerr() 7 | -------------------------------------------------------------------------------- /test/mitmproxy/data/addonscripts/recorder.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import controller 2 | from mitmproxy import events 3 | from mitmproxy import ctx 4 | import sys 5 | 6 | 7 | class CallLogger: 8 | call_log = [] 9 | 10 | def __init__(self, name = "solo"): 11 | self.name = name 12 | 13 | def __getattr__(self, attr): 14 | if attr in events.Events: 15 | def prox(*args, **kwargs): 16 | lg = (self.name, attr, args, kwargs) 17 | if attr != "log": 18 | ctx.log.info(str(lg)) 19 | self.call_log.append(lg) 20 | ctx.log.debug("%s %s" % (self.name, attr)) 21 | return prox 22 | raise AttributeError 23 | 24 | 25 | def start(): 26 | return CallLogger(*sys.argv[1:]) 27 | -------------------------------------------------------------------------------- /test/mitmproxy/data/addonscripts/stream_modify.py: -------------------------------------------------------------------------------- 1 | def modify(chunks): 2 | for chunk in chunks: 3 | yield chunk.replace(b"foo", b"bar") 4 | 5 | 6 | def responseheaders(flow): 7 | flow.response.stream = modify 8 | -------------------------------------------------------------------------------- /test/mitmproxy/data/addonscripts/tcp_stream_modify.py: -------------------------------------------------------------------------------- 1 | def tcp_message(flow): 2 | message = flow.messages[-1] 3 | if not message.from_client: 4 | message.content = message.content.replace(b"foo", b"bar") 5 | -------------------------------------------------------------------------------- /test/mitmproxy/data/amf01: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/amf01 -------------------------------------------------------------------------------- /test/mitmproxy/data/amf02: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/amf02 -------------------------------------------------------------------------------- /test/mitmproxy/data/amf03: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/amf03 -------------------------------------------------------------------------------- /test/mitmproxy/data/clientcert/.gitignore: -------------------------------------------------------------------------------- 1 | client.crt 2 | client.key 3 | client.req 4 | -------------------------------------------------------------------------------- /test/mitmproxy/data/clientcert/client.cnf: -------------------------------------------------------------------------------- 1 | [ ssl_client ] 2 | basicConstraints = CA:FALSE 3 | nsCertType = client 4 | keyUsage = digitalSignature, keyEncipherment 5 | extendedKeyUsage = clientAuth 6 | -------------------------------------------------------------------------------- /test/mitmproxy/data/clientcert/make: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | openssl genrsa -out client.key 2048 4 | openssl req -key client.key -new -out client.req 5 | openssl x509 -req -days 365 -in client.req -signkey client.key -out client.crt -extfile client.cnf -extensions ssl_client 6 | openssl x509 -req -days 1000 -in client.req -CA ~/.mitmproxy/mitmproxy-ca.pem -CAkey ~/.mitmproxy/mitmproxy-ca.pem -set_serial 00001 -out client.crt -extensions ssl_client 7 | cat client.key client.crt > 127.0.0.1.pem 8 | openssl x509 -text -noout -in 127.0.0.1.pem 9 | -------------------------------------------------------------------------------- /test/mitmproxy/data/confdir/mitmproxy-ca-cert.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICnzCCAgigAwIBAgIGDKiSwuJOMA0GCSqGSIb3DQEBBQUAMCgxEjAQBgNVBAMT 3 | CW1pdG1wcm94eTESMBAGA1UEChMJbWl0bXByb3h5MB4XDTE0MDIwNzIzMjcwOFoX 4 | DTE2MDEyODIzMjcwOFowKDESMBAGA1UEAxMJbWl0bXByb3h5MRIwEAYDVQQKEwlt 5 | aXRtcHJveHkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKsZ+XnBvjCjAJ00 6 | 9M+v41AT91h7v7cF1UG0BpS3y4MOysN88btHM/IWRCllnmY+zx5LTMAEtbnqyOIk 7 | nkgJ0sU3CFWHRIfwkinssEtMM2mOAFXm0wqffECxwe1p5z84M7nOolzuuw4FtkaK 8 | G9/UqANdRVs6uOwz+CuyOSY7illTAgMBAAGjgdMwgdAwDwYDVR0TAQH/BAUwAwEB 9 | /zAUBglghkgBhvhCAQEBAf8EBAMCAgQwewYDVR0lAQH/BHEwbwYIKwYBBQUHAwEG 10 | CCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwgGCisGAQQBgjcCARUGCisGAQQB 11 | gjcCARYGCisGAQQBgjcKAwEGCisGAQQBgjcKAwMGCisGAQQBgjcKAwQGCWCGSAGG 12 | +EIEATALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFFKVDIF+w2Ns4KsJx6tJZpILqWwG 13 | MA0GCSqGSIb3DQEBBQUAA4GBABWYxoYFLgZh/ujz/0jrNsx0pvSNVTU1T669374z 14 | PhO+ScvzuxVbgI2NQv86aqih35pzakK/DyKaTck85QduDiSiLNw2Yb5UfJvO4C0d 15 | dPzQMIKNTInFFiLBjbvxx9cuDwAPyYOF247Xj9M6C2x6e/gq1L+GR75wT5288x9h 16 | rFTJ 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /test/mitmproxy/data/confdir/mitmproxy-ca-cert.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/confdir/mitmproxy-ca-cert.p12 -------------------------------------------------------------------------------- /test/mitmproxy/data/confdir/mitmproxy-ca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICnzCCAgigAwIBAgIGDKiSwuJOMA0GCSqGSIb3DQEBBQUAMCgxEjAQBgNVBAMT 3 | CW1pdG1wcm94eTESMBAGA1UEChMJbWl0bXByb3h5MB4XDTE0MDIwNzIzMjcwOFoX 4 | DTE2MDEyODIzMjcwOFowKDESMBAGA1UEAxMJbWl0bXByb3h5MRIwEAYDVQQKEwlt 5 | aXRtcHJveHkwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKsZ+XnBvjCjAJ00 6 | 9M+v41AT91h7v7cF1UG0BpS3y4MOysN88btHM/IWRCllnmY+zx5LTMAEtbnqyOIk 7 | nkgJ0sU3CFWHRIfwkinssEtMM2mOAFXm0wqffECxwe1p5z84M7nOolzuuw4FtkaK 8 | G9/UqANdRVs6uOwz+CuyOSY7illTAgMBAAGjgdMwgdAwDwYDVR0TAQH/BAUwAwEB 9 | /zAUBglghkgBhvhCAQEBAf8EBAMCAgQwewYDVR0lAQH/BHEwbwYIKwYBBQUHAwEG 10 | CCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwgGCisGAQQBgjcCARUGCisGAQQB 11 | gjcCARYGCisGAQQBgjcKAwEGCisGAQQBgjcKAwMGCisGAQQBgjcKAwQGCWCGSAGG 12 | +EIEATALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFFKVDIF+w2Ns4KsJx6tJZpILqWwG 13 | MA0GCSqGSIb3DQEBBQUAA4GBABWYxoYFLgZh/ujz/0jrNsx0pvSNVTU1T669374z 14 | PhO+ScvzuxVbgI2NQv86aqih35pzakK/DyKaTck85QduDiSiLNw2Yb5UfJvO4C0d 15 | dPzQMIKNTInFFiLBjbvxx9cuDwAPyYOF247Xj9M6C2x6e/gq1L+GR75wT5288x9h 16 | rFTJ 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /test/mitmproxy/data/dercert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/dercert -------------------------------------------------------------------------------- /test/mitmproxy/data/dumpfile-010: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/dumpfile-010 -------------------------------------------------------------------------------- /test/mitmproxy/data/htpasswd: -------------------------------------------------------------------------------- 1 | test:$apr1$/LkYxy3x$WI4.YbiJlu537jLGEW2eu1 2 | -------------------------------------------------------------------------------- /test/mitmproxy/data/htpasswd.invalid: -------------------------------------------------------------------------------- 1 | foo 2 | -------------------------------------------------------------------------------- /test/mitmproxy/data/image-err1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/image-err1.jpg -------------------------------------------------------------------------------- /test/mitmproxy/data/image.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/image.gif -------------------------------------------------------------------------------- /test/mitmproxy/data/image.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/image.ico -------------------------------------------------------------------------------- /test/mitmproxy/data/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/image.jpg -------------------------------------------------------------------------------- /test/mitmproxy/data/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/data/image.png -------------------------------------------------------------------------------- /test/mitmproxy/data/no_common_name.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIBOQIBAAJBAKVJ43C+8SjOvN9/pP/8HwzmHGQmRvdK/R6KlWdr7He6iiXDQNfH 3 | RAp+gqX0hBRT80eRjGhSmTTBLCWiXVny4UUCAwEAAQJAUQ8nZ0d85VJd9g2XUaLH 4 | Z4ACNGtBKk2wTKYSFyIqWZxsF5qhh7HGshJIAP6tYiX8ZW+mMSfme+zsJzWe8ChL 5 | gQIhAM8QpAgUHnNteZvkv0XqceX1GILEWifMt+hO9yTp4dY5AiEAzFnKr77CKCri 6 | /DPig4R/5q4KMpMx9EqJufHdGNmIA20CICMARxnufK86RCIr6oEg/hvG8Fu6YRr1 7 | Kekk3/XnavtRAiBVLVQ7vwKE5aNpRmMzOKZrS736aLpYvjz8IaFr+zgjXQIgdad5 8 | QZoTD49NTyMEgyZp70gTXcXQLrX2PgQKL4uNmoU= 9 | -----END RSA PRIVATE KEY----- 10 | -----BEGIN CERTIFICATE----- 11 | MIIBgTCCASugAwIBAgIJAKlcXsPLQAQuMA0GCSqGSIb3DQEBBQUAMA0xCzAJBgNV 12 | BAYTAkFVMB4XDTEzMTIxMjAxMzA1NVoXDTE0MDExMTAxMzA1NVowDTELMAkGA1UE 13 | BhMCQVUwXDANBgkqhkiG9w0BAQEFAANLADBIAkEApUnjcL7xKM6833+k//wfDOYc 14 | ZCZG90r9HoqVZ2vsd7qKJcNA18dECn6CpfSEFFPzR5GMaFKZNMEsJaJdWfLhRQID 15 | AQABo24wbDAdBgNVHQ4EFgQUJm8BXcVRsROy0PVt5stkB3eVnEgwPQYDVR0jBDYw 16 | NIAUJm8BXcVRsROy0PVt5stkB3eVnEihEaQPMA0xCzAJBgNVBAYTAkFVggkAqVxe 17 | w8tABC4wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAANBAHHxcBEpWrIqtLVH 18 | m6Yn1hgqrAbfMj9IK6zY9C5Cbad/DfUj3AZMb5u758WJK0x9brmckgqdrQsuf9He 19 | Ef51/SU= 20 | -----END CERTIFICATE----- 21 | -------------------------------------------------------------------------------- /test/mitmproxy/data/pf01: -------------------------------------------------------------------------------- 1 | No ALTQ support in kernel 2 | ALTQ related functions disabled 3 | ALL tcp 127.0.0.1:8080 <- 5.5.5.6:80 <- 192.168.1.111:40001 FIN_WAIT_2:FIN_WAIT_2 4 | ALL tcp 127.0.0.1:8080 <- 5.5.5.5:80 <- 192.168.1.111:40000 ESTABLISHED:ESTABLISHED 5 | -------------------------------------------------------------------------------- /test/mitmproxy/data/pf02: -------------------------------------------------------------------------------- 1 | No ALTQ support in kernel 2 | ALTQ related functions disabled 3 | all tcp 127.0.0.1:8080 (5.5.5.6:80) <- 192.168.1.111:40001 FIN_WAIT_2:FIN_WAIT_2 4 | all tcp 127.0.0.1:8080 (5.5.5.5:80) <- 192.168.1.111:40000 ESTABLISHED:ESTABLISHED 5 | -------------------------------------------------------------------------------- /test/mitmproxy/data/protobuf01: -------------------------------------------------------------------------------- 1 | 2 | $3bbc333c-e61c-433b-819a-0b9a8cc103b8 -------------------------------------------------------------------------------- /test/mitmproxy/data/replace: -------------------------------------------------------------------------------- 1 | replacecontents 2 | -------------------------------------------------------------------------------- /test/mitmproxy/data/scripts/all.py: -------------------------------------------------------------------------------- 1 | import mitmproxy 2 | record = [] 3 | 4 | 5 | def clientconnect(cc): 6 | mitmproxy.ctx.log("XCLIENTCONNECT") 7 | record.append("clientconnect") 8 | 9 | 10 | def serverconnect(cc): 11 | mitmproxy.ctx.log("XSERVERCONNECT") 12 | record.append("serverconnect") 13 | 14 | 15 | def request(f): 16 | mitmproxy.ctx.log("XREQUEST") 17 | record.append("request") 18 | 19 | 20 | def response(f): 21 | mitmproxy.ctx.log("XRESPONSE") 22 | record.append("response") 23 | 24 | 25 | def responseheaders(f): 26 | mitmproxy.ctx.log("XRESPONSEHEADERS") 27 | record.append("responseheaders") 28 | 29 | 30 | def clientdisconnect(cc): 31 | mitmproxy.ctx.log("XCLIENTDISCONNECT") 32 | record.append("clientdisconnect") 33 | 34 | 35 | def error(cc): 36 | mitmproxy.ctx.log("XERROR") 37 | record.append("error") 38 | -------------------------------------------------------------------------------- /test/mitmproxy/data/servercert/9da13359.0: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJAPAfPQGCV/Z4MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTAxMTY0ODAxWhcNMTgwODIxMTY0ODAxWjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEArp8LD34JhKCwcQbwIYQMg4+eCgLVN8fwB7+/qOfJbArPs0djFBN+F7c6 8 | HGvMr24BKUk5u8pn4dPtNurm/vPC8ovNGmcXz62BQJpcMX2veVdRsF7yNwhNacNJ 9 | Arq+70zNMwYBznx0XUxMF6j6nVFf3AW6SU04ylT4Mp3SY/BUUDAdfl1eRo0mPLNS 10 | 8rpsN+8YBw1Q7SCuBRVqpOgVIsL88svgQUSOlzvMZPBpG/cmB3BNKNrltwb5iFEI 11 | 1jAV7uSj5IcIuNO/246kfsDVPTFMJIzav/CUoidd5UNw+SoFDlzh8sA7L1Bm7D1/ 12 | 3KHYSKswGsSR3kynAl10w/SJKDtn8wIDAQABo1AwTjAdBgNVHQ4EFgQUgOcrtxBX 13 | LxbpnOT65d+vpfyWUkgwHwYDVR0jBBgwFoAUgOcrtxBXLxbpnOT65d+vpfyWUkgw 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEE9bFmUCA+6cvESKPoi2 15 | TGSpV652d0xd2U66LpEXeiWRJFLz8YGgoJCx3QFGBscJDXxrLxrBBBV/tCpEqypo 16 | pYIqsawH7M66jpOr83Us3M8JC2eFBZJocMpXxdytWqHik5VKZNx6VQFT8bS7+yVC 17 | VoUKePhlgcg+pmo41qjqieBNKRMh/1tXS77DI1lgO5wZLVrLXcdqWuDpmaQOKJeq 18 | G/nxytCW/YJA7bFn/8Gjy8DYypJSeeaKu7o3P3+ONJHdIMHb+MdcheDBS9AOFSeo 19 | xI0D5EbO9F873O77l7nbD7B0X34HFN0nGczC4poexIpbDFG3hAPekwZ5KC6VwJLc 20 | 1Q== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/locust_get.py: -------------------------------------------------------------------------------- 1 | from locust import HttpLocust, TaskSet, task 2 | 3 | class UserBehavior(TaskSet): 4 | def on_start(self): 5 | ''' on_start is called when a Locust start before any task is scheduled ''' 6 | self.path() 7 | 8 | @task() 9 | def path(self): 10 | url = self.locust.host + '/path' 11 | 12 | headers = { 13 | 'header': 'qvalue', 14 | 'content-length': '7', 15 | } 16 | 17 | params = { 18 | 'a': ['foo', 'bar'], 19 | 'b': 'baz', 20 | } 21 | 22 | self.response = self.client.request( 23 | method='GET', 24 | url=url, 25 | headers=headers, 26 | params=params, 27 | ) 28 | 29 | ### Additional tasks can go here ### 30 | 31 | 32 | class WebsiteUser(HttpLocust): 33 | task_set = UserBehavior 34 | min_wait = 1000 35 | max_wait = 3000 36 | -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/locust_patch.py: -------------------------------------------------------------------------------- 1 | from locust import HttpLocust, TaskSet, task 2 | 3 | class UserBehavior(TaskSet): 4 | def on_start(self): 5 | ''' on_start is called when a Locust start before any task is scheduled ''' 6 | self.path() 7 | 8 | @task() 9 | def path(self): 10 | url = self.locust.host + '/path' 11 | 12 | headers = { 13 | 'header': 'qvalue', 14 | 'content-length': '7', 15 | } 16 | 17 | params = { 18 | 'query': 'param', 19 | } 20 | 21 | data = '''content''' 22 | 23 | self.response = self.client.request( 24 | method='PATCH', 25 | url=url, 26 | headers=headers, 27 | params=params, 28 | data=data, 29 | ) 30 | 31 | ### Additional tasks can go here ### 32 | 33 | 34 | class WebsiteUser(HttpLocust): 35 | task_set = UserBehavior 36 | min_wait = 1000 37 | max_wait = 3000 38 | -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/locust_post.py: -------------------------------------------------------------------------------- 1 | from locust import HttpLocust, TaskSet, task 2 | 3 | class UserBehavior(TaskSet): 4 | def on_start(self): 5 | ''' on_start is called when a Locust start before any task is scheduled ''' 6 | self.path() 7 | 8 | @task() 9 | def path(self): 10 | url = self.locust.host + '/path' 11 | 12 | data = '''content''' 13 | 14 | self.response = self.client.request( 15 | method='POST', 16 | url=url, 17 | data=data, 18 | ) 19 | 20 | ### Additional tasks can go here ### 21 | 22 | 23 | class WebsiteUser(HttpLocust): 24 | task_set = UserBehavior 25 | min_wait = 1000 26 | max_wait = 3000 27 | -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/locust_task_get.py: -------------------------------------------------------------------------------- 1 | @task() 2 | def path(self): 3 | url = self.locust.host + '/path' 4 | 5 | headers = { 6 | 'header': 'qvalue', 7 | 'content-length': '7', 8 | } 9 | 10 | params = { 11 | 'a': ['foo', 'bar'], 12 | 'b': 'baz', 13 | } 14 | 15 | self.response = self.client.request( 16 | method='GET', 17 | url=url, 18 | headers=headers, 19 | params=params, 20 | ) 21 | -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/locust_task_patch.py: -------------------------------------------------------------------------------- 1 | @task() 2 | def path(self): 3 | url = self.locust.host + '/path' 4 | 5 | headers = { 6 | 'header': 'qvalue', 7 | 'content-length': '7', 8 | } 9 | 10 | params = { 11 | 'query': 'param', 12 | } 13 | 14 | data = '''content''' 15 | 16 | self.response = self.client.request( 17 | method='PATCH', 18 | url=url, 19 | headers=headers, 20 | params=params, 21 | data=data, 22 | ) 23 | -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/locust_task_post.py: -------------------------------------------------------------------------------- 1 | @task() 2 | def path(self): 3 | url = self.locust.host + '/path' 4 | 5 | data = '''content''' 6 | 7 | self.response = self.client.request( 8 | method='POST', 9 | url=url, 10 | data=data, 11 | ) 12 | -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/python_get.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | response = requests.get( 4 | 'http://address:22/path', 5 | params=[('a', 'foo'), ('a', 'bar'), ('b', 'baz')], 6 | headers={'header': 'qvalue'} 7 | ) 8 | 9 | print(response.text) -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/python_patch.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | response = requests.patch( 4 | 'http://address:22/path', 5 | params=[('query', 'param')], 6 | headers={'header': 'qvalue'}, 7 | data=b'content' 8 | ) 9 | 10 | print(response.text) -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/python_post.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | response = requests.post( 4 | 'http://address:22/path', 5 | data=b'content' 6 | ) 7 | 8 | print(response.text) 9 | -------------------------------------------------------------------------------- /test/mitmproxy/data/test_flow_export/python_post_json.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | response = requests.post( 4 | 'http://address:22/path', 5 | headers={'content-type': 'application/json'}, 6 | json={'email': 'example@example.com', 'name': 'example'} 7 | ) 8 | 9 | print(response.text) -------------------------------------------------------------------------------- /test/mitmproxy/fuzzing/.env: -------------------------------------------------------------------------------- 1 | MITMDUMP=mitmdump 2 | PATHOD=pathod 3 | PATHOC=pathoc 4 | FUZZ_SETTINGS="-remTt 1 -n 0" 5 | -------------------------------------------------------------------------------- /test/mitmproxy/fuzzing/README: -------------------------------------------------------------------------------- 1 | 2 | A fuzzing architecture for mitmproxy 3 | ==================================== 4 | 5 | Quick start: 6 | 7 | honcho -f ./straight_stream start 8 | 9 | 10 | Notes: 11 | 12 | - Processes are managed using honcho (pip install honcho) 13 | - Paths and common settings live in .env 14 | 15 | -------------------------------------------------------------------------------- /test/mitmproxy/fuzzing/client_patterns: -------------------------------------------------------------------------------- 1 | get:'http://localhost:9999/p/200':ir,"\n" 2 | get:'http://localhost:9999/p/200':ir,"\0" 3 | get:'http://localhost:9999/p/200':ir,@5 4 | get:'http://localhost:9999/p/200':dr 5 | -------------------------------------------------------------------------------- /test/mitmproxy/fuzzing/go_proxy: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Assuming: 3 | # mitmproxy/mitmdump is running on port 8080 in straight proxy mode. 4 | # pathod is running on port 9999 5 | 6 | BASE="../../../" 7 | BASE_HTTP=$BASE"/pathod/pathoc -Tt 1 -e -I 200,400,405,502 -p 8080 localhost " 8 | BASE_HTTPS=$BASE"/pathod/pathoc -sc localhost:9999 -Tt 1 -eo -I 200,400,404,405,502,800 -p 8080 localhost " 9 | 10 | #$BASE_HTTP -n 10000 "get:'http://localhost:9999':ir,@1" 11 | #$BASE_HTTP -n 100 "get:'http://localhost:9999':dr" 12 | #$BASE_HTTP -n 10000 "get:'http://localhost:9999/p/200':ir,@300" 13 | 14 | #$BASE_HTTP -n 10000 "get:'http://localhost:9999/p/200:ir,@1'" 15 | #$BASE_HTTP -n 100 "get:'http://localhost:9999/p/200:dr'" 16 | #$BASE_HTTP -n 10000 "get:'http://localhost:9999/p/200:ir,@100'" 17 | 18 | 19 | # Assuming: 20 | # mitmproxy/mitmdump is running on port 8080 in straight proxy mode. 21 | # pathod with SSL enabled is running on port 9999 22 | 23 | #$BASE_HTTPS -en 10000 "get:'/p/200:b@100:ir,@1'" 24 | #$BASE_HTTPS -en 10000 "get:'/p/200:ir,@1'" 25 | 26 | #$BASE_HTTPS -n 100 "get:'/p/200:dr'" 27 | #$BASE_HTTPS -n 10000 "get:'/p/200:ir,@3000'" 28 | #$BASE_HTTPS -n 10000 "get:'/p/200:ir,\"\\n\"'" 29 | 30 | -------------------------------------------------------------------------------- /test/mitmproxy/fuzzing/reverse_patterns: -------------------------------------------------------------------------------- 1 | get:'/p/200':b@10:ir,"\n" 2 | get:'/p/200':b@10:ir,"\r\n" 3 | get:'/p/200':b@10:ir,"\0" 4 | get:'/p/200':b@10:ir,@5 5 | get:'/p/200':b@10:dr 6 | 7 | get:'/p/200:b@10:ir,@1' 8 | get:'/p/200:b@10:dr' 9 | get:'/p/200:b@10:ir,@100' 10 | -------------------------------------------------------------------------------- /test/mitmproxy/fuzzing/straight_stream: -------------------------------------------------------------------------------- 1 | 2 | mitmdump: $MITMDUMP 3 | pathod: $PATHOD 4 | pathoc: sleep 2 && $PATHOC $FUZZ_SETTINGS localhost:8080 ./straight_stream_patterns 5 | #pathoc: sleep 2 && $PATHOC localhost:8080 /tmp/err 6 | 7 | -------------------------------------------------------------------------------- /test/mitmproxy/fuzzing/straight_stream_patterns: -------------------------------------------------------------------------------- 1 | get:'http://localhost:9999/p/':s'200:b"foo"':ir,'\n' 2 | get:'http://localhost:9999/p/':s'200:b"foo"':ir,'a' 3 | get:'http://localhost:9999/p/':s'200:b"foo"':ir,'9' 4 | get:'http://localhost:9999/p/':s'200:b"foo"':ir,':' 5 | get:'http://localhost:9999/p/':s'200:b"foo"':ir,'"' 6 | get:'http://localhost:9999/p/':s'200:b"foo"':ir,'-' 7 | 8 | get:'http://localhost:9999/p/':s'200:b"foo":ir,"\n"' 9 | get:'http://localhost:9999/p/':s'200:b"foo":ir,"a"' 10 | get:'http://localhost:9999/p/':s'200:b"foo":ir,"9"' 11 | get:'http://localhost:9999/p/':s'200:b"foo":ir,":"' 12 | get:'http://localhost:9999/p/':s'200:b"foo":ir,"-"' 13 | get:'http://localhost:9999/p/':s'200:b"foo":dr' 14 | 15 | get:'http://localhost:9999/p/':s'200:b"foo"':ir,@2 16 | get:'http://localhost:9999/p/':s'200:b"foo":ir,@2' 17 | -------------------------------------------------------------------------------- /test/mitmproxy/fuzzing/straight_stream_ssl: -------------------------------------------------------------------------------- 1 | 2 | mitmdump: $MITMDUMP -q --stream 1 3 | pathod: $PATHOD 4 | pathoc: sleep 2 && $PATHOC $FUZZ_SETTINGS localhost:8080 ./straight_stream_patterns 5 | #pathoc: sleep 2 && $PATHOC localhost:8080 /tmp/err 6 | 7 | -------------------------------------------------------------------------------- /test/mitmproxy/mock_urwid.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import mock 4 | if os.name == "nt": 5 | m = mock.Mock() 6 | m.__version__ = "1.1.1" 7 | m.Widget = mock.Mock 8 | m.WidgetWrap = mock.Mock 9 | sys.modules['urwid'] = m 10 | sys.modules['urwid.util'] = mock.Mock() 11 | -------------------------------------------------------------------------------- /test/mitmproxy/net/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/net/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/net/data/clientcert/.gitignore: -------------------------------------------------------------------------------- 1 | client.crt 2 | client.key 3 | client.req 4 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/clientcert/client.cnf: -------------------------------------------------------------------------------- 1 | [ ssl_client ] 2 | basicConstraints = CA:FALSE 3 | nsCertType = client 4 | keyUsage = digitalSignature, keyEncipherment 5 | extendedKeyUsage = clientAuth 6 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/clientcert/make: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | openssl genrsa -out client.key 2048 4 | openssl req -key client.key -new -out client.req 5 | openssl x509 -req -days 365 -in client.req -signkey client.key -out client.crt -extfile client.cnf -extensions ssl_client 6 | openssl x509 -req -days 1000 -in client.req -CA ~/.mitmproxy/mitmproxy-ca.pem -CAkey ~/.mitmproxy/mitmproxy-ca.pem -set_serial 00001 -out client.crt -extensions ssl_client 7 | cat client.key client.crt > client.pem 8 | openssl x509 -text -noout -in client.pem 9 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/dercert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/net/data/dercert -------------------------------------------------------------------------------- /test/mitmproxy/net/data/dhparam.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN DH PARAMETERS----- 2 | MIICCAKCAgEAyT6LzpwVFS3gryIo29J5icvgxCnCebcdSe/NHMkD8dKJf8suFCg3 3 | O2+dguLakSVif/t6dhImxInJk230HmfC8q93hdcg/j8rLGJYDKu3ik6H//BAHKIv 4 | j5O9yjU3rXCfmVJQic2Nne39sg3CreAepEts2TvYHhVv3TEAzEqCtOuTjgDv0ntJ 5 | Gwpj+BJBRQGG9NvprX1YGJ7WOFBP/hWU7d6tgvE6Xa7T/u9QIKpYHMIkcN/l3ZFB 6 | chZEqVlyrcngtSXCROTPcDOQ6Q8QzhaBJS+Z6rcsd7X+haiQqvoFcmaJ08Ks6LQC 7 | ZIL2EtYJw8V8z7C0igVEBIADZBI6OTbuuhDwRw//zU1uq52Oc48CIZlGxTYG/Evq 8 | o9EWAXUYVzWkDSTeBH1r4z/qLPE2cnhtMxbFxuvK53jGB0emy2y1Ei6IhKshJ5qX 9 | IB/aE7SSHyQ3MDHHkCmQJCsOd4Mo26YX61NZ+n501XjqpCBQ2+DfZCBh8Va2wDyv 10 | A2Ryg9SUz8j0AXViRNMJgJrr446yro/FuJZwnQcO3WQnXeqSBnURqKjmqkeFP+d8 11 | 6mk2tqJaY507lRNqtGlLnj7f5RNoBFJDCLBNurVgfvq9TCVWKDIFD4vZRjCrnl6I 12 | rD693XKIHUCWOjMh1if6omGXKHH40QuME2gNa50+YPn1iYDl88uDbbMCAQI= 13 | -----END DH PARAMETERS----- 14 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/htpasswd: -------------------------------------------------------------------------------- 1 | test:$apr1$/LkYxy3x$WI4.YbiJlu537jLGEW2eu1 2 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/server.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICOzCCAaQCCQDC7f5GsEpo9jANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJO 3 | WjEOMAwGA1UECBMFT3RhZ28xEDAOBgNVBAcTB0R1bmVkaW4xDzANBgNVBAoTBm5l 4 | dGxpYjEPMA0GA1UECxMGbmV0bGliMQ8wDQYDVQQDEwZuZXRsaWIwHhcNMTIwNjI0 5 | MjI0MTU0WhcNMjIwNjIyMjI0MTU0WjBiMQswCQYDVQQGEwJOWjEOMAwGA1UECBMF 6 | T3RhZ28xEDAOBgNVBAcTB0R1bmVkaW4xDzANBgNVBAoTBm5ldGxpYjEPMA0GA1UE 7 | CxMGbmV0bGliMQ8wDQYDVQQDEwZuZXRsaWIwgZ8wDQYJKoZIhvcNAQEBBQADgY0A 8 | MIGJAoGBALJSVEl9y3QUSYuXTH0UjBOPQgS0nHmNWej9hjqnA0KWvEnGY+c6yQeP 9 | /rmwswlKw1iVV5o8kRK9Wej88YWQl/hl/xruyeJgGic0+yqY/FcueZxRudwBcWu2 10 | 7+46aEftwLLRF0GwHZxX/HwWME+TcCXGpXGSG2qs921M4iVeBn5hAgMBAAEwDQYJ 11 | KoZIhvcNAQEFBQADgYEAODZCihEv2yr8zmmQZDrfqg2ChxAoOXWF5+W2F/0LAUBf 12 | 2bHP+K4XE6BJWmadX1xKngj7SWrhmmTDp1gBAvXURoDaScOkB1iOCOHoIyalscTR 13 | 0FvSHKqFF8fgSlfqS6eYaSbXU3zQolvwP+URzIVnGDqgQCWPtjMqLD3Kd5tuwos= 14 | -----END CERTIFICATE----- 15 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXAIBAAKBgQCyUlRJfct0FEmLl0x9FIwTj0IEtJx5jVno/YY6pwNClrxJxmPn 3 | OskHj/65sLMJSsNYlVeaPJESvVno/PGFkJf4Zf8a7sniYBonNPsqmPxXLnmcUbnc 4 | AXFrtu/uOmhH7cCy0RdBsB2cV/x8FjBPk3AlxqVxkhtqrPdtTOIlXgZ+YQIDAQAB 5 | AoGAQEpGcSiVTYhy64zk2sOprPOdTa0ALSK1I7cjycmk90D5KXAJXLho+f0ETVZT 6 | dioqO6m8J7NmamcyHznyqcDzyNRqD2hEBDGVRJWmpOjIER/JwWLNNbpeVjsMHV8I 7 | 40P5rZMOhBPYlwECSC5NtMwaN472fyGNNze8u37IZKiER/ECQQDe1iY5AG3CgkP3 8 | tEZB3Vtzcn4PoOr3Utyn1YER34lPqAmeAsWUhmAVEfR3N1HDe1VFD9s2BidhBn1a 9 | /Bgqxz4DAkEAzNw0m+uO0WkD7aEYRBW7SbXCX+3xsbVToIWC1jXFG+XDzSWn++c1 10 | DMXEElzEJxPDA+FzQUvRTml4P92bTAbGywJAS9H7wWtm7Ubbj33UZfbGdhqfz/uF 11 | 109naufXedhgZS0c0JnK1oV+Tc0FLEczV9swIUaK5O/lGDtYDcw3AN84NwJBAIw5 12 | /1jrOOtm8uVp6+5O4dBmthJsEZEPCZtLSG/Qhoe+EvUN3Zq0fL+tb7USAsKs6ERz 13 | wizj9PWzhDhTPMYhrVkCQGIponZHx6VqiFyLgYUH9+gDTjBhYyI+6yMTYzcRweyL 14 | 9Suc2NkS3X2Lp+wCjvVZdwGtStp6Vo8z02b3giIsAIY= 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/verificationcerts/9da13359.0: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJAPAfPQGCV/Z4MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTAxMTY0ODAxWhcNMTgwODIxMTY0ODAxWjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEArp8LD34JhKCwcQbwIYQMg4+eCgLVN8fwB7+/qOfJbArPs0djFBN+F7c6 8 | HGvMr24BKUk5u8pn4dPtNurm/vPC8ovNGmcXz62BQJpcMX2veVdRsF7yNwhNacNJ 9 | Arq+70zNMwYBznx0XUxMF6j6nVFf3AW6SU04ylT4Mp3SY/BUUDAdfl1eRo0mPLNS 10 | 8rpsN+8YBw1Q7SCuBRVqpOgVIsL88svgQUSOlzvMZPBpG/cmB3BNKNrltwb5iFEI 11 | 1jAV7uSj5IcIuNO/246kfsDVPTFMJIzav/CUoidd5UNw+SoFDlzh8sA7L1Bm7D1/ 12 | 3KHYSKswGsSR3kynAl10w/SJKDtn8wIDAQABo1AwTjAdBgNVHQ4EFgQUgOcrtxBX 13 | LxbpnOT65d+vpfyWUkgwHwYDVR0jBBgwFoAUgOcrtxBXLxbpnOT65d+vpfyWUkgw 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEE9bFmUCA+6cvESKPoi2 15 | TGSpV652d0xd2U66LpEXeiWRJFLz8YGgoJCx3QFGBscJDXxrLxrBBBV/tCpEqypo 16 | pYIqsawH7M66jpOr83Us3M8JC2eFBZJocMpXxdytWqHik5VKZNx6VQFT8bS7+yVC 17 | VoUKePhlgcg+pmo41qjqieBNKRMh/1tXS77DI1lgO5wZLVrLXcdqWuDpmaQOKJeq 18 | G/nxytCW/YJA7bFn/8Gjy8DYypJSeeaKu7o3P3+ONJHdIMHb+MdcheDBS9AOFSeo 19 | xI0D5EbO9F873O77l7nbD7B0X34HFN0nGczC4poexIpbDFG3hAPekwZ5KC6VwJLc 20 | 1Q== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/verificationcerts/self-signed.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDEzCCAfugAwIBAgIJAJ945xt1FRsfMA0GCSqGSIb3DQEBCwUAMCAxHjAcBgNV 3 | BAMMFWV4YW1wbGUubWl0bXByb3h5Lm9yZzAeFw0xNTExMDExNjQ4MDJaFw0xODA4 4 | MjExNjQ4MDJaMCAxHjAcBgNVBAMMFWV4YW1wbGUubWl0bXByb3h5Lm9yZzCCASIw 5 | DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALFxyzPfjgIghOMMnJlW80yB84xC 6 | nJtko3tuyOdozgTCyha2W+NdIKPNZJtWrzN4P0B5PlozCDwfcSYffLs0WZs8LRWv 7 | BfZX8+oX+14qQjKFsiqgO65cTLP3qlPySYPJQQ37vOP1Y5Yf8nQq2mwQdC18hLtT 8 | QOANG6OFoSplpBLsYF+QeoMgqCTa6hrl/5GLmQoDRTjXkv3Sj379AUDMybuBqccm 9 | q5EIqCrE4+xJ8JywJclAVn2YP14baiFrrYCsYYg4sS1Od6xFj+xtpLe7My3AYjB9 10 | /aeHd8vDiob0cqOW1TFwhqgJKuErfFyg8lZ2hJmStJKyfofWuY/gl/vnvX0CAwEA 11 | AaNQME4wHQYDVR0OBBYEFB8d32zK8eqZIoKw4jXzYzhw4amPMB8GA1UdIwQYMBaA 12 | FB8d32zK8eqZIoKw4jXzYzhw4amPMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL 13 | BQADggEBAJmo2oKv1OEjZ0Q4yELO6BAnHAkmBKpW+zmLyQa8idxtLVkI9uXk3iqY 14 | GWugkmcUZCTVFRWv/QXQQSex+00IY3x2rdHbtuZwcyKiz2u8WEmfW1rOIwBaFJ1i 15 | v7+SA2aZs6vepN2sE56X54c/YbwQooaKZtOb+djWXYMJrc/Ezj0J7oQIJTptYV8v 16 | /3216yCHRp/KCL7yTLtiw25xKuXNu/gkcd8wZOY9rS2qMUD897MJF0MvgJoauRBd 17 | d4XEYCNKkrIRmfqrkiRQfAZpvpoutH6NCk7KuQYcI0BlOHlsnHHcs/w72EEqHwFq 18 | x6476tW/t8GJDZVD74+pNBcLifXxArE= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/verificationcerts/trusted-leaf.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC4TCCAckCCQCj6D9oVylb8jANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJB 3 | VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0 4 | cyBQdHkgTHRkMB4XDTE1MTEwMTE2NDgwMloXDTE4MDgyMTE2NDgwMlowIDEeMBwG 5 | A1UEAwwVZXhhbXBsZS5taXRtcHJveHkub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOC 6 | AQ8AMIIBCgKCAQEAy/L5JYHS7QFhSIsjmd6bJTgs2rdqEn6tsmPBVZKZ7SqCAVjW 7 | hPpEu7Q23akmU6Zm9Fp/vENc3jzxQLlEKhrv7eWmFYSOrCYtbJOz3RQorlwjjfdY 8 | LlNQh1wYUXQX3PN3r3dyYtt5vTtXKc8+aP4M4vX7qlbW+4j4LrQfmPjS0XOdYpu3 9 | wh+i1ZMIhZye3hpCjwnpjTf7/ff45ZFxtkoi1uzEC/+swr1RSvamY8Foe12Re17Z 10 | 5ij8ZB0NIdoSk1tDkY3sJ8iNi35+qartl0UYeG9IUXRwDRrPsEKpF4RxY1+X2bdZ 11 | r6PKb/E4CA5JlMvS5SVmrvxjCVqTQBmTjXfxqwIDAQABMA0GCSqGSIb3DQEBCwUA 12 | A4IBAQBmpSZJrTDvzSlo6P7P7x1LoETzHyVjwgPeqGYw6ndGXeJMN9rhhsFvRsiB 13 | I/aHh58MIlSjti7paikDAoFHB3dBvFHR+JUa/ailWEbcZReWRSE3lV6wFiN3G3lU 14 | OyofR7MKnPW7bv8hSqOLqP1mbupXuQFB5M6vPLRwg5VgiCHI/XBiTvzMamzvNAR3 15 | UHHZtsJkRqzogYm6K9YJaga7jteSx2nNo+ujLwrxeXsLChTyFMJGnVkp5IyKeNfc 16 | qwlzNncb3y+4KnUdNkPEtuydgAxAfuyXufiFBYRcUWbQ5/9ycgF7131ySaj9f/Y2 17 | kMsv2jg+soKvwwVYCABsk1KSHtfz 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/verificationcerts/trusted-root.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDXTCCAkWgAwIBAgIJAPAfPQGCV/Z4MA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTUxMTAxMTY0ODAxWhcNMTgwODIxMTY0ODAxWjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEArp8LD34JhKCwcQbwIYQMg4+eCgLVN8fwB7+/qOfJbArPs0djFBN+F7c6 8 | HGvMr24BKUk5u8pn4dPtNurm/vPC8ovNGmcXz62BQJpcMX2veVdRsF7yNwhNacNJ 9 | Arq+70zNMwYBznx0XUxMF6j6nVFf3AW6SU04ylT4Mp3SY/BUUDAdfl1eRo0mPLNS 10 | 8rpsN+8YBw1Q7SCuBRVqpOgVIsL88svgQUSOlzvMZPBpG/cmB3BNKNrltwb5iFEI 11 | 1jAV7uSj5IcIuNO/246kfsDVPTFMJIzav/CUoidd5UNw+SoFDlzh8sA7L1Bm7D1/ 12 | 3KHYSKswGsSR3kynAl10w/SJKDtn8wIDAQABo1AwTjAdBgNVHQ4EFgQUgOcrtxBX 13 | LxbpnOT65d+vpfyWUkgwHwYDVR0jBBgwFoAUgOcrtxBXLxbpnOT65d+vpfyWUkgw 14 | DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAEE9bFmUCA+6cvESKPoi2 15 | TGSpV652d0xd2U66LpEXeiWRJFLz8YGgoJCx3QFGBscJDXxrLxrBBBV/tCpEqypo 16 | pYIqsawH7M66jpOr83Us3M8JC2eFBZJocMpXxdytWqHik5VKZNx6VQFT8bS7+yVC 17 | VoUKePhlgcg+pmo41qjqieBNKRMh/1tXS77DI1lgO5wZLVrLXcdqWuDpmaQOKJeq 18 | G/nxytCW/YJA7bFn/8Gjy8DYypJSeeaKu7o3P3+ONJHdIMHb+MdcheDBS9AOFSeo 19 | xI0D5EbO9F873O77l7nbD7B0X34HFN0nGczC4poexIpbDFG3hAPekwZ5KC6VwJLc 20 | 1Q== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /test/mitmproxy/net/data/verificationcerts/trusted-root.srl: -------------------------------------------------------------------------------- 1 | A3E83F6857295BF2 2 | -------------------------------------------------------------------------------- /test/mitmproxy/net/http/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/net/http/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/net/http/http1/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/net/http/http1/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/net/http/http2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/net/http/http2/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/net/http/http2/test_framereader.py: -------------------------------------------------------------------------------- 1 | # foobar 2 | -------------------------------------------------------------------------------- /test/mitmproxy/net/http/test_multipart.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.net.http import Headers 2 | from mitmproxy.net.http import multipart 3 | 4 | 5 | def test_decode(): 6 | boundary = 'somefancyboundary' 7 | headers = Headers( 8 | content_type='multipart/form-data; boundary=' + boundary 9 | ) 10 | content = ( 11 | "--{0}\n" 12 | "Content-Disposition: form-data; name=\"field1\"\n\n" 13 | "value1\n" 14 | "--{0}\n" 15 | "Content-Disposition: form-data; name=\"field2\"\n\n" 16 | "value2\n" 17 | "--{0}--".format(boundary).encode() 18 | ) 19 | 20 | form = multipart.decode(headers, content) 21 | 22 | assert len(form) == 2 23 | assert form[0] == (b"field1", b"value1") 24 | assert form[1] == (b"field2", b"value2") 25 | -------------------------------------------------------------------------------- /test/mitmproxy/net/http/test_status_codes.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.net.http import status_codes 2 | 3 | 4 | def test_simple(): 5 | assert status_codes.IM_A_TEAPOT == 418 6 | assert status_codes.RESPONSES[418] == "I'm a teapot" 7 | -------------------------------------------------------------------------------- /test/mitmproxy/net/http/test_user_agents.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.net.http import user_agents 2 | 3 | 4 | def test_get_shortcut(): 5 | assert user_agents.get_by_shortcut("c")[0] == "chrome" 6 | assert not user_agents.get_by_shortcut("_") 7 | -------------------------------------------------------------------------------- /test/mitmproxy/net/test_check.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from mitmproxy.net import check 4 | 5 | 6 | def test_is_valid_host(): 7 | assert not check.is_valid_host(b"") 8 | assert check.is_valid_host(b"one.two") 9 | assert not check.is_valid_host(b"one" * 255) 10 | assert check.is_valid_host(b"one.two.") 11 | -------------------------------------------------------------------------------- /test/mitmproxy/net/test_imports.py: -------------------------------------------------------------------------------- 1 | # These are actually tests! 2 | -------------------------------------------------------------------------------- /test/mitmproxy/net/tools/getcertnames: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | sys.path.insert(0, "../../") 4 | from mitmproxy.net import tcp 5 | 6 | 7 | def get_remote_cert(host, port, sni): 8 | c = tcp.TCPClient((host, port)) 9 | c.connect() 10 | c.convert_to_ssl(sni=sni) 11 | return c.cert 12 | 13 | if len(sys.argv) > 2: 14 | port = int(sys.argv[2]) 15 | else: 16 | port = 443 17 | if len(sys.argv) > 3: 18 | sni = sys.argv[3] 19 | else: 20 | sni = None 21 | 22 | cert = get_remote_cert(sys.argv[1], port, sni) 23 | print("CN:", cert.cn) 24 | if cert.altnames: 25 | print("SANs:") 26 | for i in cert.altnames: 27 | print("\t", i) 28 | -------------------------------------------------------------------------------- /test/mitmproxy/net/websockets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/net/websockets/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/net/websockets/test_masker.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import pytest 3 | 4 | from mitmproxy.net import websockets 5 | 6 | 7 | class TestMasker: 8 | 9 | @pytest.mark.parametrize("input,expected", [ 10 | ([b"a"], '00'), 11 | ([b"four"], '070d1616'), 12 | ([b"fourf"], '070d161607'), 13 | ([b"fourfive"], '070d1616070b1501'), 14 | ([b"a", b"aasdfasdfa", b"asdf"], '000302170504021705040205120605'), 15 | ([b"a" * 50, b"aasdfasdfa", b"asdf"], '00030205000302050003020500030205000302050003020500030205000302050003020500030205000302050003020500030205120605051206050500110702'), # noqa 16 | ]) 17 | def test_masker(self, input, expected): 18 | m = websockets.Masker(b"abcd") 19 | data = b"".join([m(t) for t in input]) 20 | assert data == codecs.decode(expected, 'hex') 21 | 22 | data = websockets.Masker(b"abcd")(data) 23 | assert data == b"".join(input) 24 | -------------------------------------------------------------------------------- /test/mitmproxy/protocol/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/protocol/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/script/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/script/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/test_addonmanager.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import addonmanager 2 | from mitmproxy import options 3 | from mitmproxy import master 4 | from mitmproxy import proxy 5 | 6 | 7 | class TAddon: 8 | def __init__(self, name): 9 | self.name = name 10 | 11 | def __repr__(self): 12 | return "Addon(%s)" % self.name 13 | 14 | 15 | def test_simple(): 16 | o = options.Options() 17 | m = master.Master(o, proxy.DummyServer(o)) 18 | a = addonmanager.AddonManager(m) 19 | a.add(TAddon("one")) 20 | assert a.get("one") 21 | assert not a.get("two") 22 | a.clear() 23 | assert not a.chain 24 | -------------------------------------------------------------------------------- /test/mitmproxy/test_custom_contentview.py: -------------------------------------------------------------------------------- 1 | import mitmproxy.contentviews as cv 2 | from mitmproxy.net.http import Headers 3 | 4 | 5 | def test_custom_views(): 6 | class ViewNoop(cv.View): 7 | name = "noop" 8 | prompt = ("noop", "n") 9 | content_types = ["text/none"] 10 | 11 | def __call__(self, data, **metadata): 12 | return "noop", cv.format_text(data) 13 | 14 | view_obj = ViewNoop() 15 | 16 | cv.add(view_obj) 17 | 18 | assert cv.get("noop") 19 | 20 | r = cv.get_content_view( 21 | cv.get("noop"), 22 | "[1, 2, 3]", 23 | headers=Headers( 24 | content_type="text/plain" 25 | ) 26 | ) 27 | assert "noop" in r[0] 28 | 29 | # now try content-type matching 30 | r = cv.get_content_view( 31 | cv.get("Auto"), 32 | "[1, 2, 3]", 33 | headers=Headers( 34 | content_type="text/none" 35 | ) 36 | ) 37 | assert "noop" in r[0] 38 | 39 | # now try removing the custom view 40 | cv.remove(view_obj) 41 | r = cv.get_content_view( 42 | cv.get("Auto"), 43 | b"[1, 2, 3]", 44 | headers=Headers( 45 | content_type="text/none" 46 | ) 47 | ) 48 | assert "noop" not in r[0] 49 | -------------------------------------------------------------------------------- /test/mitmproxy/test_flow_format_compat.py: -------------------------------------------------------------------------------- 1 | from mitmproxy import io 2 | from mitmproxy import exceptions 3 | from . import tutils 4 | 5 | 6 | def test_load(): 7 | with open(tutils.test_data.path("data/dumpfile-011"), "rb") as f: 8 | flow_reader = io.FlowReader(f) 9 | flows = list(flow_reader.stream()) 10 | assert len(flows) == 1 11 | assert flows[0].request.url == "https://example.com/" 12 | 13 | 14 | def test_cannot_convert(): 15 | with open(tutils.test_data.path("data/dumpfile-010"), "rb") as f: 16 | flow_reader = io.FlowReader(f) 17 | with tutils.raises(exceptions.FlowReadException): 18 | list(flow_reader.stream()) 19 | -------------------------------------------------------------------------------- /test/mitmproxy/test_platform_pf.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from mitmproxy.platform import pf 3 | from . import tutils 4 | 5 | 6 | class TestLookup: 7 | 8 | def test_simple(self): 9 | if sys.platform == "freebsd10": 10 | p = tutils.test_data.path("data/pf02") 11 | d = open(p, "rb").read() 12 | else: 13 | p = tutils.test_data.path("data/pf01") 14 | d = open(p, "rb").read() 15 | assert pf.lookup("192.168.1.111", 40000, d) == ("5.5.5.5", 80) 16 | tutils.raises( 17 | "Could not resolve original destination", 18 | pf.lookup, 19 | "192.168.1.112", 20 | 40000, 21 | d) 22 | tutils.raises( 23 | "Could not resolve original destination", 24 | pf.lookup, 25 | "192.168.1.111", 26 | 40001, 27 | d) 28 | -------------------------------------------------------------------------------- /test/mitmproxy/test_types_bidi.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.types import bidi 2 | from mitmproxy.test import tutils 3 | 4 | 5 | def test_bidi(): 6 | b = bidi.BiDi(a=1, b=2) 7 | assert b.a == 1 8 | assert b.get_name(1) == "a" 9 | assert b.get_name(5) is None 10 | tutils.raises(AttributeError, getattr, b, "c") 11 | tutils.raises(ValueError, bidi.BiDi, one=1, two=1) 12 | -------------------------------------------------------------------------------- /test/mitmproxy/test_types_serializable.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.types import serializable 2 | 3 | 4 | class SerializableDummy(serializable.Serializable): 5 | def __init__(self, i): 6 | self.i = i 7 | 8 | def get_state(self): 9 | return self.i 10 | 11 | def set_state(self, i): 12 | self.i = i 13 | 14 | def from_state(self, state): 15 | return type(self)(state) 16 | 17 | 18 | class TestSerializable: 19 | 20 | def test_copy(self): 21 | a = SerializableDummy(42) 22 | assert a.i == 42 23 | b = a.copy() 24 | assert b.i == 42 25 | 26 | a.set_state(1) 27 | assert a.i == 1 28 | assert b.i == 42 29 | -------------------------------------------------------------------------------- /test/mitmproxy/test_web_app.py: -------------------------------------------------------------------------------- 1 | import tornado.testing 2 | 3 | from mitmproxy import proxy 4 | from mitmproxy.tools.web import app 5 | from mitmproxy.tools.web import master as webmaster 6 | 7 | 8 | class TestApp(tornado.testing.AsyncHTTPTestCase): 9 | def get_app(self): 10 | o = webmaster.Options() 11 | m = webmaster.WebMaster(o, proxy.DummyServer()) 12 | return app.Application(m, None, None) 13 | 14 | def test_index(self): 15 | assert self.fetch("/").code == 200 16 | 17 | def test_filter_help(self): 18 | assert self.fetch("/filter-help").code == 200 19 | 20 | def test_events(self): 21 | assert self.fetch("/events").code == 200 22 | 23 | def test_flows(self): 24 | assert self.fetch("/flows").code == 200 25 | -------------------------------------------------------------------------------- /test/mitmproxy/test_web_master.py: -------------------------------------------------------------------------------- 1 | from mitmproxy.tools.web import master 2 | from mitmproxy import proxy 3 | from . import mastertest 4 | 5 | 6 | class TestWebMaster(mastertest.MasterTest): 7 | def mkmaster(self, **options): 8 | o = master.Options(**options) 9 | return master.WebMaster(o, proxy.DummyServer(o)) 10 | 11 | def test_basic(self): 12 | m = self.mkmaster() 13 | for i in (1, 2, 3): 14 | self.dummy_cycle(m, 1, b"") 15 | assert len(m.state.flows) == i 16 | -------------------------------------------------------------------------------- /test/mitmproxy/tools/ab.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/tools/ab.exe -------------------------------------------------------------------------------- /test/mitmproxy/tools/bench.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import time 3 | 4 | n = 100 5 | url = "http://192.168.1.1/" 6 | proxy = "http://192.168.1.115:8080/" 7 | 8 | start = time.time() 9 | for _ in range(n): 10 | requests.get(url, allow_redirects=False, proxies=dict(http=proxy)) 11 | print(".", end="") 12 | t_mitmproxy = time.time() - start 13 | 14 | print("\r\nTotal time with mitmproxy: {}".format(t_mitmproxy)) 15 | 16 | 17 | start = time.time() 18 | for _ in range(n): 19 | requests.get(url, allow_redirects=False) 20 | print(".", end="") 21 | t_without = time.time() - start 22 | 23 | print("\r\nTotal time without mitmproxy: {}".format(t_without)) 24 | -------------------------------------------------------------------------------- /test/mitmproxy/tools/getcert: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | sys.path.insert(0, "../..") 4 | import socket 5 | import tempfile 6 | import ssl 7 | import subprocess 8 | 9 | addr = socket.gethostbyname(sys.argv[1]) 10 | print(ssl.get_server_certificate((addr, 443))) 11 | -------------------------------------------------------------------------------- /test/mitmproxy/tools/inspect_dumpfile.py: -------------------------------------------------------------------------------- 1 | from pprint import pprint 2 | 3 | import click 4 | 5 | from mitmproxy import tnetstring 6 | 7 | 8 | def read_tnetstring(input): 9 | # tnetstring throw a ValueError on EOF, which is hard to catch 10 | # because they raise ValueErrors for a couple of other reasons. 11 | # Check for EOF to avoid this. 12 | if not input.read(1): 13 | return None 14 | else: 15 | input.seek(-1, 1) 16 | return tnetstring.load(input) 17 | 18 | 19 | @click.command() 20 | @click.argument("input", type=click.File('rb')) 21 | def inspect(input): 22 | """ 23 | pretty-print a dumpfile 24 | """ 25 | while True: 26 | data = read_tnetstring(input) 27 | if not data: 28 | break 29 | pprint(data) 30 | 31 | 32 | if __name__ == "__main__": 33 | inspect() 34 | -------------------------------------------------------------------------------- /test/mitmproxy/tools/memoryleak.py: -------------------------------------------------------------------------------- 1 | import gc 2 | import threading 3 | from pympler import muppy, refbrowser 4 | from OpenSSL import SSL 5 | # import os 6 | # os.environ["TK_LIBRARY"] = r"C:\Python27\tcl\tcl8.5" 7 | # os.environ["TCL_LIBRARY"] = r"C:\Python27\tcl\tcl8.5" 8 | 9 | # Also noteworthy: guppy, objgraph 10 | 11 | step = 0 12 | __memory_locals__ = True 13 | 14 | 15 | def str_fun(obj): 16 | if isinstance(obj, dict): 17 | if "__memory_locals__" in obj: 18 | return "(-locals-)" 19 | if "self" in obj and isinstance(obj["self"], refbrowser.InteractiveBrowser): 20 | return "(-browser-)" 21 | return str(id(obj)) + ": " + str(obj)[:100].replace("\r\n", "\\r\\n").replace("\n", "\\n") 22 | 23 | 24 | def request(ctx, flow): 25 | global step, ssl 26 | print("==========") 27 | print("GC: {}".format(gc.collect())) 28 | print("Threads: {}".format(threading.active_count())) 29 | 30 | step += 1 31 | if step == 1: 32 | all_objects = muppy.get_objects() 33 | ssl = muppy.filter(all_objects, SSL.Connection)[0] 34 | if step == 2: 35 | ib = refbrowser.InteractiveBrowser(ssl, 2, str_fun, repeat=False) 36 | del ssl # do this to unpollute view 37 | ib.main(True) 38 | # print("\r\n".join(str(x)[:100] for x in gc.get_referrers(ssl))) 39 | -------------------------------------------------------------------------------- /test/mitmproxy/tools/passive_close.py: -------------------------------------------------------------------------------- 1 | import socketserver 2 | from time import sleep 3 | 4 | 5 | class service(socketserver.BaseRequestHandler): 6 | 7 | def handle(self): 8 | data = 'dummy' 9 | print("Client connected with ", self.client_address) 10 | while True: 11 | self.request.send( 12 | "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 7\r\n\r\ncontent") 13 | data = self.request.recv(1024) 14 | if not len(data): 15 | print("Connection closed by remote: ", self.client_address) 16 | sleep(3600) 17 | 18 | 19 | class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): 20 | pass 21 | 22 | server = ThreadedTCPServer(('', 1520), service) 23 | server.serve_forever() 24 | -------------------------------------------------------------------------------- /test/mitmproxy/tools/testpatt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generate a test pattern with pathoc 4 | PATHOD=localhost:9999 5 | pathoc -s -c $PATHOD localhost:8080 "get:'/p/200:p0,1:b@2048b':b@2048b" 6 | pathoc -s -c $PATHOD localhost:8080 "get:'/p/300:p0,1:b@2048b':b@2048b" 7 | pathoc -s -c $PATHOD localhost:8080 "get:'/p/400:p0,1:b@2048b':b@2048b" 8 | pathoc -s -c $PATHOD localhost:8080 "get:'/p/500:p0,1:b@2048b':b@2048b" 9 | pathoc -s -c $PATHOD localhost:8080 "get:'/p/600:p0,1:b@2048b':b@2048b" 10 | -------------------------------------------------------------------------------- /test/mitmproxy/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/mitmproxy/utils/__init__.py -------------------------------------------------------------------------------- /test/mitmproxy/utils/test_data.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from mitmproxy.utils import data 3 | 4 | 5 | def test_pkg_data(): 6 | assert data.pkg_data.path("tools/console") 7 | with pytest.raises(ValueError): 8 | data.pkg_data.path("nonexistent") 9 | -------------------------------------------------------------------------------- /test/mitmproxy/utils/test_debug.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from mitmproxy.utils import debug 4 | 5 | 6 | def test_dump_info(): 7 | cs = io.StringIO() 8 | debug.dump_info(None, None, file=cs, testing=True) 9 | assert cs.getvalue() 10 | 11 | 12 | def test_dump_stacks(): 13 | cs = io.StringIO() 14 | debug.dump_stacks(None, None, file=cs, testing=True) 15 | assert cs.getvalue() 16 | 17 | 18 | def test_sysinfo(): 19 | assert debug.sysinfo() 20 | 21 | 22 | def test_register_info_dumpers(): 23 | debug.register_info_dumpers() 24 | -------------------------------------------------------------------------------- /test/mitmproxy/utils/test_version_check.py: -------------------------------------------------------------------------------- 1 | import io 2 | import mock 3 | from mitmproxy.utils import version_check 4 | 5 | 6 | @mock.patch("sys.exit") 7 | def test_check_pyopenssl_version(sexit): 8 | fp = io.StringIO() 9 | version_check.check_pyopenssl_version(fp=fp) 10 | assert not fp.getvalue() 11 | assert not sexit.called 12 | 13 | version_check.check_pyopenssl_version((9999,), fp=fp) 14 | assert "outdated" in fp.getvalue() 15 | assert sexit.called 16 | 17 | 18 | @mock.patch("sys.exit") 19 | @mock.patch("OpenSSL.__version__") 20 | def test_unparseable_pyopenssl_version(version, sexit): 21 | version.split.return_value = ["foo", "bar"] 22 | fp = io.StringIO() 23 | version_check.check_pyopenssl_version(fp=fp) 24 | assert "Cannot parse" in fp.getvalue() 25 | assert not sexit.called 26 | -------------------------------------------------------------------------------- /test/pathod/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/test/pathod/__init__.py -------------------------------------------------------------------------------- /test/pathod/data/clientcert/.gitignore: -------------------------------------------------------------------------------- 1 | client.crt 2 | client.key 3 | client.req 4 | -------------------------------------------------------------------------------- /test/pathod/data/clientcert/client.cnf: -------------------------------------------------------------------------------- 1 | [ ssl_client ] 2 | basicConstraints = CA:FALSE 3 | nsCertType = client 4 | keyUsage = digitalSignature, keyEncipherment 5 | extendedKeyUsage = clientAuth 6 | -------------------------------------------------------------------------------- /test/pathod/data/clientcert/make: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | openssl genrsa -out client.key 2048 4 | openssl req -key client.key -new -out client.req 5 | openssl x509 -req -days 365 -in client.req -signkey client.key -out client.crt -extfile client.cnf -extensions ssl_client 6 | openssl x509 -req -days 1000 -in client.req -CA ~/.mitmproxy/mitmproxy-ca.pem -CAkey ~/.mitmproxy/mitmproxy-ca.pem -set_serial 00001 -out client.crt -extensions ssl_client 7 | cat client.key client.crt > client.pem 8 | openssl x509 -text -noout -in client.pem 9 | -------------------------------------------------------------------------------- /test/pathod/data/file: -------------------------------------------------------------------------------- 1 | testfile 2 | -------------------------------------------------------------------------------- /test/pathod/data/request: -------------------------------------------------------------------------------- 1 | get:/foo 2 | -------------------------------------------------------------------------------- /test/pathod/data/response: -------------------------------------------------------------------------------- 1 | 202 2 | -------------------------------------------------------------------------------- /test/pathod/scripts/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ ! -f ./private.key ] 4 | then 5 | openssl genrsa -out private.key 3072 6 | fi 7 | openssl req \ 8 | -batch \ 9 | -new -x509 \ 10 | -key private.key \ 11 | -sha256 \ 12 | -out cert.pem \ 13 | -days 9999 \ 14 | -config ./openssl.cnf 15 | openssl x509 -in cert.pem -text -noout 16 | cat ./private.key ./cert.pem > testcert.pem 17 | rm ./private.key ./cert.pem 18 | -------------------------------------------------------------------------------- /test/pathod/scripts/openssl.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 1024 3 | default_keyfile = privkey.pem 4 | distinguished_name = req_distinguished_name 5 | x509_extensions = v3_ca 6 | 7 | [ req_distinguished_name ] 8 | countryName = Country Name (2 letter code) 9 | countryName_default = NZ 10 | countryName_min = 2 11 | countryName_max = 2 12 | stateOrProvinceName = State or Province Name (full name) 13 | stateOrProvinceName_default = Otago 14 | localityName = Locality Name (eg, city) 15 | 0.organizationName = Organization Name (eg, company) 16 | 0.organizationName_default = Pathod 17 | commonName = Common Name (e.g. server FQDN or YOUR name) 18 | commonName_default = test.com 19 | commonName_max = 64 20 | 21 | [ v3_req ] 22 | 23 | basicConstraints = CA:FALSE 24 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 25 | 26 | [ v3_ca ] 27 | 28 | keyUsage = digitalSignature, keyEncipherment 29 | subjectKeyIdentifier=hash 30 | authorityKeyIdentifier=keyid:always,issuer:always 31 | basicConstraints = CA:true 32 | subjectAltName = @alternate_names 33 | 34 | 35 | [ alternate_names ] 36 | 37 | DNS.1 = test.com 38 | DNS.2 = test2.com 39 | DNS.3 = test3.com 40 | -------------------------------------------------------------------------------- /test/pathod/test_log.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | from pathod import log 4 | from mitmproxy import exceptions 5 | 6 | 7 | class DummyIO(io.StringIO): 8 | 9 | def start_log(self, *args, **kwargs): 10 | pass 11 | 12 | def get_log(self, *args, **kwargs): 13 | return "" 14 | 15 | 16 | def test_disconnect(): 17 | outf = DummyIO() 18 | rw = DummyIO() 19 | l = log.ConnectionLogger(outf, False, True, rw, rw) 20 | try: 21 | with l.ctx() as lg: 22 | lg("Test") 23 | except exceptions.TcpDisconnect: 24 | pass 25 | assert "Test" in outf.getvalue() 26 | -------------------------------------------------------------------------------- /test/pathod/test_utils.py: -------------------------------------------------------------------------------- 1 | from pathod import utils 2 | 3 | from . import tutils 4 | 5 | 6 | def test_membool(): 7 | m = utils.MemBool() 8 | assert not m.v 9 | assert m(1) 10 | assert m.v == 1 11 | assert m(2) 12 | assert m.v == 2 13 | 14 | 15 | def test_data_path(): 16 | tutils.raises(ValueError, utils.data.path, "nonexistent") 17 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py35, docs, lint 3 | skipsdist = True 4 | toxworkdir={env:TOX_WORK_DIR:.tox} 5 | 6 | [testenv] 7 | deps = 8 | {env:CI_DEPS:} 9 | -rrequirements.txt 10 | passenv = CODECOV_TOKEN CI CI_* TRAVIS TRAVIS_* APPVEYOR APPVEYOR_* 11 | setenv = HOME = {envtmpdir} 12 | commands = 13 | py.test --timeout 60 {posargs} 14 | {env:CI_COMMANDS:python -c ""} 15 | 16 | [testenv:docs] 17 | changedir = docs 18 | commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html 19 | 20 | [testenv:lint] 21 | commands = 22 | flake8 --jobs 8 --count mitmproxy pathod examples test 23 | rstcheck README.rst 24 | mypy -s ./mitmproxy/addonmanager.py 25 | -------------------------------------------------------------------------------- /web/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "react"], 3 | "plugins": ["transform-class-properties", "transform-object-rest-spread"] 4 | } -------------------------------------------------------------------------------- /web/.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = space 3 | indent_size = 4 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | end_of_line = lf 7 | -------------------------------------------------------------------------------- /web/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint" 3 | } -------------------------------------------------------------------------------- /web/README: -------------------------------------------------------------------------------- 1 | 2 | Starting up 3 | 4 | - npm install 5 | - gulp 6 | - run mitmweb and open http://localhost:8081/ 7 | -------------------------------------------------------------------------------- /web/conf.js: -------------------------------------------------------------------------------- 1 | 2 | var conf = { 3 | src: "src/", 4 | dist: "../mitmproxy/web", 5 | static: "../mitmproxy/web/static", 6 | js: { 7 | // Don't package these in the vendor distribution 8 | vendor_excludes: [ 9 | "bootstrap" // We only use Bootstrap's CSS. 10 | ], 11 | // Package these as well as the dependencies 12 | vendor_includes: [ 13 | ], 14 | app: 'src/js/app', 15 | eslint: ["src/js/**/*.js", "!src/js/filt/filt.js"] 16 | }, 17 | css: { 18 | vendor: ["src/css/vendor.less"], 19 | app: ["src/css/app.less"] 20 | }, 21 | copy: [ 22 | "src/images/**", "src/fonts/fontawesome-webfont.*" 23 | ], 24 | templates: [ 25 | "src/templates/*" 26 | ], 27 | peg: ["src/js/filt/filt.peg"] 28 | }; 29 | 30 | module.exports = conf; 31 | -------------------------------------------------------------------------------- /web/src/css/app.less: -------------------------------------------------------------------------------- 1 | // www.paulirish.com/2012/box-sizing-border-box-ftw/ 2 | html { 3 | box-sizing: border-box; 4 | } 5 | 6 | *, *:before, *:after { 7 | box-sizing: inherit; 8 | } 9 | 10 | @import (less) "sprites.less"; 11 | @import (less) "layout.less"; 12 | @import (less) "tabs.less"; 13 | @import (less) "header.less"; 14 | @import (less) "flowtable.less"; 15 | @import (less) "flowdetail.less"; 16 | @import (less) "flowview.less"; 17 | @import (less) "prompt.less"; 18 | @import (less) "eventlog.less"; 19 | @import (less) "footer.less"; 20 | @import (less) "codemirror.less"; 21 | @import (less) "contentview.less"; 22 | -------------------------------------------------------------------------------- /web/src/css/codemirror.less: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | border: 1px solid #ccc; 3 | height: auto !important; 4 | max-height: 2048px !important; 5 | } 6 | 7 | @import (inline) "../../node_modules/codemirror/lib/codemirror.css"; 8 | -------------------------------------------------------------------------------- /web/src/css/contentview.less: -------------------------------------------------------------------------------- 1 | .contentview { 2 | .header { 3 | font-weight: bold; 4 | } 5 | .highlight{ 6 | font-weight: bold; 7 | } 8 | .offset{ 9 | color: blue 10 | } 11 | .text{ 12 | 13 | } 14 | .codeeditor{ 15 | margin-bottom: 12px; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /web/src/css/dropdown.less: -------------------------------------------------------------------------------- 1 | hr.divider { 2 | margin-top: 5px; 3 | margin-bottom: 5px; 4 | } 5 | -------------------------------------------------------------------------------- /web/src/css/eventlog.less: -------------------------------------------------------------------------------- 1 | .eventlog { 2 | 3 | height: 200px; 4 | flex: 0 0 auto; 5 | 6 | display: flex; 7 | flex-direction: column; 8 | 9 | > div { 10 | background-color: #F2F2F2; 11 | padding: 0 5px; 12 | flex: 0 0 auto; 13 | border-top: 1px solid #aaa; 14 | cursor: row-resize; 15 | } 16 | 17 | > pre { 18 | flex: 1 1 auto; 19 | 20 | margin: 0; 21 | border-radius: 0; 22 | overflow-x: auto; 23 | overflow-y: scroll; 24 | background-color: #fcfcfc; 25 | } 26 | 27 | .fa-close { 28 | cursor: pointer; 29 | float: right; 30 | color: grey; 31 | padding: 3px 0; 32 | padding-left: 10px; 33 | &:hover { 34 | color: black; 35 | } 36 | } 37 | 38 | .btn-toggle { 39 | margin-top: -2px; 40 | margin-left: 3px; 41 | padding: 2px 2px; 42 | font-size: 10px; 43 | line-height: 10px; 44 | border-radius: 2px; 45 | } 46 | .label { 47 | cursor: pointer; 48 | vertical-align: middle; 49 | display: inline-block; 50 | margin-top: -2px; 51 | margin-left: 3px; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /web/src/css/flowview.less: -------------------------------------------------------------------------------- 1 | .flowview-image { 2 | 3 | text-align: center; 4 | 5 | img { 6 | max-width: 100%; 7 | max-height: 100%; 8 | } 9 | } 10 | 11 | .edit-flow-container { 12 | position: relative; 13 | } 14 | 15 | .edit-flow { 16 | cursor: pointer; 17 | position: absolute; 18 | right: 0; 19 | top: 5px; 20 | height: 40px; 21 | width: 40px; 22 | border-radius: 20px; 23 | z-index: 10000; 24 | 25 | background-color: rgba(255, 255, 255, 0.7); 26 | border: solid 2px rgba(248, 145, 59, 0.7); 27 | 28 | text-align: center; 29 | font-size: 22px; 30 | line-height: 37px; 31 | 32 | transition: all 100ms ease-in-out; 33 | } 34 | 35 | .edit-flow:hover { 36 | background-color: rgba(239, 108, 0, 0.7); 37 | color: rgba(0,0,0,0.8); 38 | border: solid 2px transparent; 39 | } 40 | -------------------------------------------------------------------------------- /web/src/css/footer.less: -------------------------------------------------------------------------------- 1 | footer { 2 | box-shadow: 0 -1px 3px lightgray; 3 | padding: 0px 10px 3px; 4 | 5 | .label { 6 | margin-right: 3px; 7 | } 8 | } -------------------------------------------------------------------------------- /web/src/css/header.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../../node_modules/bootstrap/less/variables.less'; 2 | @import (reference) '../../node_modules/bootstrap/less/mixins/grid.less'; 3 | 4 | header { 5 | padding-top: 0.5em; 6 | background-color: white; 7 | @separator-color: lighten(grey, 15%); 8 | .menu { 9 | padding: 10px; 10 | border-bottom: solid @separator-color 1px; 11 | } 12 | } 13 | 14 | @menu-row-gutter-width: 5px; 15 | .menu-row { 16 | .make-row(@menu-row-gutter-width); 17 | } 18 | 19 | .filter-input { 20 | .make-sm-column(3, @menu-row-gutter-width); 21 | margin-bottom:5px; 22 | } 23 | 24 | .filter-input .popover { 25 | top: 27px; 26 | display: block; 27 | max-width: none; 28 | opacity: 0.9; 29 | 30 | .popover-content { 31 | max-height: 500px; 32 | overflow-y: auto; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /web/src/css/prompt.less: -------------------------------------------------------------------------------- 1 | .prompt-dialog { 2 | top: 0; 3 | bottom: 0; 4 | left: 0; 5 | right: 0; 6 | position: fixed; 7 | z-index: 100; 8 | background-color: rgba(0, 0, 0, 0.1); 9 | } 10 | 11 | .prompt-content { 12 | position: fixed; 13 | bottom: 0; 14 | left: 0; 15 | right: 0; 16 | height: 25px; 17 | padding: 2px 5px; 18 | background-color: white; 19 | box-shadow: 0 -1px 3px lightgray; 20 | 21 | .option { 22 | cursor: pointer; 23 | &:not(:last-child)::after { 24 | content: ", "; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /web/src/css/sprites.less: -------------------------------------------------------------------------------- 1 | .resource-icon { 2 | width: 32px; 3 | height: 32px; 4 | } 5 | 6 | // From Chrome Dev Tools 7 | .resource-icon-css { 8 | background-image: url(images/chrome-devtools/resourceCSSIcon.png); 9 | } 10 | 11 | .resource-icon-document { 12 | background-image: url(images/chrome-devtools/resourceDocumentIcon.png); 13 | } 14 | 15 | .resource-icon-js { 16 | background-image: url(images/chrome-devtools/resourceJSIcon.png); 17 | } 18 | 19 | .resource-icon-plain { 20 | background-image: url(images/chrome-devtools/resourcePlainIcon.png); 21 | } 22 | 23 | // Own 24 | .resource-icon-executable { 25 | background-image: url(images/resourceExecutableIcon.png); 26 | } 27 | 28 | .resource-icon-flash { 29 | background-image: url(images/resourceFlashIcon.png); 30 | } 31 | 32 | .resource-icon-image { 33 | background-image: url(images/resourceImageIcon.png); 34 | } 35 | 36 | .resource-icon-java { 37 | background-image: url(images/resourceJavaIcon.png); 38 | } 39 | 40 | .resource-icon-not-modified { 41 | background-image: url(images/resourceNotModifiedIcon.png); 42 | } 43 | 44 | .resource-icon-redirect { 45 | background-image: url(images/resourceRedirectIcon.png); 46 | } -------------------------------------------------------------------------------- /web/src/css/tabs.less: -------------------------------------------------------------------------------- 1 | .nav-tabs { 2 | 3 | @separator-color: lighten(grey, 15%); 4 | 5 | border-bottom: solid @separator-color 1px; 6 | 7 | a { 8 | display: inline-block; 9 | border: solid transparent 1px; 10 | text-decoration: none; 11 | //text-transform: uppercase; 12 | //font-family: Lato; 13 | 14 | &.active { 15 | background-color: white; 16 | border-color: @separator-color; 17 | border-bottom-color: white; 18 | } 19 | &.special { 20 | @special-color: #396cad; 21 | color: white; 22 | background-color: @special-color; 23 | border-bottom-color: @special-color; 24 | &:hover { 25 | background-color: lighten(@special-color, 10%); 26 | } 27 | } 28 | } 29 | 30 | } 31 | 32 | .nav-tabs-lg { 33 | a { 34 | padding: 3px 14px; 35 | margin: 0 2px -1px; 36 | } 37 | } 38 | 39 | .nav-tabs-sm { 40 | a { 41 | padding: 0px 7px; 42 | margin: 2px 2px -1px; 43 | } 44 | a.nav-action { 45 | float: right; 46 | padding: 0; 47 | margin: 1px 0 0px; 48 | } 49 | } -------------------------------------------------------------------------------- /web/src/css/vendor-bootstrap-variables.less: -------------------------------------------------------------------------------- 1 | @navbar-height: 32px; 2 | @navbar-default-link-color: #303030; 3 | @navbar-default-color: #303030; 4 | @navbar-default-bg: #ffffff; 5 | @navbar-default-border: #e0e0e0; 6 | -------------------------------------------------------------------------------- /web/src/css/vendor.less: -------------------------------------------------------------------------------- 1 | // Bootstrap 2 | @import 'vendor-bootstrap.less'; 3 | @import (less) '../fonts/font-awesome.css'; 4 | -------------------------------------------------------------------------------- /web/src/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /web/src/fonts/README: -------------------------------------------------------------------------------- 1 | 2 | From a rendered version of the FontAwesome github repo. 3 | -------------------------------------------------------------------------------- /web/src/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /web/src/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /web/src/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /web/src/images/chrome-devtools/resourceCSSIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/chrome-devtools/resourceCSSIcon.png -------------------------------------------------------------------------------- /web/src/images/chrome-devtools/resourceDocumentIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/chrome-devtools/resourceDocumentIcon.png -------------------------------------------------------------------------------- /web/src/images/chrome-devtools/resourceJSIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/chrome-devtools/resourceJSIcon.png -------------------------------------------------------------------------------- /web/src/images/chrome-devtools/resourcePlainIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/chrome-devtools/resourcePlainIcon.png -------------------------------------------------------------------------------- /web/src/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/favicon.ico -------------------------------------------------------------------------------- /web/src/images/resourceExecutableIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/resourceExecutableIcon.png -------------------------------------------------------------------------------- /web/src/images/resourceFlashIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/resourceFlashIcon.png -------------------------------------------------------------------------------- /web/src/images/resourceImageIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/resourceImageIcon.png -------------------------------------------------------------------------------- /web/src/images/resourceJavaIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/resourceJavaIcon.png -------------------------------------------------------------------------------- /web/src/images/resourceNotModifiedIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/resourceNotModifiedIcon.png -------------------------------------------------------------------------------- /web/src/images/resourceRedirectIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/mitmproxy/11d266419c29ca26a4671f8170b0c36c32036389/web/src/images/resourceRedirectIcon.png -------------------------------------------------------------------------------- /web/src/js/__tests__/ducks/flowsSpec.js: -------------------------------------------------------------------------------- 1 | jest.unmock('../../ducks/flows'); 2 | 3 | import reduceFlows, * as flowActions from '../../ducks/flows' 4 | 5 | 6 | describe('select flow', () => { 7 | 8 | let state = reduceFlows(undefined, {}) 9 | for (let i of [1, 2, 3, 4]) { 10 | state = reduceFlows(state, flowActions.addFlow({ id: i })) 11 | } 12 | 13 | it('should be possible to select a single flow', () => { 14 | expect(reduceFlows(state, flowActions.select(2))).toEqual( 15 | { 16 | ...state, 17 | selected: [2], 18 | } 19 | ) 20 | }) 21 | 22 | it('should be possible to deselect a flow', () => { 23 | expect(reduceFlows({ ...state, selected: [1] }, flowActions.select())).toEqual( 24 | { 25 | ...state, 26 | selected: [], 27 | } 28 | ) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /web/src/js/__tests__/ducks/tutils.js: -------------------------------------------------------------------------------- 1 | jest.unmock('redux') 2 | jest.unmock('redux-thunk') 3 | 4 | import { combineReducers, applyMiddleware, createStore as createReduxStore } from 'redux' 5 | import thunk from 'redux-thunk' 6 | 7 | export function createStore(parts) { 8 | return createReduxStore( 9 | combineReducers(parts), 10 | applyMiddleware(...[thunk]) 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /web/src/js/actions.js: -------------------------------------------------------------------------------- 1 | import {AppDispatcher} from "./dispatcher.js"; 2 | 3 | export var ActionTypes = { 4 | // Connection 5 | CONNECTION_OPEN: "connection_open", 6 | CONNECTION_CLOSE: "connection_close", 7 | CONNECTION_ERROR: "connection_error", 8 | 9 | // Stores 10 | SETTINGS_STORE: "settings", 11 | EVENT_STORE: "events", 12 | FLOW_STORE: "flows" 13 | }; 14 | 15 | export var StoreCmds = { 16 | ADD: "add", 17 | UPDATE: "update", 18 | REMOVE: "remove", 19 | RESET: "reset" 20 | }; 21 | 22 | export var ConnectionActions = { 23 | open: function () { 24 | AppDispatcher.dispatchViewAction({ 25 | type: ActionTypes.CONNECTION_OPEN 26 | }); 27 | }, 28 | close: function () { 29 | AppDispatcher.dispatchViewAction({ 30 | type: ActionTypes.CONNECTION_CLOSE 31 | }); 32 | }, 33 | error: function () { 34 | AppDispatcher.dispatchViewAction({ 35 | type: ActionTypes.CONNECTION_ERROR 36 | }); 37 | } 38 | }; 39 | 40 | export var Query = { 41 | SEARCH: "s", 42 | HIGHLIGHT: "h", 43 | SHOW_EVENTLOG: "e" 44 | }; 45 | -------------------------------------------------------------------------------- /web/src/js/app.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { applyMiddleware, createStore } from 'redux' 4 | import { Provider } from 'react-redux' 5 | import thunk from 'redux-thunk' 6 | 7 | import ProxyApp from './components/ProxyApp' 8 | import rootReducer from './ducks/index' 9 | import { add as addLog } from './ducks/eventLog' 10 | 11 | const middlewares = [thunk]; 12 | 13 | if (process.env.NODE_ENV !== 'production') { 14 | const createLogger = require('redux-logger'); 15 | middlewares.push(createLogger()); 16 | } 17 | 18 | // logger must be last 19 | const store = createStore( 20 | rootReducer, 21 | applyMiddleware(...middlewares) 22 | ) 23 | 24 | // @todo move to ProxyApp 25 | window.addEventListener('error', msg => { 26 | store.dispatch(addLog(msg)) 27 | }) 28 | 29 | // @todo remove this 30 | document.addEventListener('DOMContentLoaded', () => { 31 | render( 32 | 33 | 34 | , 35 | document.getElementById("mitmproxy") 36 | ) 37 | }) 38 | -------------------------------------------------------------------------------- /web/src/js/components/ContentView/CodeEditor.jsx: -------------------------------------------------------------------------------- 1 | import React, {PropTypes} from 'react' 2 | import Codemirror from 'react-codemirror'; 3 | 4 | 5 | CodeEditor.propTypes = { 6 | content: PropTypes.string.isRequired, 7 | onChange: PropTypes.func.isRequired, 8 | } 9 | 10 | export default function CodeEditor ( { content, onChange} ){ 11 | 12 | let options = { 13 | lineNumbers: true 14 | }; 15 | return ( 16 |
e.stopPropagation()}> 17 | 18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /web/src/js/components/ContentView/ContentViewOptions.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { connect } from 'react-redux' 3 | import ViewSelector from './ViewSelector' 4 | import UploadContentButton from './UploadContentButton' 5 | import DownloadContentButton from './DownloadContentButton' 6 | 7 | ContentViewOptions.propTypes = { 8 | flow: React.PropTypes.object.isRequired, 9 | message: React.PropTypes.object.isRequired, 10 | } 11 | 12 | function ContentViewOptions(props) { 13 | const { flow, message, uploadContent, readonly, contentViewDescription } = props 14 | return ( 15 |
16 | 17 |   18 | 19 |   20 | 21 |   22 | {contentViewDescription} 23 |
24 | ) 25 | } 26 | 27 | export default connect( 28 | state => ({ 29 | contentViewDescription: state.ui.flow.viewDescription 30 | }) 31 | )(ContentViewOptions) 32 | -------------------------------------------------------------------------------- /web/src/js/components/ContentView/DownloadContentButton.jsx: -------------------------------------------------------------------------------- 1 | import { MessageUtils } from "../../flow/utils" 2 | import { PropTypes } from 'react' 3 | 4 | DownloadContentButton.propTypes = { 5 | flow: PropTypes.object.isRequired, 6 | message: PropTypes.object.isRequired, 7 | } 8 | 9 | export default function DownloadContentButton({ flow, message }) { 10 | 11 | return ( 12 | 15 | 16 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /web/src/js/components/ContentView/ShowFullContentButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react' 2 | import { connect } from 'react-redux' 3 | import { render } from 'react-dom'; 4 | import Button from '../common/Button'; 5 | import { setShowFullContent } from '../../ducks/ui/flow' 6 | 7 | 8 | 9 | ShowFullContentButton.propTypes = { 10 | setShowFullContent: PropTypes.func.isRequired, 11 | showFullContent: PropTypes.bool.isRequired 12 | } 13 | 14 | function ShowFullContentButton ( {setShowFullContent, showFullContent, visibleLines, contentLines} ){ 15 | 16 | return ( 17 | !showFullContent && 18 |
19 |
22 | ) 23 | } 24 | 25 | export default connect( 26 | state => ({ 27 | showFullContent: state.ui.flow.showFullContent, 28 | visibleLines: state.ui.flow.maxContentLines, 29 | contentLines: state.ui.flow.content.length 30 | 31 | }), 32 | { 33 | setShowFullContent 34 | } 35 | )(ShowFullContentButton) 36 | 37 | -------------------------------------------------------------------------------- /web/src/js/components/ContentView/UploadContentButton.jsx: -------------------------------------------------------------------------------- 1 | import { PropTypes } from 'react' 2 | import FileChooser from '../common/FileChooser' 3 | 4 | UploadContentButton.propTypes = { 5 | uploadContent: PropTypes.func.isRequired, 6 | } 7 | 8 | export default function UploadContentButton({ uploadContent }) { 9 | 10 | return ( 11 | 16 | ) 17 | } 18 | 19 | -------------------------------------------------------------------------------- /web/src/js/components/FlowTable/FlowRow.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import classnames from 'classnames' 3 | import columns from './FlowColumns' 4 | import { pure } from '../../utils' 5 | 6 | FlowRow.propTypes = { 7 | onSelect: PropTypes.func.isRequired, 8 | flow: PropTypes.object.isRequired, 9 | highlighted: PropTypes.bool, 10 | selected: PropTypes.bool, 11 | } 12 | 13 | function FlowRow({ flow, selected, highlighted, onSelect }) { 14 | const className = classnames({ 15 | 'selected': selected, 16 | 'highlighted': highlighted, 17 | 'intercepted': flow.intercepted, 18 | 'has-request': flow.request, 19 | 'has-response': flow.response, 20 | }) 21 | 22 | return ( 23 | onSelect(flow.id)}> 24 | {columns.map(Column => ( 25 | 26 | ))} 27 | 28 | ) 29 | } 30 | 31 | export default pure(FlowRow) 32 | -------------------------------------------------------------------------------- /web/src/js/components/FlowTable/FlowTableHead.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { connect } from 'react-redux' 3 | import classnames from 'classnames' 4 | import columns from './FlowColumns' 5 | 6 | import { updateSort } from '../../ducks/flowView' 7 | 8 | FlowTableHead.propTypes = { 9 | updateSort: PropTypes.func.isRequired, 10 | sortDesc: React.PropTypes.bool.isRequired, 11 | sortColumn: React.PropTypes.string, 12 | } 13 | 14 | function FlowTableHead({ sortColumn, sortDesc, updateSort }) { 15 | const sortType = sortDesc ? 'sort-desc' : 'sort-asc' 16 | 17 | return ( 18 | 19 | {columns.map(Column => ( 20 | updateSort(Column.name, Column.name !== sortColumn ? false : !sortDesc)}> 23 | {Column.headerName} 24 | 25 | ))} 26 | 27 | ) 28 | } 29 | 30 | export default connect( 31 | state => ({ 32 | sortDesc: state.flowView.sort.desc, 33 | sortColumn: state.flowView.sort.column, 34 | }), 35 | { 36 | updateSort 37 | } 38 | )(FlowTableHead) 39 | -------------------------------------------------------------------------------- /web/src/js/components/Header/ViewMenu.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import { connect } from 'react-redux' 3 | import ToggleButton from '../common/ToggleButton' 4 | import { toggleVisibility } from '../../ducks/eventLog' 5 | 6 | ViewMenu.title = 'View' 7 | ViewMenu.route = 'flows' 8 | 9 | ViewMenu.propTypes = { 10 | eventLogVisible: PropTypes.bool.isRequired, 11 | toggleEventLog: PropTypes.func.isRequired, 12 | } 13 | 14 | function ViewMenu({ eventLogVisible, toggleEventLog }) { 15 | return ( 16 |
17 |
18 | 19 |
20 |
21 |
22 | ) 23 | } 24 | 25 | export default connect( 26 | state => ({ 27 | eventLogVisible: state.eventLog.visible, 28 | }), 29 | { 30 | toggleEventLog: toggleVisibility, 31 | } 32 | )(ViewMenu) 33 | -------------------------------------------------------------------------------- /web/src/js/components/common/Button.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import classnames from 'classnames' 3 | 4 | Button.propTypes = { 5 | onClick: PropTypes.func.isRequired, 6 | text: PropTypes.string, 7 | icon: PropTypes.string 8 | } 9 | 10 | export default function Button({ onClick, text, icon, disabled, className }) { 11 | return ( 12 |
15 | {icon && ( )} 16 | {text && text} 17 |
18 | ) 19 | } 20 | -------------------------------------------------------------------------------- /web/src/js/components/common/FileChooser.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | 3 | FileChooser.propTypes = { 4 | icon: PropTypes.string, 5 | text: PropTypes.string, 6 | className: PropTypes.string, 7 | title: PropTypes.string, 8 | onOpenFile: PropTypes.func.isRequired 9 | } 10 | 11 | export default function FileChooser({ icon, text, className, title, onOpenFile }) { 12 | let fileInput; 13 | return ( 14 | fileInput.click()} 15 | className={className} 16 | title={title}> 17 | 18 | {text} 19 | fileInput = ref} 21 | className="hidden" 22 | type="file" 23 | onChange={e => { e.preventDefault(); if(e.target.files.length > 0) onOpenFile(e.target.files[0]); fileInput = "";}} 24 | /> 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /web/src/js/components/common/ToggleButton.jsx: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | 3 | ToggleButton.propTypes = { 4 | checked: PropTypes.bool.isRequired, 5 | onToggle: PropTypes.func.isRequired, 6 | text: PropTypes.string.isRequired 7 | } 8 | 9 | export default function ToggleButton({ checked, onToggle, text }) { 10 | return ( 11 |
12 | 13 |   14 | {text} 15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /web/src/js/components/helpers/AutoScroll.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | const symShouldStick = Symbol("shouldStick"); 5 | const isAtBottom = v => v.scrollTop + v.clientHeight === v.scrollHeight; 6 | 7 | export default Component => Object.assign(class AutoScrollWrapper extends Component { 8 | 9 | static displayName = Component.name; 10 | 11 | componentWillUpdate() { 12 | const viewport = ReactDOM.findDOMNode(this); 13 | this[symShouldStick] = viewport.scrollTop && isAtBottom(viewport); 14 | super.componentWillUpdate && super.componentWillUpdate(); 15 | } 16 | 17 | componentDidUpdate() { 18 | const viewport = ReactDOM.findDOMNode(this); 19 | if (this[symShouldStick] && !isAtBottom(viewport)) { 20 | viewport.scrollTop = viewport.scrollHeight; 21 | } 22 | super.componentDidUpdate && super.componentDidUpdate(); 23 | } 24 | 25 | }, Component); 26 | -------------------------------------------------------------------------------- /web/src/js/dispatcher.js: -------------------------------------------------------------------------------- 1 | 2 | import flux from "flux"; 3 | 4 | const PayloadSources = { 5 | VIEW: "view", 6 | SERVER: "server" 7 | }; 8 | 9 | 10 | export var AppDispatcher = new flux.Dispatcher(); 11 | AppDispatcher.dispatchViewAction = function (action) { 12 | action.source = PayloadSources.VIEW; 13 | this.dispatch(action); 14 | }; 15 | AppDispatcher.dispatchServerAction = function (action) { 16 | action.source = PayloadSources.SERVER; 17 | this.dispatch(action); 18 | }; -------------------------------------------------------------------------------- /web/src/js/ducks/README.md: -------------------------------------------------------------------------------- 1 | https://github.com/erikras/ducks-modular-redux -------------------------------------------------------------------------------- /web/src/js/ducks/app.js: -------------------------------------------------------------------------------- 1 | import { connect as wsConnect, disconnect as wsDisconnect } from './websocket' 2 | 3 | export const INIT = 'APP_INIT' 4 | 5 | const defaultState = {} 6 | 7 | export function reduce(state = defaultState, action) { 8 | switch (action.type) { 9 | 10 | default: 11 | return state 12 | } 13 | } 14 | 15 | export function init() { 16 | return dispatch => { 17 | dispatch(wsConnect()) 18 | dispatch({ type: INIT }) 19 | } 20 | } 21 | 22 | export function destruct() { 23 | return dispatch => { 24 | dispatch(wsDisconnect()) 25 | dispatch({ type: DESTRUCT }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /web/src/js/ducks/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import eventLog from './eventLog' 3 | import websocket from './websocket' 4 | import flows from './flows' 5 | import flowView from './flowView' 6 | import settings from './settings' 7 | import ui from './ui/index' 8 | import msgQueue from './msgQueue' 9 | 10 | export default combineReducers({ 11 | eventLog, 12 | websocket, 13 | flows, 14 | flowView, 15 | settings, 16 | ui, 17 | msgQueue, 18 | }) 19 | -------------------------------------------------------------------------------- /web/src/js/ducks/ui/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import flow from './flow' 3 | import header from './header' 4 | 5 | export default combineReducers({ 6 | flow, 7 | header, 8 | }) 9 | -------------------------------------------------------------------------------- /web/src/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | mitmproxy 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | --------------------------------------------------------------------------------