diff --git a/docs/audit-2026-10-12.md b/docs/audit-2026-10-12.md index e341768..3cbdb2d 100644 --- a/docs/audit-2026-10-12.md +++ b/docs/audit-2026-10-12.md @@ -840,7 +840,44 @@ order - each stands on the one before. refusal's line and a recovered row), guide § Outbound log and § Health, schema-migrations.md for the mark on `sender_check_days`. — **Sonnet** -### Stage 3 — persistence and restore (M1–M5, L1–L4, L7) → `2.5.2` +### Stage 2c — the sender check reads From: as OpenDKIM does → `2.5.2` + +Decided by the owner on 2026-10-14, after reading OpenDKIM's source together +(libopendkim `dkim_mail_parse`, called in `mlfi_eoh` on the first `From:` +with `SenderHeaders` unset - `build/opendkim.conf` sets none): the signing +address is the one in angle brackets, or a bare address, found while quoted +strings and comments are skipped; encoded words are not decoded. The check's +scan of the display name and comments for another sender of the server is a +guard against two parsers parting ways - and it is what makes L8 half +closed. This stage replaces the guard with the parser itself. Stage 3 and +those after it move up one patch number each. + +- [ ] **Port `dkim_mail_parse` to Go, from the version the image runs** + (Debian bookworm's opendkim 2.11.0~beta2 source package, not upstream's + develop branch), line for line, with its tokenizer + (`dkim_mail_first_special`, `dkim_mail_token`). The check then decides by + the address OpenDKIM will take: the application's - any address of its + domain, or one of its list - passes, whatever the display name or a + comment holds; anything else, a parse failure included, is refused. One + `From:` line and one address stay required. The scan for another sender + of the server in the name, in a comment and in a decoded name goes, and + with it the reason that told an application which domains are hosted + here (L8 closed; security.md's accepted risk struck). Whether the "one @ + outside quotes" rule is still needed is decided against the port, and + said. - **Opus** +- [ ] **The port checked against OpenDKIM itself.** On the stand, several + hundred generated `From:` lines - quotes, escapes, comments nested and + open, groups, brackets, encoded words, a second domain of the server in + every place - are sent; for each, the domain the port names must be the + `d=` the message arrives signed with, or both say "no signature". The + shared files of lines (`from-lines-passed.txt`, `from-lines-other.txt`) + are kept: the second one's test becomes "never signed for {other}" + rather than "refused". - **Opus** +- [ ] Docs and wording: help topic sender-check and guide § Who an + application may send as (mockup first), security.md § Mail path, + CHANGELOG. - **Sonnet** + +### Stage 3 — persistence and restore (M1–M5, L1–L4, L7) → `2.5.3` - [ ] **Consume the manifest after the resync succeeds** (`CheckRestore` reports; `run` deletes after `resyncAfterRestore` returns nil). Test: Resync fails → @@ -865,7 +902,7 @@ order - each stands on the one before. package. — **Opus** - [ ] Docs: security.md § Backup; guide § Backup. — **Sonnet** -### Stage 4 — panel behaviour (M8, M11, L10–L19) → `2.5.3` +### Stage 4 — panel behaviour (M8, M11, L10–L19) → `2.5.4` - [ ] Login: dummy bcrypt on unknown user; a test that measures both paths within a tolerance. Account password change gets its own limiter bucket. Login diff --git a/docs/development.md b/docs/development.md index 8360079..614c8f8 100644 --- a/docs/development.md +++ b/docs/development.md @@ -133,9 +133,10 @@ than the default «meaningful step» rule in [§ Commits and release build](#com | 20 | audit-2026-10-12 stage 0: release hygiene | `2.4.2` (PATCH) | | 21 | audit-2026-10-12 stages 1 and 2 | `2.5.0` | | 22 | audit-2026-10-12 stage 2b: the log tailer | `2.5.1` | -| 23 | audit-2026-10-12 stage 3 | `2.5.2` | -| 24 | audit-2026-10-12 stage 4 | `2.5.3` | -| 25 | audit-2026-10-12 stage 5 | `2.6.0` | +| 23 | audit-2026-10-12 stage 2c: the sender check reads From: as OpenDKIM does | `2.5.2` | +| 24 | audit-2026-10-12 stage 3 | `2.5.3` | +| 25 | audit-2026-10-12 stage 4 | `2.5.4` | +| 26 | audit-2026-10-12 stage 5 | `2.6.0` | Docs-only prep (checklists in plans, no product code) uses the same per-step commit rule but **no** version cut until the next product stage ships. diff --git a/docs/roadmap.md b/docs/roadmap.md index f6ba7dd..e48a426 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -42,7 +42,7 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md). | from-header-binding | Hold a message's `From:` header against the application that sends it | shipped `2.4.0` | 9/9 | [plans/from-header-binding.md](plans/from-header-binding.md) | | panel-redesign | Panel redesign (accepted mockups, design contract) | shipped `2.0.0` | 27/27 | [plans/panel-redesign.md](plans/panel-redesign.md) | | schema-squash | Squash SQLite migrations into a 2.x baseline | built in panel-redesign (stage 1) | — | [plans/panel-redesign.md](plans/panel-redesign.md) | -| audit-2026-10-12 | Follow-ups of the 2026-10-12 full-tree code audit, seven stages | in progress (stage 0 shipped `2.4.2`, stages 1+2 `2.5.0`; next stage 2b → `2.5.1`) | 30/62 | [audit-2026-10-12.md](audit-2026-10-12.md) | +| audit-2026-10-12 | Follow-ups of the 2026-10-12 full-tree code audit, seven stages | in progress (stage 0 shipped `2.4.2`, stages 1+2 `2.5.0`; next stage 2b → `2.5.1`) | 30/65 | [audit-2026-10-12.md](audit-2026-10-12.md) | **Recommended order** (not binding; owner, 2026-10-09: feature order follows development convenience): **panel-redesign** shipped as `2.0.0` (2026-10-11) and