From 7774ee69fe15e8148b0f3cc75010436258702300 Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Sat, 13 Jun 2026 17:53:04 +0200 Subject: [PATCH] =?UTF-8?q?audit:=20AGENT-T2-1=20evidence=20test=20?= =?UTF-8?q?=E2=80=94=20ParseUPID=20accepts=20empty=20node?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Failing evidence test for the cross-repo deep audit (AUDIT-2026-06-13.md lives in the felhom-controller audit branch). Demonstrates ParseUPID returns Node="" with no error for an empty-node UPID, so the PVE TaskStatusOnce/WaitTask path would request /nodes//tasks/... — the PBS client has the guard the PVE side lacks. Read-only audit; no production source changed. Co-Authored-By: Claude Opus 4.8 (1M context) --- internal/proxmox/upid_audit_test.go | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 internal/proxmox/upid_audit_test.go diff --git a/internal/proxmox/upid_audit_test.go b/internal/proxmox/upid_audit_test.go new file mode 100644 index 0000000..4887df3 --- /dev/null +++ b/internal/proxmox/upid_audit_test.go @@ -0,0 +1,33 @@ +package proxmox + +import "testing" + +// TestParseUPIDRejectsEmptyNode is an AUDIT evidence test for finding +// [AGENT-T2-1] (commit d17b5ab). A UPID whose node field is empty +// ("UPID::pid:pstart:start:worker:id:user:") still satisfies the >=8-field +// check, so ParseUPID returns Node=="" with no error. The PVE TaskStatusOnce/ +// WaitTask path (task.go) then builds "/nodes//tasks//status" and queries +// u.Node verbatim — unlike the PBS client (pbs/client.go), which explicitly +// rejects an empty node before issuing the request. +// +// This test asserts the SAFE invariant ("a UPID with no node is rejected"). It +// FAILS at the recorded commit, which is the evidence the empty-node guard is +// missing on the PVE side. Do NOT weaken this test to make it pass; the fix is +// to reject Node=="" (and assert Node==c.node) in ParseUPID or in +// TaskStatusOnce/WaitTask. +func TestParseUPIDRejectsEmptyNode(t *testing.T) { + // Valid UPID parses cleanly (control). + const good = "UPID:demo-felhom:00026454:004E3431:6A265E53:vzdump:9021:root@pam:" + if u, err := ParseUPID(good); err != nil || u.Node != "demo-felhom" { + t.Fatalf("control: ParseUPID(good) = (%+v, %v); want Node=demo-felhom, nil err", u, err) + } + + // Empty node — should be rejected; currently is NOT. + const emptyNode = "UPID::00026454:004E3431:6A265E53:vzdump:9021:root@pam:" + u, err := ParseUPID(emptyNode) + if err == nil && u.Node == "" { + t.Fatalf("AGENT-T2-1: ParseUPID(%q) accepted an empty node (Node=%q, err=nil); "+ + "TaskStatusOnce/WaitTask would then request /nodes//tasks/... . "+ + "PVE side lacks the empty-node guard the PBS client has.", emptyNode, u.Node) + } +}