mirror of
https://github.com/vee1e/mitmproxy.git
synced 2026-09-01 18:27:18 +00:00
Bumps the github-actions group with 3 updates: [install-pinned/ruff](https://github.com/install-pinned/ruff), [apple-actions/import-codesign-certs](https://github.com/apple-actions/import-codesign-certs) and [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action). Updates `install-pinned/ruff` from fe472defb50a6a2c00ea3a3982534e86e69991e8 to 38b373a3a8635c2be31d92314e816a491fda910a <details> <summary>Commits</summary> <ul> <li><a href="38b373a3a8"><code>38b373a</code></a> update README.md (ruff 0.3.0)</li> <li><a href="06af3ea1c3"><code>06af3ea</code></a> update pins (ruff 0.3.0)</li> <li><a href="be1c354876"><code>be1c354</code></a> update README.md (ruff 0.2.2)</li> <li><a href="c9779bbd5b"><code>c9779bb</code></a> update pins (ruff 0.2.2)</li> <li><a href="48831a86ce"><code>48831a8</code></a> update README.md (ruff 0.2.1)</li> <li><a href="6775b5f352"><code>6775b5f</code></a> update pins (ruff 0.2.1)</li> <li><a href="bc12a64c2f"><code>bc12a64</code></a> update README.md (ruff 0.2.0)</li> <li><a href="3b8cceff45"><code>3b8ccef</code></a> update pins (ruff 0.2.0)</li> <li>See full diff in <a href="fe472defb5...38b373a3a8">compare view</a></li> </ul> </details> <br /> Updates `apple-actions/import-codesign-certs` from 5565bb656f60c98c8fc515f3444dd8db73545dc2 to 493007ed063995cf2d4fbca064704150548f8bb5 <details> <summary>Commits</summary> <ul> <li><a href="493007ed06"><code>493007e</code></a> Merge pull request <a href="https://redirect.github.com/apple-actions/import-codesign-certs/issues/62">#62</a> from himself65/patch-1</li> <li><a href="2e5aa07267"><code>2e5aa07</code></a> Update README.md</li> <li>See full diff in <a href="5565bb656f...493007ed06">compare view</a></li> </ul> </details> <br /> Updates `docker/setup-buildx-action` from 3.0.0 to 3.1.0 <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/docker/setup-buildx-action/releases">docker/setup-buildx-action's releases</a>.</em></p> <blockquote> <h2>v3.1.0</h2> <ul> <li><code>cache-binary</code> input to enable/disable caching binary to GHA cache backend by <a href="https://github.com/crazy-max"><code>@crazy-max</code></a> in <a href="https://redirect.github.com/docker/setup-buildx-action/pull/300">docker/setup-buildx-action#300</a></li> <li>build(deps): bump <code>@babel/traverse</code> from 7.17.3 to 7.23.2 in <a href="https://redirect.github.com/docker/setup-buildx-action/pull/282">docker/setup-buildx-action#282</a></li> <li>build(deps): bump <code>@docker/actions-toolkit</code> from 0.12.0 to 0.17.0 in <a href="https://redirect.github.com/docker/setup-buildx-action/pull/281">docker/setup-buildx-action#281</a> <a href="https://redirect.github.com/docker/setup-buildx-action/pull/284">docker/setup-buildx-action#284</a> <a href="https://redirect.github.com/docker/setup-buildx-action/pull/299">docker/setup-buildx-action#299</a></li> <li>build(deps): bump uuid from 9.0.0 to 9.0.1 in <a href="https://redirect.github.com/docker/setup-buildx-action/pull/271">docker/setup-buildx-action#271</a></li> <li>build(deps): bump undici from 5.26.3 to 5.28.3 in <a href="https://redirect.github.com/docker/setup-buildx-action/pull/297">docker/setup-buildx-action#297</a></li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/docker/setup-buildx-action/compare/v3.0.0...v3.1.0">https://github.com/docker/setup-buildx-action/compare/v3.0.0...v3.1.0</a></p> </blockquote> </details> <details> <summary>Commits</summary> <ul> <li><a href="0d103c3126"><code>0d103c3</code></a> Merge pull request <a href="https://redirect.github.com/docker/setup-buildx-action/issues/300">#300</a> from crazy-max/cache-binary</li> <li><a href="f19477aacd"><code>f19477a</code></a> chore: update generated content</li> <li><a href="a4180f835d"><code>a4180f8</code></a> cache-binary input to enable/disable caching binary to GHA cache backend</li> <li><a href="524315340d"><code>5243153</code></a> Merge pull request <a href="https://redirect.github.com/docker/setup-buildx-action/issues/299">#299</a> from docker/dependabot/npm_and_yarn/docker/actions-to...</li> <li><a href="3679a54023"><code>3679a54</code></a> chore: update generated content</li> <li><a href="37a22a2fb2"><code>37a22a2</code></a> build(deps): bump <code>@docker/actions-toolkit</code> from 0.14.0 to 0.17.0</li> <li><a href="65afe610a1"><code>65afe61</code></a> Merge pull request <a href="https://redirect.github.com/docker/setup-buildx-action/issues/297">#297</a> from docker/dependabot/npm_and_yarn/undici-5.28.3</li> <li><a href="fcb8f722fd"><code>fcb8f72</code></a> chore: update generated content</li> <li><a href="f62b9a17c0"><code>f62b9a1</code></a> Merge pull request <a href="https://redirect.github.com/docker/setup-buildx-action/issues/298">#298</a> from crazy-max/bump-gha</li> <li><a href="74c5b717e5"><code>74c5b71</code></a> bump codecov/codecov-action from 3 to 4</li> <li>Additional commits viewable in <a href="f95db51fdd...0d103c3126">compare view</a></li> </ul> </details> <br /> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
266 lines
7.9 KiB
Python
266 lines
7.9 KiB
Python
"""
|
|
This script serializes the entire traffic dump, including websocket traffic,
|
|
as JSON, and either sends it to a URL or writes to a file. The serialization
|
|
format is optimized for Elasticsearch; the script can be used to send all
|
|
captured traffic to Elasticsearch directly.
|
|
|
|
Usage:
|
|
|
|
mitmproxy
|
|
--mode reverse:http://example.com/
|
|
-s examples/complex/jsondump.py
|
|
|
|
Configuration:
|
|
|
|
Send to a URL:
|
|
|
|
cat > ~/.mitmproxy/config.yaml <<EOF
|
|
dump_destination: "https://elastic.search.local/my-index/my-type"
|
|
# Optional Basic auth:
|
|
dump_username: "never-gonna-give-you-up"
|
|
dump_password: "never-gonna-let-you-down"
|
|
# Optional base64 encoding of content fields
|
|
# to store as binary fields in Elasticsearch:
|
|
dump_encodecontent: true
|
|
EOF
|
|
|
|
Dump to a local file:
|
|
|
|
cat > ~/.mitmproxy/config.yaml <<EOF
|
|
dump_destination: "/user/rastley/output.log"
|
|
EOF
|
|
"""
|
|
|
|
import base64
|
|
import json
|
|
import logging
|
|
from queue import Queue
|
|
from threading import Lock
|
|
from threading import Thread
|
|
|
|
import requests
|
|
|
|
from mitmproxy import ctx
|
|
|
|
FILE_WORKERS = 1
|
|
HTTP_WORKERS = 10
|
|
|
|
|
|
class JSONDumper:
|
|
"""
|
|
JSONDumper performs JSON serialization and some extra processing
|
|
for out-of-the-box Elasticsearch support, and then either writes
|
|
the result to a file or sends it to a URL.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.outfile = None
|
|
self.transformations = None
|
|
self.encode = None
|
|
self.url = None
|
|
self.lock = None
|
|
self.auth = None
|
|
self.queue = Queue()
|
|
|
|
def done(self):
|
|
self.queue.join()
|
|
if self.outfile:
|
|
self.outfile.close()
|
|
|
|
fields = {
|
|
"timestamp": (
|
|
("error", "timestamp"),
|
|
("request", "timestamp_start"),
|
|
("request", "timestamp_end"),
|
|
("response", "timestamp_start"),
|
|
("response", "timestamp_end"),
|
|
("client_conn", "timestamp_start"),
|
|
("client_conn", "timestamp_end"),
|
|
("client_conn", "timestamp_tls_setup"),
|
|
("server_conn", "timestamp_start"),
|
|
("server_conn", "timestamp_end"),
|
|
("server_conn", "timestamp_tls_setup"),
|
|
("server_conn", "timestamp_tcp_setup"),
|
|
),
|
|
"ip": (
|
|
("server_conn", "source_address"),
|
|
("server_conn", "ip_address"),
|
|
("server_conn", "address"),
|
|
("client_conn", "address"),
|
|
),
|
|
"ws_messages": (("messages",),),
|
|
"headers": (
|
|
("request", "headers"),
|
|
("response", "headers"),
|
|
),
|
|
"content": (
|
|
("request", "content"),
|
|
("response", "content"),
|
|
),
|
|
}
|
|
|
|
def _init_transformations(self):
|
|
self.transformations = [
|
|
{
|
|
"fields": self.fields["headers"],
|
|
"func": dict,
|
|
},
|
|
{
|
|
"fields": self.fields["timestamp"],
|
|
"func": lambda t: int(t * 1000),
|
|
},
|
|
{
|
|
"fields": self.fields["ip"],
|
|
"func": lambda addr: {
|
|
"host": addr[0].replace("::ffff:", ""),
|
|
"port": addr[1],
|
|
},
|
|
},
|
|
{
|
|
"fields": self.fields["ws_messages"],
|
|
"func": lambda ms: [
|
|
{
|
|
"type": m[0],
|
|
"from_client": m[1],
|
|
"content": base64.b64encode(bytes(m[2], "utf-8"))
|
|
if self.encode
|
|
else m[2],
|
|
"timestamp": int(m[3] * 1000),
|
|
}
|
|
for m in ms
|
|
],
|
|
},
|
|
]
|
|
|
|
if self.encode:
|
|
self.transformations.append(
|
|
{
|
|
"fields": self.fields["content"],
|
|
"func": base64.b64encode,
|
|
}
|
|
)
|
|
|
|
@staticmethod
|
|
def transform_field(obj, path, func):
|
|
"""
|
|
Apply a transformation function `func` to a value
|
|
under the specified `path` in the `obj` dictionary.
|
|
"""
|
|
for key in path[:-1]:
|
|
if not (key in obj and obj[key]):
|
|
return
|
|
obj = obj[key]
|
|
if path[-1] in obj and obj[path[-1]]:
|
|
obj[path[-1]] = func(obj[path[-1]])
|
|
|
|
@classmethod
|
|
def convert_to_strings(cls, obj):
|
|
"""
|
|
Recursively convert all list/dict elements of type `bytes` into strings.
|
|
"""
|
|
if isinstance(obj, dict):
|
|
return {
|
|
cls.convert_to_strings(key): cls.convert_to_strings(value)
|
|
for key, value in obj.items()
|
|
}
|
|
elif isinstance(obj, list) or isinstance(obj, tuple):
|
|
return [cls.convert_to_strings(element) for element in obj]
|
|
elif isinstance(obj, bytes):
|
|
return str(obj)[2:-1]
|
|
return obj
|
|
|
|
def worker(self):
|
|
while True:
|
|
frame = self.queue.get()
|
|
self.dump(frame)
|
|
self.queue.task_done()
|
|
|
|
def dump(self, frame):
|
|
"""
|
|
Transform and dump (write / send) a data frame.
|
|
"""
|
|
for tfm in self.transformations:
|
|
for field in tfm["fields"]:
|
|
self.transform_field(frame, field, tfm["func"])
|
|
frame = self.convert_to_strings(frame)
|
|
|
|
if self.outfile:
|
|
self.lock.acquire()
|
|
self.outfile.write(json.dumps(frame) + "\n")
|
|
self.lock.release()
|
|
else:
|
|
requests.post(self.url, json=frame, auth=(self.auth or None))
|
|
|
|
@staticmethod
|
|
def load(loader):
|
|
"""
|
|
Extra options to be specified in `~/.mitmproxy/config.yaml`.
|
|
"""
|
|
loader.add_option(
|
|
"dump_encodecontent", bool, False, "Encode content as base64."
|
|
)
|
|
loader.add_option(
|
|
"dump_destination",
|
|
str,
|
|
"jsondump.out",
|
|
"Output destination: path to a file or URL.",
|
|
)
|
|
loader.add_option(
|
|
"dump_username", str, "", "Basic auth username for URL destinations."
|
|
)
|
|
loader.add_option(
|
|
"dump_password", str, "", "Basic auth password for URL destinations."
|
|
)
|
|
|
|
def configure(self, _):
|
|
"""
|
|
Determine the destination type and path, initialize the output
|
|
transformation rules.
|
|
"""
|
|
self.encode = ctx.options.dump_encodecontent
|
|
|
|
if ctx.options.dump_destination.startswith("http"):
|
|
self.outfile = None
|
|
self.url = ctx.options.dump_destination
|
|
logging.info("Sending all data frames to %s" % self.url)
|
|
if ctx.options.dump_username and ctx.options.dump_password:
|
|
self.auth = (ctx.options.dump_username, ctx.options.dump_password)
|
|
logging.info("HTTP Basic auth enabled.")
|
|
else:
|
|
self.outfile = open(ctx.options.dump_destination, "a")
|
|
self.url = None
|
|
self.lock = Lock()
|
|
logging.info("Writing all data frames to %s" % ctx.options.dump_destination)
|
|
|
|
self._init_transformations()
|
|
|
|
for i in range(FILE_WORKERS if self.outfile else HTTP_WORKERS):
|
|
t = Thread(target=self.worker)
|
|
t.daemon = True
|
|
t.start()
|
|
|
|
def response(self, flow):
|
|
"""
|
|
Dump request/response pairs.
|
|
"""
|
|
self.queue.put(flow.get_state())
|
|
|
|
def error(self, flow):
|
|
"""
|
|
Dump errors.
|
|
"""
|
|
self.queue.put(flow.get_state())
|
|
|
|
def websocket_end(self, flow):
|
|
"""
|
|
Dump websocket messages once the connection ends.
|
|
|
|
Alternatively, you can replace `websocket_end` with
|
|
`websocket_message` if you want the messages to be
|
|
dumped one at a time with full metadata. Warning:
|
|
this takes up _a lot_ of space.
|
|
"""
|
|
self.queue.put(flow.get_state())
|
|
|
|
|
|
addons = [JSONDumper()] # pylint: disable=invalid-name
|