Files
felhom.eu/documentation/audits/dataloss-hunt-2026-10-20/C1-restore-rollback-candidates.md
T
admin f84f94d987 Rehearsal 2026-10-20 (Tester4) + data-loss hunt: baselines, B1 install evidence, A6 drawn order, tools, first two reviewers' candidates
No release, deploy or publish. Off-site is OFF for Tester4 and the owner passphrase is needed from the operator (bind blocked after 2 of 5 tries).
2026-10-20 11:57:55 +02:00

5.1 KiB

C1 reviewer 1 — restore and rollback (fresh, read-only; clones at controller 7ca59e2 / v0.336.0)

Raw candidates as returned by the reviewer, condensed; each is UNVERIFIED until C2 marks it. File:line are in the controller unless named.

# Sev guess harm mechanism
R1 P2 high A local / second-drive unit restore that fails halfway restarts the app on half-replaced data, no undo copy; the next night's capture then overwrites the only restore point with the broken state internal/backup/restore_unit.go:429-431 failed stop only warns; restore.go:217-218 docker volume rm -f error ignored before untar (10-min limit :237-241); restore_unit.go:466-486 DB replay failure → app started anyway; one restore point per app, promoted in place (atomicPromoteTar)
R2 P2 high A power cut / controller restart during a restore starts the app on a mixed state; the undo copy is never used at boot offbox_reconstitute.go:942 marker → appstop_marker.go:307 StartStack with no restore check; offbox_restore_undo.go:137 undo dir only dropped, never read at start; unit restore and .fab import set no marker → bootrecon/bootrecon.go:210-213,269 starts the app; updates have a journal (stacks/update.go:1420-1489), restores do not
R3 P2 high A cut-off shared-folder restore can be „placed" and reported as restored while files are missing; scratch deleted shares_restore.go:138 readiness = rErr == nil && len(entries) > 0 (R-358 class, not fixed for shares); 30-min ctx web/offbox_handlers.go:716; place reports N files and deletes scratch :301; free-space gate fails open and fixed 2 GiB :107
R4 P2 high A .fab export silently drops a whole drive folder that contains a symlink; import never sets owners appexport/fabplan.go:292 tar.FileInfoHeader(info,"") then os.Open+io.Copy on a symlink; export.go:714 warn-and-continue; assertBundleDataComplete refuses only when no data at all; restore.go:1157-1176 extracts only dirs/regular files, no chown
R5 P2 med-high Second-drive whole restore swaps files, then refuses the DB half, leaves the app STOPPED and says „the app is untouched" tier2_whole.go stops + merges first, then RestoreFromRecoveryUnitAtWith whose refusals (restore_unit.go:366-371, 386-389, 413-416, 420-423) assume nothing happened; en.json:2667 „the restore did not start. The app is untouched"
R6 P2 med .fab import into a STOPPED app merges into existing volumes; the default export (stop box ticked) has no SQL dump appexport/restore.go:361-374 volumes removed only if running; docker cp merge :721; templates/app_data.html:221 stop default; export.go:262 stop before dumpDatabase :366; appbackup/dbdump.go:142 discovers only running containers
R7 P2/P3 high .fab import has no concurrency guard, no undo, no record restore.go:181-186; web/handler_export.go:312-353 no restoreOpBlocked(); RemoveStackVolumes before anything else, no copy
R8 P2 med .fab import drops the family gate, sign-up lock and version pin → a family-only app opens to anyone cmd/controller/main.go:3517-3530 bare AppConfig; stacks/family_gate.go:173-190 removes the gate file when FamilyGate == nil; R-773 fix only on the unit path (stacks/deploy.go:840-870)
R9 P3 med .fab import trusts the bundle's HDD_PATH; empty → relative path; older userdata overwrites newer restore.go:589-631
R10 P3 high .fab DB import: plain psql without ON_ERROR_STOP; postgis not classified restore.go:1052-1053, :928-937
R11 P3 med volume replace never checks the old volume is gone restore.go:218
R12 P3 high interrupted second-drive whole restore leaves <dst>.felhom-restoring (O_EXCL) → every retry fails „file exists" tier2_whole.go:86
R13 P3 design full off-site download capped at 30 min per press; household not told to retry web/offbox_handlers.go:455
R14 P4 med place/full restore re-read the LATEST snapshot instead of the downloaded one offbox_restore.go:675-679, offbox_reconstitute.go:657-661

Inventory drift (July → now): three more customer restore entries exist: the Tier-2 unit restore (R-102), the Tier-2 whole restore (decision 26, tier2_whole.go), the kept-data Load (kept_load.go).

Checked SAFE (reviewer): .fab path confinement (appexport/restore.go:1104-1107,1152-1155; validate.go:28-66; web/handler_export.go:385-394); off-site placement map refuses ../outside/backups/ (offbox_restore.go:603-640); R-358 marker after restic success; full off-site restore fit/safety-dump/undo checks before stop, fail closed (offbox_reconstitute.go:719-818, 909-927); Tier-1 refusals before change; main loader ON_ERROR_STOP=1 --single-transaction (appbackup/dbdump.go:775-784); Tier-2 file restore rsync --ignore-existing, never --delete; whole restore newer-wins + Lchown; NFC/NFD: no normalisation anywhere — every lane compares bytes, so the missing-only compare sees TWO names (consistent on one filesystem); agent RestoreLXC force only when asked. Not reviewed: kept_load.go, hub operator restore paths.