Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
5dbbd14
reopen main for development
Apr 12, 2026
58370ff
Remove cooldown from pyOpenSSL and our own packages
mhils Apr 13, 2026
00ee4e9
Update pyopenssl requirement from <=25.3.0,>=24.3 to >=24.3,<=26.0.0 …
dependabot[bot] Apr 13, 2026
d332cbf
Update asgiref requirement from <=3.11.0,>=3.2.10 to >=3.2.10,<=3.11.…
dependabot[bot] Apr 13, 2026
c9b063b
Use sys.executable instead of python3 in docs build script (#8183)
optinsoft Apr 19, 2026
a8fbfc7
fix: avoid IndexError in is_mostly_bin for short tails (#8196)
juliosuas Apr 25, 2026
ab470e5
Fix IP blocking and merge additional DoH blocklists (#8197)
Olexandr88 Apr 28, 2026
3dd268a
Bump upper bounds for cryptography and pyOpenSSL (#8219)
reaperhulk May 9, 2026
373d0b8
upgrade the version of pyopenssl in the lockfile (#8223)
alex May 11, 2026
680d219
Bump the github-actions group with 8 updates (#8209)
dependabot[bot] May 11, 2026
96ec8f7
Bump awscli from 1.44.53 to 1.44.63 in the deploy group (#8208)
dependabot[bot] May 11, 2026
a5f8716
Bump the tox group with 2 updates (#8207)
dependabot[bot] May 11, 2026
f478061
Bump pyinstaller-hooks-contrib from 2026.2 to 2026.3 in the pyinstall…
dependabot[bot] May 11, 2026
b464cbf
Copy issuer's SubjectKeyIdentifier into leaf AuthorityKeyIdentifier (…
unique-jakub May 11, 2026
ed564af
Reduce generated certificate validity (#8203)
emanuele-em May 11, 2026
0fb62c4
docs: document addon live-reload + testing pattern (#8210)
ChrisJr404 May 11, 2026
6bc7f0a
Rename mitmweb XSRF cookie to `_mitmproxy_xsrf` to recover broken Fir…
Copilot May 12, 2026
f9c2b44
Update CHANGELOG.md
mhils May 12, 2026
6c09d56
mitmproxy 12.2.3
May 12, 2026
e875daf
reopen main for development
May 12, 2026
b9958b2
Bump requests from 2.32.5 to 2.33.1 (#8230)
dependabot[bot] May 12, 2026
2f6f251
Bump awscli from 1.44.63 to 1.44.69 in the deploy group (#8228)
dependabot[bot] May 12, 2026
48e58be
Bump types-requests from 2.32.4.20260107 to 2.33.0.20260327 in the my…
dependabot[bot] May 12, 2026
e33503d
Web: Remove Bootstrap dependency and keep mitmweb styling parity (#8147)
lups2000 May 12, 2026
e7ad119
Update urwid requirement from <=3.0.5,>=2.6.14 to >=2.6.14,<=4.0.0 (#…
dependabot[bot] May 12, 2026
fb8c1a9
Bump the tox group across 1 directory with 2 updates (#8226)
dependabot[bot] May 12, 2026
e20a24b
fix(web): render AVIF images and IANA icon favicons in the response t…
ariel42 May 15, 2026
a4518ab
fix(web): avoid infinite componentDidUpdate loop in FlowTable (#8233)
ariel42 May 15, 2026
782af06
fix(web): preserve in-progress filter input across parent re-renders …
ariel42 May 16, 2026
12a292c
Treat carriage return as whitespace in strutils.is_xml (#8243)
ADiTyaRaj8969 May 21, 2026
d19c601
Merge upstream/main into citusdata/main
ihalatci May 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ updates:
directory: "/"
cooldown:
default-days: 42
exclude:
- "pyopenssl"
- "cryptography"
- "mitmproxy*"
schedule:
interval: "monthly"
open-pull-requests-limit: 10
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/autofix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:

- run: web/gen/all

- uses: actions/setup-node@v6
- uses: actions/setup-node@v6.3.0
with:
node-version-file: .github/node-version.txt
- run: npm ci
Expand Down
18 changes: 9 additions & 9 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
- if: runner.os == 'macOS' && github.repository == 'mitmproxy/mitmproxy'
&& (startsWith(github.ref, 'refs/heads/') || startsWith(github.ref, 'refs/tags/'))
id: keychain
uses: apple-actions/import-codesign-certs@b610f78488812c1e56b20e6df63ec42d833f2d14
uses: apple-actions/import-codesign-certs@fe74d46e82474f87e1ba79832ad28a4013d0e33a
with:
keychain: ${{ runner.temp }}/temp
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
Expand Down Expand Up @@ -153,11 +153,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: mhils/workflows/checkout@8fe88b311a66c441e01edfebe4cd90d8a47fa335
- uses: actions/setup-node@v6
- uses: actions/setup-node@v6.3.0
with:
node-version-file: .github/node-version.txt
- name: Cache Node.js modules
uses: actions/cache@v5
uses: actions/cache@v5.0.4
with:
# npm cache files are stored in `~/.npm` on Linux/macOS
path: ~/.npm
Expand Down Expand Up @@ -250,24 +250,24 @@ jobs:
with:
name: binaries.wheel
path: release/docker
- uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v1.6.0
- uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0
- uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v1.6.0

- name: Login to Docker Hub
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
username: mitmbot
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GitHub Container Registry
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Docker meta
id: meta
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
env:
DOCKER_METADATA_ANNOTATIONS_LEVELS: index
with:
Expand All @@ -283,7 +283,7 @@ jobs:

- name: Build and push
id: push
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0
with:
context: release/docker
platforms: linux/amd64,linux/arm64
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- uses: actions/checkout@v6
with:
token: ${{ secrets.GH_PUSH_TOKEN }} # this token works to push to the protected main branch.
- uses: actions/setup-node@v6
- uses: actions/setup-node@v6.3.0
with:
node-version-file: .github/node-version.txt
- uses: mhils/workflows/setup-python@8fe88b311a66c441e01edfebe4cd90d8a47fa335
Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,29 @@

## Unreleased: mitmproxy next

- Fix contentview detection for XML files that start with CRLF.
([#8243](https://github.com/mitmproxy/mitmproxy/pull/8243), @ADiTyaRaj8969)
- mitmweb: Fix the filter input losing half-typed text on unrelated parent re-renders.
([#8234](https://github.com/mitmproxy/mitmproxy/pull/8234), @ariel42)
- mitmweb: Fix an infinite update cycle in `FlowTable` by only recomputing the virtual-scroll window in `componentDidUpdate` when `flowView` or `rowHeight` actually change.
([#8233](https://github.com/mitmproxy/mitmproxy/pull/8233), @ariel42)
- mitmweb: Fix AVIF images and `image/vnd.microsoft.icon` favicons not rendering in the response tab.
([#8232](https://github.com/mitmproxy/mitmproxy/pull/8232), @ariel42)

## 12 May 2026: mitmproxy 12.2.3

- Reduce generated leaf certificate validity from 199 to 197 days so the 2-day
`notBefore` backdate remains below Chromium's 200-day limit.
([#8203](https://github.com/mitmproxy/mitmproxy/pull/8203), @emanuele-em)
- Fixed a bug where mitmweb would not pick up its XSRF cookie.
([#8224](https://github.com/mitmproxy/mitmproxy/pull/8224), @mhils)
- Fix `authority and subject key identifier mismatch` errors when mitmproxy
is configured with a custom CA whose SubjectKeyIdentifier was not derived
as SHA-1 of the public key.
([#8214](https://github.com/mitmproxy/mitmproxy/pull/8214), @unique-jakub)
- Fix `IndexError` in `is_mostly_bin` when exporting flows to HAR with payloads
that have a UTF-8 continuation byte at the 100-byte cutoff.
([#8196](https://github.com/mitmproxy/mitmproxy/pull/8196), @juliosuas)

## 12 April 2026: mitmproxy 12.2.2

Expand Down
5 changes: 4 additions & 1 deletion docs/build.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
#!/usr/bin/env python3
import shutil
import subprocess
import sys
from pathlib import Path

here = Path(__file__).parent

for script in sorted((here / "scripts").glob("*.py")):
print(f"Generating output for {script.name}...")
out = subprocess.check_output(["python3", script.absolute()], cwd=here, text=True)
out = subprocess.check_output(
[sys.executable, script.absolute()], cwd=here, text=True
)
if out:
(here / "src" / "generated" / f"{script.stem}.html").write_text(
out, encoding="utf8"
Expand Down
25 changes: 25 additions & 0 deletions docs/src/content/addons/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,28 @@ This lets us place event handler functions in the module scope.
For instance, here is a complete script that adds a header to every request:

{{< example src="examples/addons/anatomy2.py" lang="py" >}}

# Developing Addons

## Live Reloading

Scripts loaded with `-s path/to/script.py` are watched for changes.
Whenever the file's modification time changes, mitmproxy unregisters the
old module, re-imports the file, and re-registers the new addon — without
restarting the proxy or losing the state of any other addons or in-flight
flows. This means you can edit your addon in your editor and the changes
take effect on the next save (within roughly one second).

Errors raised at import time, in `configure`, or in `running` are logged
to the event log and the previous version of the addon is left
unregistered. Fix the error and save the file again to retry. Errors
raised inside event handlers (`request`, `response`, …) are logged but do
not unload the addon.

## Testing Addons

Because addons are regular Python files, the easiest way to unit-test
them is to import the module from your test, instantiate the addon, and
call the event handler directly. For more complex testing needs,
see [`test/mitmproxy/addons`](https://github.com/mitmproxy/mitmproxy/tree/main/test/mitmproxy/addons)
(but please note that internal testing helpers have no guaranteed stable API).
6 changes: 3 additions & 3 deletions examples/contrib/block_dns_over_https.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@
doh_hostnames, doh_ips = default_blocklist["hostnames"], default_blocklist["ips"]

# convert to sets for faster lookups
doh_hostnames = set(doh_hostnames)
doh_ips = set(doh_ips)
doh_hostnames = set(doh_hostnames) | set(additional_doh_names)
doh_ips = set(doh_ips) | set(additional_doh_ips)


def _has_dns_message_content_type(flow):
Expand Down Expand Up @@ -335,7 +335,7 @@ def _requested_hostname_is_in_doh_blocklist(flow):
:return: True if server's hostname is in DoH blocklist, otherwise False
"""
hostname = flow.request.host
ip = flow.server_conn.address
ip = flow.server_conn.address[0]
return hostname in doh_hostnames or hostname in doh_ips or ip in doh_ips


Expand Down
40 changes: 30 additions & 10 deletions mitmproxy/certs.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,14 @@
logger = logging.getLogger(__name__)

# Default expiry must not be too long: https://github.com/mitmproxy/mitmproxy/issues/815
# Note that expiry will be offset by `CERT_VALIDITY_OFFSET`, i.e. the cert will be
# backdated a bit to account for clients with incorrect clocks.
CA_EXPIRY = datetime.timedelta(days=10 * 365)
CERT_EXPIRY = datetime.timedelta(days=199)
CRL_EXPIRY = datetime.timedelta(days=7)

CERT_VALIDITY_OFFSET = datetime.timedelta(days=-2)

# Generated with "openssl dhparam". It's too slow to generate this on startup.
DEFAULT_DHPARAM = b"""
-----BEGIN DH PARAMETERS-----
Expand Down Expand Up @@ -245,8 +249,8 @@ def create_ca(
builder = x509.CertificateBuilder()
builder = builder.serial_number(x509.random_serial_number())
builder = builder.subject_name(name)
builder = builder.not_valid_before(now - datetime.timedelta(days=2))
builder = builder.not_valid_after(now + CA_EXPIRY)
builder = builder.not_valid_before(now + CERT_VALIDITY_OFFSET)
builder = builder.not_valid_after(now + CERT_VALIDITY_OFFSET + CA_EXPIRY)
builder = builder.issuer_name(name)
builder = builder.public_key(private_key.public_key())
builder = builder.add_extension(
Expand Down Expand Up @@ -335,8 +339,8 @@ def dummy_cert(
builder = builder.public_key(cacert.public_key())

now = datetime.datetime.now()
builder = builder.not_valid_before(now - datetime.timedelta(days=2))
builder = builder.not_valid_after(now + CERT_EXPIRY)
builder = builder.not_valid_before(now + CERT_VALIDITY_OFFSET)
builder = builder.not_valid_after(now + CERT_VALIDITY_OFFSET + CERT_EXPIRY)

subject = []
is_valid_commonname = commonname is not None and len(commonname) < 64
Expand All @@ -356,10 +360,26 @@ def dummy_cert(
)

# https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.1
builder = builder.add_extension(
x509.AuthorityKeyIdentifier.from_issuer_public_key(cacert.public_key()), # type: ignore
critical=False,
)
# Per RFC 5280 §4.2.1.2, the AKI's keyIdentifier in a child certificate
# MUST be byte-equal to the issuer's stored SubjectKeyIdentifier. If we
# recompute it from the public key with `from_issuer_public_key()`, we
# always derive a SHA-1 digest, which mismatches whenever the issuer's
# SKI was generated by a different method (RFC 7093 truncated
# SHA-256/384/512, hardware-rooted CAs, or any custom value). This
# mismatch is rejected by strict TLS chain builders (`X509_V_FLAG_X509_STRICT`,
# Python `ssl`, Go `crypto/x509`) with "authority and subject key
# identifier mismatch". Most notably, cert-manager >=1.18 / Go >=1.25
# default to truncated SHA-256 SKIs for FIPS 140-3 compliance.
try:
issuer_ski = cacert.extensions.get_extension_for_class(
x509.SubjectKeyIdentifier
).value
aki = x509.AuthorityKeyIdentifier.from_issuer_subject_key_identifier(issuer_ski)
except x509.ExtensionNotFound:
# Fall back when the issuer cert has no SKI extension at all (legacy
# CAs predating RFC 5280 conformance).
aki = x509.AuthorityKeyIdentifier.from_issuer_public_key(cacert.public_key()) # type: ignore
builder = builder.add_extension(aki, critical=False)
# If CA and leaf cert have the same Subject Key Identifier, SChannel breaks in funny ways,
# see https://github.com/mitmproxy/mitmproxy/issues/6494.
# https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.2 states
Expand Down Expand Up @@ -400,8 +420,8 @@ def dummy_crl(
builder = builder.issuer_name(cacert.issuer)

now = datetime.datetime.now()
builder = builder.last_update(now - datetime.timedelta(days=2))
builder = builder.next_update(now + CRL_EXPIRY)
builder = builder.last_update(now + CERT_VALIDITY_OFFSET)
builder = builder.next_update(now + CERT_VALIDITY_OFFSET + CRL_EXPIRY)

builder = builder.add_extension(x509.CRLNumber(1000), False) # meaningless number
crl = builder.sign(private_key=privkey, algorithm=hashes.SHA256())
Expand Down
3 changes: 3 additions & 0 deletions mitmproxy/tools/web/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,9 @@ def __init__(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
xsrf_cookies=True,
# https://github.com/mitmproxy/mitmproxy/issues/8194
# 2026-05: We can move back to the default cookie name in a few years.
xsrf_cookie_name="_mitmproxy_xsrf",
xsrf_cookie_kwargs=dict(samesite="Strict"),
cookie_secret=secrets.token_bytes(32),
debug=debug,
Expand Down
2 changes: 1 addition & 1 deletion mitmproxy/tools/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<title>mitmproxy</title>
<link rel="icon" href="static/favicon.ico" type="image/x-icon" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script type="module" crossorigin src="./static/index-D7g-N5jK.js"></script>
<script type="module" crossorigin src="./static/index-Be7e-cwP.js"></script>
<link rel="modulepreload" crossorigin href="./static/vendor-BS4xPthR.js">
<link rel="stylesheet" crossorigin href="./static/vendor-Cg3S-P9H.css">
<link rel="stylesheet" crossorigin href="./static/index-DhPPoJ7G.css">
Expand Down

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions mitmproxy/utils/strutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def is_mostly_bin(s: bytes) -> bool:
# Cut off at ~100 chars, but do it smartly so that if the input is UTF-8, we don't
# chop a multibyte code point in half.
if len(s) > 100:
for cut in range(100, 104):
for cut in range(100, min(104, len(s))):
is_continuation_byte = (s[cut] >> 6) == 0b10
if not is_continuation_byte:
# A new character starts here, so we cut off just before that.
Expand Down Expand Up @@ -163,8 +163,10 @@ def is_mostly_bin(s: bytes) -> bool:


def is_xml(s: bytes) -> bool:
# XML 1.0 §2.3 defines whitespace as (#x20 | #x9 | #xD | #xA), so a
# leading \r before "<" should also be skipped here.
for char in s:
if char in (9, 10, 32): # is space?
if char in (9, 10, 13, 32): # is whitespace?
continue
return char == 60 # is a "<"?
return False
Expand Down
2 changes: 1 addition & 1 deletion mitmproxy/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import subprocess
import sys

VERSION = "12.2.2"
VERSION = "13.0.0.dev"
MITMPROXY = "mitmproxy " + VERSION

# Serialization format version. This is displayed nowhere, it just needs to be incremented by one
Expand Down
20 changes: 10 additions & 10 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ classifiers = [
dependencies = [
"aioquic>=1.2.0,<=1.2.0",
"argon2-cffi>=23.1.0,<=25.1.0",
"asgiref>=3.2.10,<=3.11.0",
"asgiref>=3.2.10,<=3.11.1",
"bcrypt>=5.0.0,<=5.0.0",
"Brotli>=1.0,<=1.2.0",
"certifi>=2019.9.11", # no upper bound here to get latest CA bundle
"cryptography>=42.0,<=46.1", # relaxed upper bound here to get security fixes
"cryptography>=42.0,<=48.1", # relaxed upper bound here to get security fixes
"flask>=3.0,<=3.1.3",
"h11>=0.16.0,<=0.16.0",
"h2>=4.3.0,<=4.3.0",
Expand All @@ -47,14 +47,14 @@ dependencies = [
"mitmproxy_rs>=0.12.6,<0.13", # relaxed upper bound here: we control this
"msgpack>=1.0.0,<=1.1.2",
"pydivert>=2.0.3,<=2.1.0; sys_platform == 'win32'",
"pyOpenSSL>=24.3,<=25.3.0",
"pyOpenSSL>=24.3,<=27.0.0",
"pyparsing>=2.4.2,<=3.3.2",
"pyperclip>=1.9.0,<=1.11.0",
"ruamel.yaml>=0.18.10,<=0.19.1",
"sortedcontainers>=2.3,<=2.4.0",
"tornado>=6.5.0,<=6.5.5",
"typing-extensions>=4.13.2,<=4.14; python_version < '3.13'",
"urwid>=2.6.14,<=3.0.5",
"urwid>=2.6.14,<=4.0.0",
"wsproto>=1.0,<=1.3.2",
"publicsuffix2>=2.20190812,<=2.20191221",
"zstandard>=0.25,<=0.25.0",
Expand All @@ -67,29 +67,29 @@ dev = [
"maturin==1.12.6",
"pdoc==16.0.0",
"pyinstaller==6.19.0",
"pyinstaller-hooks-contrib==2026.2",
"pyinstaller-hooks-contrib==2026.3",
"pytest-asyncio==1.2.0",
"pytest-cov==7.0.0",
"pytest-timeout==2.4.0",
"pytest-xdist==3.8.0",
"pytest==8.4.2",
"requests==2.32.5",
"requests==2.33.1",
"wheel==0.46.3",
"build==1.4.2",
"mypy==1.19.1",
"types-requests==2.32.4.20260107",
"types-requests==2.33.0.20260327",
{include-group = "tox"},
{include-group = "ruff"},
]
tox = [
"tox==4.49.0",
"tox-uv==1.33.1",
"tox==4.52.0",
"tox-uv==1.34.0",
]
ruff = [
"ruff==0.15.8",
]
deploy = [
"awscli==1.44.53",
"awscli==1.44.69",
"twine==6.2.0",
]

Expand Down
Loading
Loading