Skip to content

feat(view): opt in to remote images - #1744

Open
christos-chatzifountas-biotz-io wants to merge 4 commits into
floatpane:masterfrom
christos-chatzifountas-biotz-io:feature/remote-images-1588
Open

christos-chatzifountas-biotz-io wants to merge 4 commits into
floatpane:masterfrom
christos-chatzifountas-biotz-io:feature/remote-images-1588

Conversation

@christos-chatzifountas-biotz-io

@christos-chatzifountas-biotz-io christos-chatzifountas-biotz-io commented Oct 17, 2026 •

Copy link
Copy Markdown

What?

  • Keep HTTP(S) images unfetched when rendering email by default. Embedded CID and data URI images can still display.
  • Add a per-message I action and command-palette entry to load remote images explicitly. The permission resets when a new message view opens and is configurable as email.load_remote_images.
  • Run opted-in remote image downloads in a background command so a slow host does not block the terminal UI.
  • Reject private, loopback, link-local, reserved, and mixed public/private DNS answers. Dial the checked IP address directly to prevent DNS rebinding between validation and connection. Apply the check to each redirect, cap redirects at five, and bypass environment HTTP proxies for these requests.
  • Update image documentation and settings help text. Add tests for opt-in behavior, private destinations, mixed DNS answers, checked-IP dialing, redirects, and the UI loading path.

Why?

Opening an HTML email can currently request its remote images immediately, revealing that the message was opened. A malicious image URL can also target an internal service. Normal reading now makes no remote request; a deliberate load action remains responsive and cannot connect to private network destinations.

Fixes #1588. The destination checks overlap with #1693. This PR performs the check at dial time and connects to the checked address, which also protects against DNS rebinding and redirects.

Verification

  • make lint
  • go test -race ./view ./tui -run 'TestRemoteImage' -count=1 -timeout 90s
  • go test ./... -skip '^(TestConn_SendEvent|TestFetchEmails|TestFetchEmailsWithCustomServer)$' -timeout 90s

The three skipped existing tests require a live mail setup or hang in this environment; they are unchanged by this PR. Remote images will not load through an environment HTTP proxy because proxy-side DNS resolution cannot be pinned to a checked address.

AI-assisted implementation, reviewed and tested locally.

@floatpanebot floatpanebot added area/tui Terminal UI / view layer area/config Configuration / settings area/docs Docs site / README bug Something isn't working documentation Documentation changes enhancement New feature or request question Further information requested size/M Diff: 51–200 lines labels Oct 17, 2026
@floatpanebot

floatpanebot commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Benchmark report — no significant change

Metrics worse: 0 · better: 0 (threshold: ±3%).

benchstat output
goos: linux
goarch: amd64
pkg: github.com/floatpane/matcha/backend
cpu: AMD EPYC 7763 64-Core Processor                
                           │   old.txt    │               new.txt                │
                           │    sec/op    │    sec/op      vs base               │
ParseSearchQuery_Simple-4    2.642µ ± 37%   2.492µ ± 244%        ~ (p=0.589 n=6)
ParseSearchQuery_Complex-4   7.589µ ± 31%   7.618µ ±  61%        ~ (p=0.699 n=6)
TokenizeSearchQuery-4        6.696µ ± 92%   4.516µ ±  30%  -32.55% (p=0.026 n=6)
geomean                      5.120µ         4.409µ         -13.89%

                           │  old.txt   │              new.txt               │
                           │    B/op    │    B/op     vs base                │
ParseSearchQuery_Simple-4    26.00 ± 0%   26.00 ± 0%       ~ (p=1.000 n=6) ¹
ParseSearchQuery_Complex-4   762.0 ± 0%   762.0 ± 0%       ~ (p=1.000 n=6) ¹
TokenizeSearchQuery-4        176.0 ± 0%   176.0 ± 0%       ~ (p=1.000 n=6) ¹
geomean                      151.6        151.6       +0.00%
¹ all samples are equal

                           │  old.txt   │              new.txt               │
                           │ allocs/op  │ allocs/op   vs base                │
ParseSearchQuery_Simple-4    2.000 ± 0%   2.000 ± 0%       ~ (p=1.000 n=6) ¹
ParseSearchQuery_Complex-4   23.00 ± 0%   23.00 ± 0%       ~ (p=1.000 n=6) ¹
TokenizeSearchQuery-4        9.000 ± 0%   9.000 ± 0%       ~ (p=1.000 n=6) ¹
geomean                      7.453        7.453       +0.00%
¹ all samples are equal

pkg: github.com/floatpane/matcha/tui
                    │   old.txt    │              new.txt               │
                    │    sec/op    │    sec/op     vs base              │
LogPanelView-4        163.4µ ±  7%   156.6µ ± 10%       ~ (p=0.132 n=6)
SearchOverlayView-4   170.9µ ±  8%   171.3µ ±  8%       ~ (p=0.937 n=6)
InboxConstruction-4   922.4µ ± 14%   915.0µ ±  7%       ~ (p=1.000 n=6)
geomean               295.4µ         290.6µ        -1.60%

                    │    old.txt    │               new.txt               │
                    │     B/op      │     B/op       vs base              │
LogPanelView-4        44.67Ki ± 51%   44.67Ki ± 51%       ~ (p=1.000 n=6)
SearchOverlayView-4   56.15Ki ± 41%   44.67Ki ± 26%       ~ (p=0.208 n=6)
InboxConstruction-4   874.5Ki ±  0%   874.4Ki ±  0%       ~ (p=0.310 n=6)
geomean               129.9Ki         120.4Ki        -7.34%

                    │   old.txt   │              new.txt              │
                    │  allocs/op  │  allocs/op   vs base              │
LogPanelView-4         714.0 ± 0%    714.0 ± 0%       ~ (p=1.000 n=6)
SearchOverlayView-4    926.0 ± 0%    925.0 ± 0%       ~ (p=0.697 n=6)
InboxConstruction-4   3.483k ± 0%   3.482k ± 0%       ~ (p=0.489 n=6)
geomean               1.321k        1.320k       -0.05%

auto-generated by benchmarks.yml

@floatpanebot floatpanebot added the size/L Diff: 201–800 lines label Oct 17, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Configuration / settings area/docs Docs site / README area/tui Terminal UI / view layer bug Something isn't working documentation Documentation changes enhancement New feature or request question Further information requested size/L Diff: 201–800 lines size/M Diff: 51–200 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FEAT: Block remote tracking pixels in HTML email

2 participants