diff --git a/documentation/audits/CAMPAIGN-nomercy-2026-07-06.md b/documentation/audits/CAMPAIGN-nomercy-2026-07-06.md index 2ba8ebf..b0d578d 100644 --- a/documentation/audits/CAMPAIGN-nomercy-2026-07-06.md +++ b/documentation/audits/CAMPAIGN-nomercy-2026-07-06.md @@ -217,3 +217,77 @@ No production code, versions, or configuration were changed. The single change i The demo node was destroyed and restored (authorized) and left **running and healthy** at R1 state, with notifications restored. Evidence bundle retained on DooPlex, never committed; no secrets in this report. + +--- + +# Findings triage addendum (2026-07-06) + +Follow-up implementation task on F1–F3. F1/F3 fixed + shipped; F2 diagnosed to a verdict (agent +untouched — a fix is a separate follow-up). Shipped: catalog `d86e256`, controller **v0.101.0** +(`fc28033` sync deadline, `c997d79` agent-refusal surfacing). + +## F1 — CORRECTED root cause (the original hypothesis above was wrong) + +The report's F1 hypothesis — *"the shim sets `SMTP_FROM` without `SMTP_HOST`"* — is **incorrect**: +with app-email off (the default) the shim injects **nothing**. Validated against live source + +the pinned image. Real cause: the template **always** defines `SMTP_HOST=${SMTP_HOST:-}` and +`SMTP_FROM=${SMTP_FROM:-}`; vaultwarden treats a **defined-but-empty** env var as *set*, and with +its `_enable_smtp` defaulting **true** its validation (`smtp_host.is_some() == smtp_from.is_empty()` +→ `true == true`) errors out and the process exits. Every fresh email-off deploy shipped a dead +password manager. + +**Empirical proof** on `vaultwarden/server:1.33.2-alpine` (evidence: +DooPlex `evidence/PROBE-F1/`): P1 `SMTP_HOST=`/`SMTP_FROM=` (defined-empty) → **exit 12**, exact +campaign error; P2 `+_ENABLE_SMTP=false` → **boots**; P3 `_ENABLE_SMTP=true`+host+from → **boots**. + +**Fix** (catalog): gate the SMTP group with `_ENABLE_SMTP` — compose default `false` (validation +skipped, clean boot), flipped `"true"` by the app-email injection via `smtp_mapping.extra`. +**Live-validated via the real UI pipeline** (v0.101.0 on 9201): fresh email-off deploy → healthy, +`_ENABLE_SMTP=false`, restarts=0; app-email ON → healthy with `SMTP_HOST=felhom-controller` + +`SMTP_FROM=vaultwarden@felhom.eu`; OFF → healthy. The other 5 smtp-mapped templates are +campaign-boot-proven tolerant (no edits). + +## F2 — VERDICT: **real finding** (agent role-resolution inconsistency; fail-safe direction) + +Campaign-time agent journal (persistent journald survived the reboots), the authoritative evidence: +``` +Jul 06 08:36:03 felhom-agent WARN "local-api: protected — decommission refused by role" + vmid=9201 where=/mnt/teszt_enroll role=system +``` +`where` + `role` give the verdict directly (no reproduction needed — the drive was never mutated). + +**Mechanism** (`felhom-agent internal/localapi/disks.go:1020` `roleForMountPath`): it loops the +agent's `storage.Observe()` **storage targets** for one whose `MountPath == where`, and **fail-safe +returns `RoleSystem` when none matches** (`:1031`). `/mnt/teszt_enroll` is a real user-data USB +drive but is **not a PVE storage** (`pvesm status` lists only felhom-usb / felhom-flash — both +`content backup` — plus the PBS stores; no `teszt_enroll`). So no target matches → `system` → +decommission refused. + +**Why it's a real finding, not a harness artifact:** the agent's own `/disks` inventory +**already classifies teszt_enroll as `role: user-data`** (durable_id `uuid:f2236136…`), yet the +decommission/eject gate independently resolves it to `system`. Two role-resolution paths disagree: +the disk-inventory path knows it's user-data; the gate (keyed on PVE storage targets only) doesn't +see a bind-mounted user-data drive that isn't its own PVE `dir` storage. Any such drive hits this, +so a legitimate customer "decommission anyway" can be blocked. + +**Severity: MEDIUM, fail-safe direction.** The default is *over-refusal* (refuse rather than wrongly +unmount) — it never permits a wrong decommission, only blocks a right one. Not a security hole. + +**Suggested fix (deferred — agent follow-up task, NOT done here):** have `roleForMountPath` fall +back to the durable-id / DiskInfo role (or `RoleForRawDevice`) when no storage target matches the +mount, instead of blanket-defaulting to `system` — so an enrolled user-data drive is decommissionable +regardless of whether it is a standalone PVE storage. Pair with a red-proof on a bind-mount-only +user-data drive. + +## F3 — FIXED (controller v0.101.0) + +Real half confirmed: `internal/sync/sync.go runGitInDir` ran `cmd.Run()` with no context, so a hung +remote parked the sync goroutine, held `syncing=true`, and refused every later sync until a +controller restart. Fixed with a per-command `exec.CommandContext` deadline (`gitCmdTimeout = 120s`); +the deadline error names the timeout + masked args. Debounce unchanged. Tests + red-proof shipped; +live sync smoke `POST /api/sync` → 2xx post-deploy. + +## Harness note (unchanged — for the record) +The P9 finale FAIL in the main report was a harness volid-format bug (epoch vs ISO-8601), fixed in +the DooPlex workspace copy; the DR restore mechanism itself is sound (9201 recovered from R1). The +other harness mis-scores (H2–H8) stand as written.