docs: REUSE.md introduced — cross-repo reuse map (exec/format-safety guards, durable-id seams, traps; docs-only, no version bump)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
@@ -1,78 +1,44 @@
|
||||
# REPORT — blast-radius audit fixes B1 + D1 + D2 + D3 (v0.61.0)
|
||||
# REPORT — docs: REUSE.md introduced (cross-repo reuse map)
|
||||
|
||||
**Date:** 2026-07-03 · **Repo:** `felhom-agent` · **Class:** Implementation (destructive-op guard
|
||||
surfaces D1/D3 proven in unit-land + a loop-device recorder harness — NO real mkfs, no live /dev race,
|
||||
per the §13 STOP). Source of truth:
|
||||
`felhom.eu/documentation/audits/AUDIT-blast-radius-hostroot-localapi-2026-07-02.md`.
|
||||
**Date:** 2026-07-03 · **Class:** docs-only (no code change, no version bump, no deploy)
|
||||
**Baseline:** `main` @ `0db09bf34fe2eff4b80ac77ed19afa18aef7675f` (v0.61.0)
|
||||
|
||||
## 1. Baseline → target
|
||||
## What was done
|
||||
|
||||
agent **v0.60.0** (`cc93dae`, confirmed before starting) → **v0.61.0**. Commits on `main`:
|
||||
- **`3f382bf`** — all four fixes + tests + harness + version bump + CHANGELOG/CONTEXT/README.
|
||||
- (this REPORT is the follow-up docs commit)
|
||||
- **`REUSE.md`** created at the repo root (160 lines): canonical helpers (48 rows — allowlisted
|
||||
exec/sudoers surface, disk/format safety with each guard named, durable-id seams, atomic stores,
|
||||
local-API plumbing, proxmox/hub/PBS/provisioning), canonical patterns (12), dangerous lookalikes
|
||||
(10), test seams + cross-repo edges, extension points, observed duplication (7 clusters, NOT fixed).
|
||||
- Sourced from a full code sweep (702 non-test `func` declarations scanned; mandatory trap sweep).
|
||||
Trap-sweep result: the only `--delete` in Go code is `pct set --delete mpN` (DetachBind, itself a
|
||||
§3 trap vs `DetachDrive`); `os.RemoveAll` only on escrow temp workdirs; no wipefs/sfdisk/dd.
|
||||
- **`CLAUDE.md`**: two hook lines — "See `REUSE.md` before writing new code" and the same-commit
|
||||
maintenance rule. No other CLAUDE.md edits.
|
||||
- `CHANGELOG.md` entry (no version number) + `CONTEXT.md` note.
|
||||
|
||||
## 2. What shipped
|
||||
## Verification
|
||||
|
||||
| Fix | Files | What changed |
|
||||
|---|---|---|
|
||||
| **B1** random temp staging | `internal/guesthook/install.go`, `internal/localapi/intermediary.go` (new `stageTemp`), `configs/felhom-agent.sudoers`, `internal/capability/manifest.go` | Fixed, predictable `/tmp` names (local TOCTOU into root-executed hookscript/boot script) → `os.CreateTemp` random names (the `lanresolver` pattern). Sudoers install-SOURCE grants became globs (`/tmp/felhom-guest-hook-*.sh`, `/tmp/felhom-shared-parent-*.{sh,service}`); destinations + modes unchanged. Manifest repr vectors updated to match. |
|
||||
| **D1** wrapper asymmetry | `configs/felhom-mkfs-guarded.sh`, new `scripts/mkfs-guarded-harness.sh` | Additive re-checks: `/sys/block/<disk>/ro`==1 → die; lsblk-FSTYPE loop refusing exactly `claim.go`'s `memberFSTypes` (LVM2_member/zfs_member/linux_raid_member/crypto_LUKS/swap); pvs via absolute candidates (`/usr/sbin/pvs`, `/sbin/pvs`) with the FSTYPE loop as the independent LVM catch when pvs is absent. Nothing removed/reordered; a blank partitioned disk still formats. |
|
||||
| **D2** empty-lsblk fail-safe | `internal/storage/claim.go` | `classifyClaim` refuses when the node tree is EMPTY or the target whole-disk is absent from it (previously: empty-but-successful lsblk ⇒ `unclaimed`). Placed after the member/mount loop; the `gatherErr`/`wholeDiskOK`/`isSystem`/`readonly`/`lvmPV`/`zfsMember` ordering untouched. |
|
||||
| **D3** blank-format anti-retarget | `internal/localapi/wipe_reresolve.go` (shared `antiRetargetResolveExpect` core + new `antiRetargetResolveBlank`), `disks.go`, `formatjob.go`, `server.go` (new `reresolveBlank` seam) | The blank branch now derives the device's durable id (**no durable id ⇒ 409 refuse** — path-only formats not permitted), re-resolves it (resolve → re-derive exact match → re-inspect **STILL !DataBearing**) and formats the RE-RESOLVED device, never `req.Device`. The format-job record carries `blank`; restart recovery re-checks blank jobs with the blank variant. Confirmed (data-bearing) branch untouched. |
|
||||
`python felhom.eu/scripts/reuse_refs_check.py e:/git/felhom-agent` →
|
||||
`OK [felhom-agent]: 57 cited paths, all exist`.
|
||||
|
||||
**Not in this batch (per spec §0/§12):** A1 (stale-lock pool-membership — a SPIKE: the role lacks
|
||||
`Pool.Audit`, no cheap membership read exists); C1, C2, A2, B2–B5, E1, E2 (deferred). B2's `..` guard
|
||||
intentionally skipped — `DriveNameFromRaw` already rejects `/`, so `..` alone can't traverse.
|
||||
## Notable traps recorded (§3 highlights)
|
||||
|
||||
## 3. Tests + red-proofs (each new test shown FAILING on the pre-fix impl, then green on the fix)
|
||||
Acting on `req.Device` after inspection (AGENT-001 TOCTOU) vs the re-resolve seams; raw `mkfs.*`
|
||||
(no longer sudoers-allowlisted) vs `SudoHostOps.Format`; `uuid:` vs `byid:`/`byuuid:` durable-id
|
||||
scheme confusion (F20-BUG2); `MemoryNonceStore` on a real host; adding methods to
|
||||
`proxmox.Privileged` (breaks the 3-exception fence); mutating outside gate/queue/journal; copying
|
||||
the pool-blind stale-lock scan (open audit A1).
|
||||
|
||||
| Fix | Tests | Red-proof result |
|
||||
|---|---|---|
|
||||
| B1 | `TestInstallSnippet_RandomTempName`, `TestInstallSharedParent_RandomTempName` (fake runner records the install source; random pattern + two-calls-differ + content + cleanup asserted) | Pre-fix (HEAD files restored): both FAIL — fixed name, identical across calls. Fix restored → green. |
|
||||
| D2 | `TestClassifyClaim_EmptyNodesRefused`, `TestClassifyClaim_TargetAbsentFromTree` | Pre-fix `claim.go`: both FAIL — `(true,"unclaimed")` returned. Fix restored → green. |
|
||||
| D3 | Pure: `TestFormatBlankPath_AntiRetarget_{ReassignedDataBearingRefused,ReassignedDifferentDiskRefused,UnresolvableRefused,SameBlankProceeds}`. Handler: `TestFormat_Blank_{FormatsReresolvedDeviceNotCallerPath,ReresolveRefusalNoMkfs,NoDurableIDRefused}` | Pre-fix blank branch (formats `req.Device`, no binding): all 3 handler tests FAIL — mkfs ran on the caller path with HTTP 200 and no durable binding. Fix restored → green. |
|
||||
| D1 | `scripts/mkfs-guarded-harness.sh` on felhom-pve (root): throwaway loop devices (`losetup`, one `-r` read-only), PATH-shimmed `lsblk` FSTYPE fixture, `/dev/null` bound over pvs for the pvs-absent case, and a RECORDER bind-mounted over `/usr/sbin/mkfs.ext4` in a private mount ns — **no real mkfs possible**; "formats" is proven by the recorder argv, refusal by its absence | **Fixed wrapper: 8/8 PASS** (5 member FSTYPEs refused, read-only refused, pvs-hidden LVM still refused, plain blank disk still reaches mkfs). **Pre-fix wrapper: 7/8 FAIL** — every member/RO/pvs-hidden fixture reached mkfs. The loop-device `-b` check ran for real (no stub). |
|
||||
## Observations (NOT acted on)
|
||||
|
||||
Full gates green on Windows AND on the build server (go1.26.0):
|
||||
`go build ./... && go vet ./... && go test ./...` — **0 failures**. Test count **472 → 483** (+11).
|
||||
- **CLAUDE.md is badly stale**: says "Current: v0.31.0" and its Layout omits `localapi`,
|
||||
`signedjobs`, `escrow`, `capability`, `guesthook`, `lanresolver`, `desired`, `provision` — all
|
||||
live at v0.61.0. A CLAUDE.md refresh is a separate task.
|
||||
- Dead config knobs: `storage.Binaries.MkfsExt4/MkfsXfs` defined+defaulted but never executed by
|
||||
non-test code (`Format` only execs `MkfsGuarded`).
|
||||
- Acknowledged mirror: `antiRetargetResolveExpect` re-implements `WipeExecutor.Execute` steps 1–3
|
||||
("mirrors" comments in both) — a change to one must touch both.
|
||||
- Duplication clusters (2× lsblk parsers, 2× smartctl paths, 3× atomic JSON store, 4× run() helper,
|
||||
several /proc mount readers) recorded in REUSE.md §6.
|
||||
|
||||
## 4. Build / publish / deploy / verify
|
||||
|
||||
- Built on 180 from `3f382bf`: `CGO_ENABLED=0 go build -ldflags "-X main.version=0.61.0"`;
|
||||
`--version` → `felhom-agent 0.61.0`.
|
||||
- **Published to Gitea** (generic package, delete-then-put + round-trip GET verified):
|
||||
`AGENT_VERSION=0.61.0`, `AGENT_SHA256=d5f2f283501dac9daac1c197c9665c98f4577e11d155f5da831b26048c736ed8`.
|
||||
Credentials sourced out-of-band (`/root/.golden-secrets.env` on the host; not committed anywhere).
|
||||
- **Deployed to felhom-pve** (backups kept: `felhom-agent.bak-0.60.0`, `felhom-mkfs-guarded.bak-0.60.0`):
|
||||
binary → `/usr/local/bin/felhom-agent`; **new wrapper** → `/usr/local/sbin/felhom-mkfs-guarded`
|
||||
(grep-confirmed the D1 checks in the installed copy); **new sudoers** → `/etc/sudoers.d/felhom-agent`
|
||||
(`visudo -cf` passed both staged and installed).
|
||||
- **Verify after restart:** service `active`, `--version` 0.61.0, **0 ERROR lines** in the startup
|
||||
journal; **capability self-check 46/46 ok, 0 degraded** (proves the new sudoers globs cover the new
|
||||
manifest vectors live); `ReassertGuestBinds`/reconcile re-bound exactly the three enrolled drives
|
||||
(felhom-flash, felhom-usb, teszt_enroll) — nothing else; stale-lock recovery ran with nothing to reap
|
||||
(no stale locks, no false action); local API listening; hub desired-state pulled.
|
||||
- **B1 live-proven end-to-end:** made the installed shared-parent script stale (appended a marker) →
|
||||
the next reconcile self-healed it through the REAL sudo path with random-named temps
|
||||
(`install -m 0755 -- /tmp/felhom-shared-parent-281383295.sh …`,
|
||||
`install -m 0644 -- /tmp/felhom-shared-parent-2841067761.service …`) — authorized by the new globs.
|
||||
- Cleanup: all /tmp staging removed from felhom-pve and 180; harness loop devices/workdir self-cleaned.
|
||||
|
||||
## 5. NOT yet live-validated — awaiting a supervised step
|
||||
|
||||
- **D1 destructive proof:** a real `mkfs` attempt against a crafted ZFS/mdraid/LUKS/swap member disk.
|
||||
- **D3 race proof:** a live /dev re-enumeration race during a real format.
|
||||
Both are proven in unit-land + the recorder harness only, per the task's hard STOP.
|
||||
|
||||
## 6. Observations / follow-ups (recorded, not acted on)
|
||||
|
||||
- **Operator follow-up:** the hub Day-0 artifact manifest still vouches agent **0.60.0** — record
|
||||
`0.61.0` / sha `d5f2f283…` in the password-gated operator UI (Configs → Day-0 artifacts) so Day-0
|
||||
self-installs fetch the new build. (CC cannot do this; see hub-artifact-manifest gotcha.)
|
||||
- **A1 (LOW, pending SPIKE):** stale-lock recovery reaps by a pool-blind `ListLXC` scan — contained by
|
||||
the pool-scoped token; MUST be fixed before any broad/root-token deployment. Open design question:
|
||||
which pool-membership read the role can authorize (`Pool.Audit` absent today).
|
||||
- Deferred audit items: C1 (controller-swap version floor), C2 (NAS server allowlist), A2 (gate
|
||||
journal cross-check), B3/B4/B5 housekeeping, E1/E2 comment fixes.
|
||||
- Known pre-existing: the non-root agent cannot read the PBS key (`felhom-pbs.pw` permission denied at
|
||||
startup — the BUNDLE leftover, unchanged by this batch).
|
||||
Cross-repo summary + full sweep counts: `felhom.eu/REPORT.md`.
|
||||
|
||||
Reference in New Issue
Block a user