inbound-antispam-panel: findings of the technical and security reviews
test / test (push) Canceled after 0s
test / test (push) Canceled after 0s
Steps 11 and 12 of docs/plans/inbound-antispam-panel.md: 12/12. Two
read-only reviews of d65d632..HEAD by different models (Opus,
technical; Fable, security), each given the code and questions, not
conclusions. Nothing Critical or High in the feature; the High the
technical review found outside it is the commit before this one.
Log tailer (internal/logtail/inbound.go):
- the reply of a refusal ends at the first "; from=<", Postfix's own,
and the HELO name after it is whatever is left. Taking the longest
reply let a HELO name that spelt "; from=<..> to=<..> proto=.."
supply the row's sender, recipient and reply - and so make a
refusal read as the deny list's;
- an address with a quote, a bracket or a space is not kept: a quoted
sender could otherwise supply the recipient;
- the journal is cut to its row cap every 500 rows written, not only
every six hours;
- a queue id is forgotten when the message is queued or the id is
opened by another listener, and a stale place in the queue of
arrivals no longer drops a live id;
- milter-hold lines are not read: the held message reaches the
journal milter, which writes its row - it was written twice.
Lists (internal/inbound/lists.go): a change of the table and the
rewrite of the files are one step under a mutex; a failed rewrite puts
a removed entry back and rewrites after taking a new one out.
Store: the row cap removes the oldest by time, as the page orders them.
Page: on a sheet a refusal's text is a Reply whatever it starts with.
Help and the guide say that an allow entry for a sender address trusts
an address anyone can claim (design commit before this one).
docs/security.md: the two reviews; the roles table gains Inbound > Log
and Filter lists; Mail path describes the milters, the tailer and the
lists; four accepted risks - the filter's verdict while it is down, an
allow entry by sender address, the list files' mode, log rows held by
domain name. The plan's stale lines (size and rate refusals, who reads
the journal) are corrected.
Left as found, told to the owner: the journal's rows after an unclean
stop may be written twice (the tailer saves its place every five
seconds); a "Deny sender" link is offered for an address the form then
refuses; 1.2.3.4 and 1.2.3.4/32 are two entries.
Checked on the test server, uncommitted tree: go vet ./... ok,
go test ./... ok (22 packages), make e2e ok.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -74,7 +74,9 @@ antispam hook). Without an external filter:
|
||||
|
||||
- **accept** rows — message relayed (engine `selfpost`, detail empty);
|
||||
- **reject** / **tempfail** rows — from the mail.log tailer (Postfix policy,
|
||||
size limit, unknown recipient, etc.).
|
||||
unknown recipient, a sender domain that does not exist. As built, a refusal
|
||||
whose log line names no recipient - the size limit answered at MAIL FROM -
|
||||
and the per-client rate limit, which Postfix logs as a warning, make no row).
|
||||
|
||||
Panel copy explains that rspamd symbols appear only when the antispam milter is
|
||||
configured.
|
||||
@@ -260,7 +262,9 @@ flowchart LR
|
||||
## Security
|
||||
|
||||
- Same validation whitelists as inbound relay ([security.md](../security.md)).
|
||||
- Journal rows contain PII — retention and global-admin-only access.
|
||||
- Journal rows contain PII — retention, and access by reach: the global role
|
||||
reads all of it, a `domain` user the rows of assigned inbound domains (as
|
||||
built; see § RBAC).
|
||||
- Inbound journal milter fail-open must not block mail (backup-MX role).
|
||||
- List map writes injection-safe; deny wins over allow.
|
||||
|
||||
@@ -312,5 +316,5 @@ Target version cut: **`2.1.0`** (MINOR), after `2.0.0`. One commit per step;
|
||||
- [x] Unit + handler tests — **Composer** (written with each step: store, milter, tailer, lists and their usage, view fixtures held to the outlines, handlers for both roles)
|
||||
- [x] [guide.md](../guide.md), [architecture.md](../architecture.md) — **Composer** (kept up with each step; [security.md](../security.md) follows the security review)
|
||||
- [x] `go vet`, `go test`, e2e if applicable — **Composer** (on the test server for every step; last on `7192c38`: vet ok, 22 packages ok, e2e ok, which relays, refuses, reads the log page and edits a list through the panel)
|
||||
- [ ] Technical review of inbound antispam changes — **Opus**
|
||||
- [ ] Security review inbound antispam path — **Fable**
|
||||
- [x] Technical review of inbound antispam changes — **Opus** (2026-10-12; findings fixed: client address of a connection's later messages, the lists' writer under a lock, the tailer's queue-id memory, hold rows written twice, the cap by time; and, outside the feature, the level-2 limit and client-address list skipped after a connection's first message)
|
||||
- [x] Security review inbound antispam path — **Fable** (2026-10-12; no Critical or High; HELO-forged journal rows and the six-hourly row cap fixed, three accepted risks recorded in [security.md](../security.md))
|
||||
|
||||
Reference in New Issue
Block a user