* fix(web): theme the sorted flow table header
The sorted column header and its chevron mask were the only colors left as literals after the semantic token layer landed, because LESS `lighten`/`fadeout` cannot operate on `var()`.
In dark mode this painted the sorted `th` near-white and hid the chevron, which is stroked in `--mitmweb-fg`, against its own mask.
Both now read `--mitmweb-th-sorted-bg`, with the mask deriving its alpha through `color-mix` instead.
The light value is byte-equal to what `lighten(#f2f2f2, 3%)` produced, so the light theme is unchanged.
No LESS color functions remain in the stylesheets, so the note about them in the token header comment goes away too.
* docs: add changelog entry for the sorted flow table header fix
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* feat(web): add dark theme and web_theme option
Add a `web_theme` mitmweb option (system/dark/light; default system) and apply it in the frontend by setting `data-theme` on the document root.
`system` follows the OS `prefers-color-scheme` and tracks live changes.
The dark palette overrides the semantic color tokens under `[data-theme="dark"]`, with targeted overrides for surfaces whose colors are consumed by LESS functions.
The CodeMirror content editor switches to the one-dark theme in dark mode.
Part of #7789.
* fix(web): theme native form controls for dark mode
Native selects, inputs, checkboxes and scrollbars ignored the semantic tokens and rendered with the browser's light defaults.
Set color-scheme per theme so they follow it, and replace the remaining literal black text colors on the local-applications input and the close-button hover states with tokens.
* fix(web): make search and close icons legible in dark mode
The search filter icon and the eventlog/flow-detail close icons were hardcoded to black/grey, which is hard to read on dark surfaces.
Route them through the foreground tokens instead.
* [autofix.ci] apply automated fixes
* fix(web): theme header and tab separators for dark mode
The header, nav-tab, and menu-group separator borders were computed with lighten(grey, ...), leaving them as bright literals that stayed light-grey on dark surfaces.
Route them through the border tokens instead so they follow the theme.
* feat(web): add a theme selector to the Options menu
The web_theme option was only reachable by scrolling to the bottom of the raw options list in the Edit Options modal.
Surface it as an Appearance dropdown (system/dark/light) in the header Options menu so the theme is discoverable and switchable in one click.
* refactor(web): drop the dark flow table rule override
The base branch tokenized every flow table row color, so the nested .flow-table tr block in the dark theme now duplicates rules that var() already resolves.
Override the --mitmweb-row-* tokens instead.
Base rows fall through to --mitmweb-bg / --mitmweb-bg-alt like they do in the light theme, rather than carrying their own near-identical literals.
* fix(web): give row hover a dark highlight color
--mitmweb-highlight kept its light value in dark mode, compositing a pale blue over the dark surface and leaving hovered header rows and command suggestions hard to read.
* fix(web): darken the first-line banner in dark mode
The request/response first line reused --mitmweb-accent, which is brightened for dark surfaces and left white monospace text at roughly 2.5:1 contrast.
Give the banner its own token pair so dark mode can use a deep blue with pale text without dragging the accent color along.
* fix(web): make status codes legible in dark mode
The status column painted itself with hardcoded CSS color names.
`darkgreen` and `darkred` are near-invisible against the dark surface, so route the colors through per-class tokens instead.
Light values are byte-equal to the literals they replace.
The shadow token added alongside them is consumed in the next commit.
* fix(web): stop footers casting a light halo in dark mode
Both footer shadows were hardcoded to a light gray, which on a dark surface reads as a thick white border rather than a shadow.
Route them through the shadow token so the dark theme casts black instead.
* fix(web): dim OS-supplied app icons in dark mode
The executable icons in the local-applications dropdown come from the OS as bitmaps, so we cannot recolor them.
macOS hands back a plain white square for the generic executable, which glares against the dark popover.
Dim them via a token so the white square reads as grey while colored app icons stay recognizable.
* fix(web): stop idle scrollbars showing as stray lines in dark mode
Panes such as the flow table reserve a scrollbar gutter permanently via `overflow-y: scroll`.
Under `color-scheme: dark` the UA paints that gutter light, so an idle scrollbar reads as a stray vertical line beside the pane divider rather than as a scrollbar.
Color the track to match the surface so it stays as invisible as it already is on light backgrounds.
* fix(web): blend the process-filter input into its dark header
Under color-scheme: dark the browser paints the native text field a lighter grey than the transparent dropdown header around it, so the input read lighter than the chevron beside it.
Making the field background transparent lets it blend with the header.
* fix(web): make the filter-docs row hover visible in dark mode
The dark highlight token was barely distinguishable from the popover surface, so a hovered suggestion row read as unhighlighted.
Brightening it makes the hovered row stand out.
The command-bar suggestion highlight shares this token and gains the same visibility.
* test(web): cover theme selector and status column colors
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* feat(web): add semantic CSS custom-property color layer
Introduce a :root set of semantic color tokens (surfaces, text, borders, accent, state) in global.less and route the recurring, directly-used colors across the LESS files through them.
Light-mode values are byte-equal to the literals they replace, so this is a visual no-op; it exists to give a future dark theme a single place to override.
Colors that LESS functions (lighten/darken/fadeout) consume, and one-off decorative colors, are intentionally left as literals because those functions cannot operate on var().
Part of #7789.
* [autofix.ci] apply automated fixes
* refactor(web): route remaining hardcoded colors through color tokens
Route the remaining directly-used background, text, and border literals across the LESS files through the existing semantic tokens introduced in the color layer, reusing tokens rather than adding new ones.
Light-mode colors stay visually unchanged; one-off decorative colors and LESS-function inputs are left as literals.
* refactor(web): tokenize recurring colors and drop dead styles
Add a --mitmweb-highlight token for the shared row-hover / suggestion-selection color (used in header.less and command.less) and a --mitmweb-gray token for the neutral gray reused across mode.less.
Remove the unused .command-suggestion rule and the dead rgba() border fallback in dropdown.less, which was overriding the tokenized border and rendering as translucent black.
* refactor(web): use the accent token for the first line and special tab
Replace the near-accent literals #428bca (flow detail first line) and #396cad (special tab) with --mitmweb-accent so the accent lives in exactly one place.
Both were within a few percent of the token, so this is a small deliberate light-theme shift rather than a visual no-op.
The special tab derived its hover from lighten(@special-color, 10%); since LESS functions cannot operate on var(), it now uses --mitmweb-accent-active, the same hover shade .btn-primary already uses.
* refactor(web): tokenize the mode green left border
Add a --mitmweb-green token for the #77c77a left border in mode.less, mirroring the --mitmweb-gray token already used by the neighbouring .gray-left-border.
* refactor(web): tokenize flow table row state colors
Add --mitmweb-row-* tokens for the highlighted, selected, and selected+highlighted row shades and route flowtable.less through them.
The base rows reuse the existing --mitmweb-bg / --mitmweb-bg-alt surfaces, whose values are byte-equal to the hsl() literals they replace.
Values are unchanged, so this is a visual no-op; it gives the dark theme one place to override the row states.
* docs: drop the changelog entry for the CSS color token layer
The entry conflicts with the upstream CHANGELOG.md on the PR.
Removing it for now; the entry will be re-added once the branch is rebased.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
If allow_hosts or ignore_hosts was set, any QUIC connections would stall
forever after TLS handshake and never get assigned an HttpLayer or e.g.
appear in the flow list in mitmweb. The codepath goes like this:
1. After client TLS handshake completes, next layer hook is invoked with
data_client = b'', i.e. the data so far inside the QUIC stream
2. _next_layer first checks using _ignore_connection
3. _ignore_connection uses _get_client_hello, passing b'' data_client
4. quick_parse_client_hello_from_datagram raises a type of ValueError
because b'' is definitely not a valid QUIC packet
5. ValueError is skipped, so dtls_parse_client_hello is tried, which
also raises a type of ValueError for about the same reason
6. This is now converted into NeedsMoreData, which bails out of the
entire _next_layer process.
This repeats each time next_layer is called and never makes progress,
even if data is read on the QUIC stream, it's still not going to be a
valid QUIC or DTLS packet.
This fix matches the tcp case and only tries to parse the packet as quic
or dtls if it heuristically looks like that.
* proxy: bracket IPv6 literals in upstream CONNECT authority
When mitmproxy runs in upstream mode and the target server is an IPv6
literal, the CONNECT request and Host header sent to the upstream proxy
built the authority as `host:port` without bracketing the address, e.g.
`CONNECT 2001:db8::1:443` / `Host: 2001:db8::1:443`. That is a malformed
authority per RFC 3986 §3.2.2 / RFC 7230 §5.4, and downstream proxies
(e.g. Burp) reject it as an unparseable CONNECT target.
Bracket IPv6 literals so the authority is `[2001:db8::1]:443`. Hostnames
(still IDNA-encoded) and IPv4 addresses are unchanged.
Closes#8309
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* fix(web): avoid infinite componentDidUpdate loop in the event log
EventList.componentDidUpdate called this.onViewportUpdate() on every
update, even when the inputs to calcVScroll (the event list and the row
height) had not changed. onViewportUpdate reads the current scroll
position, recomputes vScroll, and setStates when the window shape
changes. Because the event log measures per-row heights, a setState it
produced could feed itself: rendering a new window remeasured rows,
which shifted the computed window, which setState again, so the cycle
never converged and surfaced as "Maximum update depth exceeded" while
scrolling the log.
Gate the call on prevProps.events !== this.props.events ||
prevProps.rowHeight !== this.props.rowHeight, mirroring the FlowTable fix
in #8233. The other call sites (componentDidMount, the resize listener,
the viewport onScroll, and setHeight when a row is first measured) still
drive updates as needed, so scrolling and autoscroll keep working.
Adds a regression test that fails without the gate.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
The msgpack contentview was rewritten in Rust and moved to mitmproxy_rs
in #7623, which deleted the only import of the msgpack package. The
pyproject entry survived, and the dependency-bump automation has been
dutifully maintaining the dead dependency ever since, most recently
resolving msgpack 1.1.2 in uv.lock. Dropping it removes a C-extension
wheel from every install, and users lose nothing because the Rust
contentview still provides msgpack rendering.
Discovered using `pyproject-udeps` [[1]].
[1]: https://github.com/lukehsiao/pyproject-udeps
Fixes: d0a35dcf9b ("introduce new contentview API (#7623)")
* mitmweb: honor view_order_reversed for live flows
mitmweb only applied view_order_reversed to the initial flow list it
fetches over HTTP. Flows delivered afterwards over the WebSocket were
always appended to the end of the table, so with the option enabled the
newest flow stayed off-screen at the bottom instead of appearing on top.
Track the option in the flows reducer and, when no column sort is active,
insert newly added flows at the front when view_order_reversed is set and
at the back otherwise. This matches the order the server already uses for
the initial list.
Closes#5520
* [autofix.ci] apply automated fixes
* mitmweb: destructure orderReversed in flows reducer for consistency
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* Treat carriage return as whitespace in strutils.is_xml
is_xml() skipped only tab, LF and space before checking for the
opening "<", so a response body that started with a leading CR
or CRLF (which is what HTTP frames look like, and what some
Windows-side XML producers emit) was not detected as XML.
The XML/HTML content view's auto-detection score then dropped
to 0 instead of the usual 0.4 for those bodies.
XML 1.0 §2.3 defines whitespace as (#x20 | #x9 | #xD | #xA), so
\r (0x0D) belongs in the skip set alongside the other three.
Adds the missing byte plus three assertions in the existing
test_is_xml: \r<foo and \r\n<foo are now recognised, and a
sanity check that \r\nfoo is still rejected.
* [autofix.ci] apply automated fixes
* simplify wording
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
* fix(web): preserve in-progress filter input across parent re-renders
`FilterInput`'s `onChange` only propagates valid filters to the parent,
so while the user is mid-typing an incomplete or invalid filter (e.g.
`~foo bar`) the local `state.value` intentionally diverges from
`props.value` — the parent still holds the last valid filter. Any
unrelated parent re-render then fires
`UNSAFE_componentWillReceiveProps`, which unconditionally called
`this.setState({ value: nextProps.value })` and clobbered the user's
in-progress text back to the last valid filter.
Gated the sync on `nextProps.value !== this.props.value` so the
component only adopts an externally-changed value. When the parent
re-renders without changing the value prop, the local state is
preserved.
Added a `FilterInputSpec.tsx` regression test alongside the existing
`should handle componentWillReceiveProps` test (which covers the
positive path — external value change still wins). The new test types
`~foo bar` (asserted invalid by the existing `should handle isValid`
test), rerenders with the same value prop, and asserts the in-progress
text survives. The test fails without the fix.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
* fix(web): avoid infinite componentDidUpdate loop in FlowTable
`FlowTable.componentDidUpdate` calls `this.onViewportUpdate()` on every
update, even when the inputs to `calcVScroll` (the flow list and the row
height) did not change. `onViewportUpdate` reads the current
`viewport.scrollTop`, recomputes `vScroll`, and `setState`s when
`state.viewportTop !== scrollTop` or `vScroll` shape changed. The new
`state.viewportTop` is `Math.min(scrollTop, vScroll.end * rowHeight)`,
so when `vScroll.end * rowHeight < scrollTop` the new state stays
strictly below `scrollTop`, the inequality holds on the next iteration,
and the cycle spins until the browser kills the JS thread.
Gated the call on `prevProps.flowView !== this.props.flowView ||
prevProps.rowHeight !== this.props.rowHeight`. The other
`onViewportUpdate` call sites — `componentDidMount`, the window resize
listener, the viewport `onScroll` handler, and the post-scroll-into-view
call — are unchanged, so user scrolling and resizing still drive the
virtual-scroll window normally.
Added a `FlowTableSpec.tsx` regression test that spies on
`PureFlowTable.prototype.onViewportUpdate` and asserts it is NOT called
after a `select` dispatch (which changes connect-mapped props but leaves
`flowView` and `rowHeight` alone). The test fails without the fix.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Maximilian Hils <git@maximilianhils.com>
* fix(web): render AVIF images and IANA icon favicons in the response tab
The `ViewImage` regex in `HttpMessage.tsx` rejected two valid image
content types, so the response tab did not render them inline:
1. `image/avif` was missing from the alternation. AVIF is widely served
by image CDNs and decoded natively in `<img>` by Chrome 85+,
Firefox 93+, and Safari 16.1+.
2. `image/vnd.microsoft.icon` (the IANA-canonical `.ico` MIME) never
matched because the alternation had `vnc.microsoft.icon` — a
`vnc`/`vnd` typo. The de-facto legacy `image/x-icon` continued to
work, which made the bug easy to miss. The dots are also unescaped,
so the regex coincidentally matches `image/vncXmicrosoftXicon` for
any single character `X`.
Added `avif`, corrected `vnc` → `vnd`, and escaped the dots. Added a
`ViewImage.matches` unit test covering both the additions and the
existing image MIME types.
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
* migrate bootstrap decoupling with dual classes and local less tokens
* migrate form and utility bootstrap classes to m-/u- styles
* remove remaining Bootstrap class aliases and migrate to internal m-* / u-* classes
* fix tab link colors to match previous blue styling
* update snapshots
* adjust table styling and alignment
* adjust divider style
* adjust flowdetails panel style
* adjust options modal style
* fix icons position and color in capture tab page
* adjust style search,highlight, intercept popovers
* update snapshots
* extract global styles into global.less
* keep behavior aligned with pre-migration Bootstrap (same class names)
* update snapshots
* [autofix.ci] apply automated fixes
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>