release: 1.9.2: Review pass: port-25 milters, DMARC validation, rate-limit fix
Review pass over `1.4.0`…`1.9.1`: port 25 no longer shares the submission milter chain, DMARC reports are validated before they are stored, and the auto rate limit is correct for a non-default level-1 window. Schema migration `0010` (index only). Upgrading is a tag bump. Added: - SQLite migration `0010_send_log_app_login_index.sql` — - DMARC aggregate reports are accepted as **zip** as well as gzip or plain XML - e2e: `checkPort25ConfigCombinations` regenerates the Postfix configuration Changed: - README hero line and a **Positioning** block in `docs/product.md` record the Fixed: - The setup account's password is hashed at bcrypt cost `12` like every other - Login/setup rate limiter: protecting blocked buckets from eviction is now - `postqueue -p` output is bounded to 4 MiB (the timeout alone left the size of - [schema-migrations.md](docs/schema-migrations.md): the schema section was - postqueue reader is now time-bounded: `postqueue -p` runs under a 5-second … (11 more in CHANGELOG) Changed: - [roadmap.md](docs/roadmap.md) now carries everything the 2026-08-19 code - Extracted `internal/configsafe` — the empty-value and forbidden-character - Build, deploy and migration comments no longer cite the archived - Dropped the `parseDomainRateLimitForm` / `parseAppRateLimitForm` pass-through - Extracted shared helpers: `internal/atomicfile` (atomic temp-write+rename) and … (7 more in CHANGELOG) Removed: - Unused legacy asset `docs/assets/icon.png` (superseded by the four brand SVGs - Plan documents for already-shipped features: `domain-stats-auto-ratelimit`, Full detail: CHANGELOG.md (1.9.2).
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package e2e
|
||||
|
||||
import "testing"
|
||||
|
||||
// realMasterCfSample is captured verbatim (via `cat -A`) from a container
|
||||
// generated with INBOUND_RELAY_ENABLE=false DMARC_REPORTS_ENABLE=true: after
|
||||
// the unconditional `postconf -F "*/*/chroot=n"` rewraps the file, the
|
||||
// smtp/inet stanza's options are spread across several continuation lines
|
||||
// that split values mid-token and re-flow what were originally separate
|
||||
// "-o " lines into one paragraph. Confirms parseSMTPInetOptions reconstructs
|
||||
// the logical options from that rather than a plain single-option-per-line
|
||||
// master.cf.
|
||||
const realMasterCfSample = `smtp inet n - n - - smtpd
|
||||
-o smtpd_milters=
|
||||
-o smtpd_recipient_restrictions=check_recipient_access
|
||||
texthash:/data/postfix/dmarc_recipients,reject -o smtpd_sasl_auth_enable=no
|
||||
-o smtpd_tls_auth_only=no -o smtpd_sender_login_maps= -o
|
||||
smtpd_sender_restrictions= -o smtpd_client_restrictions= -o
|
||||
smtpd_relay_restrictions=reject_unauth_destination -o
|
||||
smtpd_client_message_rate_limit=20 -o message_size_limit=5242880
|
||||
pickup unix n - n 60 1 pickup
|
||||
`
|
||||
|
||||
func TestParseSMTPInetOptionsAcrossContinuationLines(t *testing.T) {
|
||||
stanza := smtpInetStanza(realMasterCfSample)
|
||||
opts := parseSMTPInetOptions(stanza)
|
||||
|
||||
want := map[string]string{
|
||||
"smtpd_milters": "",
|
||||
"smtpd_recipient_restrictions": "check_recipient_access texthash:/data/postfix/dmarc_recipients,reject",
|
||||
"smtpd_sasl_auth_enable": "no",
|
||||
"smtpd_tls_auth_only": "no",
|
||||
"smtpd_sender_login_maps": "",
|
||||
"smtpd_sender_restrictions": "",
|
||||
"smtpd_client_restrictions": "",
|
||||
"smtpd_relay_restrictions": "reject_unauth_destination",
|
||||
"smtpd_client_message_rate_limit": "20",
|
||||
"message_size_limit": "5242880",
|
||||
}
|
||||
for param, wantVal := range want {
|
||||
if got, ok := opts[param]; !ok || got != wantVal {
|
||||
t.Errorf("opts[%q] = %q (present=%v), want %q", param, got, ok, wantVal)
|
||||
}
|
||||
}
|
||||
if len(opts) != len(want) {
|
||||
t.Errorf("parsed %d options, want %d: %+v", len(opts), len(want), opts)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSMTPInetStanzaSkipsDeliveryAgent(t *testing.T) {
|
||||
master := "smtp unix - - n - - smtp\n" +
|
||||
"# -o smtp_helo_timeout=5\n" +
|
||||
realMasterCfSample
|
||||
stanza := smtpInetStanza(master)
|
||||
if stanza == "" {
|
||||
t.Fatal("no smtp/inet stanza found")
|
||||
}
|
||||
opts := parseSMTPInetOptions(stanza)
|
||||
if v, ok := opts["smtpd_milters"]; !ok || v != "" {
|
||||
t.Fatalf("picked up the wrong \"smtp\" service: opts = %+v", opts)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user