From dd1621c0077e079e0693f16fe83f17d50338216e Mon Sep 17 00:00:00 2001 From: srdusr <99972264+srdusr@users.noreply.github.com> Date: Fri, 15 May 2026 19:48:00 +0200 Subject: Stop mislabeling truncated captures as "exact" internal/proxy/tee.go's teeConn silently drops bytes past maxCaptureBytes (10 MiB) but callers unconditionally marked the result "exact" anyway. Confirmed live: proxying a 15 MiB response worked correctly end-to-end (the client got the full, real 15 MiB - proxying itself is unbounded, only storage is capped), but the stored history entry was exactly 10485760 bytes with response_exact=1 still set. For a tool whose core value proposition is "raw bytes are the source of truth," a silently truncated capture presented as complete could hide the very evidence a smuggling or parser-differential investigation is looking for in the tail of a large body - and give false confidence that it isn't there. teeConn.Take() now returns (data, truncated) instead of just data; truncated is true whenever a Read had to drop bytes because the buffer was already at cap. Every caller (proxy.go's forward() on both the request and response side, repeat.go's sendRaw for Repeater/Intruder) now folds truncated into exact - a truncated capture is never marked exact - and additionally threads a distinct RequestTruncated/ ResponseTruncated bool through store.Entry, ipc.EntryDetail, and the TUI, since "truncated" and "reconstructed" (HTTP/2, which never had wire-exact bytes to begin with) are different situations worth telling apart: a truncated capture is still real wire bytes, just incomplete, not a synthesized reconstruction. The detail/repeater views now show "truncated (hit capture size limit)" specifically rather than lumping it in with "reconstructed", which would have implied more transformation happened than actually did. Schema: history gains request_truncated/response_truncated columns via the same ALTER-TABLE-and-ignore-duplicate-column pattern already used for source/flagged, so existing databases upgrade in place. Verified live: a target server returning a 15 MiB body (over the 10 MiB cap) proxied through cleanly - full body reached the client - while the stored entry shows length=10485760, response_exact=0, response_truncated=1 (previously would have shown response_exact=1); the TUI's Detail view correctly displays "Response (10485760 bytes, truncated (hit capture size limit))" instead of "exact". go build/vet/gofmt/test/mod tidy all clean. --- internal/ipc/ipc.go | 7 +++++++ internal/ipc/server.go | 1 + 2 files changed, 8 insertions(+) (limited to 'internal/ipc') diff --git a/internal/ipc/ipc.go b/internal/ipc/ipc.go index 270b8e4..5dac21f 100644 --- a/internal/ipc/ipc.go +++ b/internal/ipc/ipc.go @@ -100,6 +100,13 @@ type EntryDetail struct { ResponseRaw []byte `json:"response_raw"` RequestExact bool `json:"request_exact"` ResponseExact bool `json:"response_exact"` + // *Truncated is true when the matching *Exact is false specifically + // because capture hit its size cap and dropped bytes off the end - + // as opposed to false because there was never a wire-exact + // representation to begin with (HTTP/2). Only meaningful alongside + // a false *Exact. + RequestTruncated bool `json:"request_truncated,omitempty"` + ResponseTruncated bool `json:"response_truncated,omitempty"` } // Client talks to a mitmuxd instance for request/response queries diff --git a/internal/ipc/server.go b/internal/ipc/server.go index 94073da..2879b24 100644 --- a/internal/ipc/server.go +++ b/internal/ipc/server.go @@ -312,6 +312,7 @@ func detailFromEntry(e *store.Entry) *EntryDetail { }, RequestRaw: e.RequestRaw, ResponseRaw: e.ResponseRaw, RequestExact: e.RequestExact, ResponseExact: e.ResponseExact, + RequestTruncated: e.RequestTruncated, ResponseTruncated: e.ResponseTruncated, } } -- cgit v1.2.3