The security documentation's last open item of panel-dkim-signing (the owner's decision, 2026-10-16): go-milter v0.4.1 added every header of a session to a map of its own, emptied only on ABORT, and handed it to Headers - which no milter of the panel reads. Postfix bounds one header, not their number, so a message of nothing but headers up to the message size limit was held whole by the library in the sender check, the signer and the journal milter on 465/587 and in the inbound journal milter on port 25, where strangers send; the signer's 1 MiB refusal came after it was all in. third_party/go-milter is v0.4.1 from the module cache (BSD 2-Clause, LICENSE kept) with session.go changed: CodeHeader hands the header to Header and keeps nothing, CodeEOH hands Headers an empty map. PATCHES.md says what and why, and how to take a new upstream version. go.mod replaces the module with it; the Dockerfile copies third_party before "go mod download". TestTheLibraryKeepsNoHeaders holds it through the library's own client over two messages of one connection. Comments, security.md (the accepted risk becomes a note), development.md's library table, the plan and CHANGELOG follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1.7 KiB
go-milter v0.4.1, as SelfPost builds it
This is github.com/emersion/go-milter at v0.4.1 (BSD 2-Clause, LICENSE),
copied from the Go module cache, with one change. The main module takes it
through a replace in its go.mod; the module path stays the upstream one.
Left out of the copy: cmd/ (a command-line tool SelfPost does not build) and
the protocol notes (milter-protocol*.txt).
The change: headers are not kept
session.go, Process(), cases CodeHeader and CodeEOH. Upstream adds
every header of a session to a map (m.headers) that it empties only on
ABORT, and hands that map to the backend's Headers at the end of the
headers. Postfix bounds one header (60000 bytes with milters) but not their
number, so a message of nothing but headers, up to message_size_limit,
was held whole by the library in every milter that receives headers - the
sender check, the DKIM signer and the journal milter on 465/587, the inbound
journal milter on port 25 - on top of what the milters keep themselves
(security review of panel-dkim-signing, 2026-10-16).
Patched, CodeHeader hands each header to the backend's Header and keeps
nothing; CodeEOH hands Headers an empty map. No SelfPost milter reads
that map: the sender check and the signer keep the headers they need
themselves (internal/milter, senderSession, signSession), and the journal
milters do not implement Headers.
The client side (client.go, used by the unit tests) is unchanged.
Taking a new upstream version
Copy the files again, apply the change above, and keep this note. Read the
new session.go first: if upstream bounds or drops the map itself, the
patch - and this copy - may go.