[security] Host report path writes VM status and hard-deletes without a host_id predicate #110

Closed
opened 2026-09-02 04:38:08 +00:00 by a73x · 2 comments
a73x commented 2026-09-02 04:38:08 +00:00 (Migrated from git.a73x.sh)

[claude 2026-09-02] Found by whole-tree security audit.

internal/server/syncsvc/syncsvc.go:498 → store.RecordVMStatus (store.go:1413, UPDATE vms ... WHERE id=?) and syncsvc.go:548 → store.HardDeleteVM (store.go:958, DELETE FROM vms WHERE id=? AND deleted_at IS NOT NULL) take the VM id straight from the host's Report with no host_id predicate. Their siblings RecordVMNetworkIP and RecordVMHostKey both carry AND host_id=?, and the RecordVMNetworkIP comment states the rule ("a host may only ever speak for the VMs it holds").

The sync channel never hands a host a foreign id, but cloud-init instance-id IS the VM id (reconcile.go:733, seed.go:288), so a guest can leak it to a compromised host in any tenant. Impact: overwrite status/last_error/assigned_ip shown to the owning tenant, or hard-delete a tombstoned row before its own host reaps it (defeats the restore grace window). Severity Low; the fix is one predicate per statement.

Fix: RecordVMStatus(id, hostID, ...) and HardDeleteVM(id, hostID) with AND host_id=?; pass hostID from applyReport; tracker.writeThrough already treats a no-row result as rejection. Keep the operator force-delete (api.go:239) on a separate unscoped store method with its own authz. Tests: a report from host B naming host A's VM changes nothing. Related: 0faec1af (same rule, network_ip).


Migrated from git-collab issue 860df1fdc765418aaffc4598232830dd69c20a80.

Original author: a73x dev@a73x.sh.

Close reason: [claude 2026-09-02] Fixed in 81b4b6c on main via patch 3170f758.

Linked commit: a1ebe074596ecb4092505e3376b6c5e8875e7b0b

Linked commit: 81b4b6c1ca2b07794222ef7cd452e02d7dec46b4

[claude 2026-09-02] Found by whole-tree security audit. `internal/server/syncsvc/syncsvc.go:498` → `store.RecordVMStatus` (`store.go:1413`, `UPDATE vms ... WHERE id=?`) and `syncsvc.go:548` → `store.HardDeleteVM` (`store.go:958`, `DELETE FROM vms WHERE id=? AND deleted_at IS NOT NULL`) take the VM id straight from the host's Report with no `host_id` predicate. Their siblings `RecordVMNetworkIP` and `RecordVMHostKey` both carry `AND host_id=?`, and the RecordVMNetworkIP comment states the rule ("a host may only ever speak for the VMs it holds"). The sync channel never hands a host a foreign id, but cloud-init `instance-id` IS the VM id (`reconcile.go:733`, `seed.go:288`), so a guest can leak it to a compromised host in any tenant. Impact: overwrite status/last_error/assigned_ip shown to the owning tenant, or hard-delete a tombstoned row before its own host reaps it (defeats the restore grace window). Severity Low; the fix is one predicate per statement. Fix: `RecordVMStatus(id, hostID, ...)` and `HardDeleteVM(id, hostID)` with `AND host_id=?`; pass hostID from applyReport; `tracker.writeThrough` already treats a no-row result as rejection. Keep the operator force-delete (`api.go:239`) on a separate unscoped store method with its own authz. Tests: a report from host B naming host A's VM changes nothing. Related: 0faec1af (same rule, network_ip). --- Migrated from git-collab issue `860df1fdc765418aaffc4598232830dd69c20a80`. Original author: a73x <dev@a73x.sh>. Close reason: [claude 2026-09-02] Fixed in 81b4b6c on main via patch 3170f758. Linked commit: `a1ebe074596ecb4092505e3376b6c5e8875e7b0b` Linked commit: `81b4b6c1ca2b07794222ef7cd452e02d7dec46b4`
a73x commented 2026-09-02 04:39:25 +00:00 (Migrated from git.a73x.sh)

[claude 2026-09-02] Picking this up; branch audit-fixes, one commit per issue, boot-gate before merge.

[claude 2026-09-02] Picking this up; branch audit-fixes, one commit per issue, boot-gate before merge.
a73x commented 2026-09-02 04:54:02 +00:00 (Migrated from git.a73x.sh)

[claude 2026-09-02] Fixed in a1ebe07 on branch audit-fixes; carried by patch 3170f758fa (whose --fixes is 08f40f15). Close on merge.

[claude 2026-09-02] Fixed in a1ebe07 on branch audit-fixes; carried by patch 3170f758faf406897dcacc4e3d605a2a8ba1fb03 (whose --fixes is 08f40f15). Close on merge.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
a73x/eitri#110
No description provided.