package guesthook import ( "context" "io" "os" "regexp" "testing" ) // recordingRunner is a fake proxmox.Runner that records every call and snapshots the content of the // install SOURCE file at call time (the deferred os.Remove would erase it before the test can look). type recordingRunner struct { calls [][]string srcContent []string } func (r *recordingRunner) Run(_ context.Context, name string, args ...string) ([]byte, []byte, error) { r.calls = append(r.calls, append([]string{name}, args...)) if name == "install" && len(args) > 0 { src := args[len(args)-2] b, _ := os.ReadFile(src) r.srcContent = append(r.srcContent, string(b)) } return nil, nil, nil } func (r *recordingRunner) RunStdin(ctx context.Context, _ io.Reader, name string, args ...string) ([]byte, []byte, error) { return r.Run(ctx, name, args...) } // TestInstallSnippet_RandomTempName is the audit-B1 negative test: the staged install SOURCE must be a // RANDOM os.CreateTemp name (felhom-guest-hook-.sh), never the fixed, pre-creatable // /tmp/felhom-guest-hook.sh (a local TOCTOU into a root-executed hookscript), and two consecutive // installs must stage through DIFFERENT paths. func TestInstallSnippet_RandomTempName(t *testing.T) { r := &recordingRunner{} if err := InstallSnippet(context.Background(), r); err != nil { t.Fatalf("InstallSnippet #1: %v", err) } if err := InstallSnippet(context.Background(), r); err != nil { t.Fatalf("InstallSnippet #2: %v", err) } var installs [][]string for _, call := range r.calls { if call[0] == "install" { installs = append(installs, call) } } if len(installs) != 2 { t.Fatalf("expected 2 install calls, got %d: %v", len(installs), r.calls) } randomName := regexp.MustCompile(`felhom-guest-hook-[^/\\]+\.sh$`) fixedName := regexp.MustCompile(`felhom-guest-hook\.sh$`) var srcs []string for i, call := range installs { // install -m 0755 -- if len(call) != 6 { t.Fatalf("call %d: unexpected vector %v", i, call) } src, dest := call[4], call[5] if dest != SnippetPath { t.Errorf("call %d: dest = %q, want %q", i, dest, SnippetPath) } if !randomName.MatchString(src) { t.Errorf("call %d: source %q does not match the random felhom-guest-hook-*.sh pattern", i, src) } if fixedName.MatchString(src) { t.Errorf("call %d: source %q is the FIXED predictable temp name (B1 TOCTOU)", i, src) } srcs = append(srcs, src) } if srcs[0] == srcs[1] { t.Errorf("two consecutive installs staged through the SAME source path %q — must be random per call", srcs[0]) } // Non-hollow: the staged file must actually carry the snippet body at install time. for i, c := range r.srcContent { if c != snippetBody { t.Errorf("call %d: staged content is not the snippet body (got %d bytes)", i, len(c)) } } // And the temp is cleaned up after. for _, src := range srcs { if _, err := os.Stat(src); err == nil { t.Errorf("staged temp %q left behind (defer os.Remove missing)", src) } } } // B2 Scenario D (DRILL-day0-cleanroom-2026-07-03): on a fresh PVE, /var/lib/vz/snippets does not // exist and `install` (no -D) cannot create it — the drill saw // `install: cannot create regular file … No such file or directory` and the guest silently got no // pre-start self-heal hook. InstallSnippet must therefore issue a `mkdir -p ` fenced op // BEFORE the `install` op. Pre-fix wrong outcome: no mkdir call at all — only the doomed install. func TestInstallSnippet_EnsuresSnippetsDirFirst(t *testing.T) { r := &recordingRunner{} if err := InstallSnippet(context.Background(), r); err != nil { t.Fatalf("InstallSnippet: %v", err) } mkdirIdx, installIdx := -1, -1 for i, call := range r.calls { switch call[0] { case "mkdir": if mkdirIdx == -1 { mkdirIdx = i want := []string{"mkdir", "-p", SnippetDir} if len(call) != 3 || call[1] != want[1] || call[2] != want[2] { t.Errorf("mkdir vector = %v, want %v (the sudoers fence matches exactly this argv)", call, want) } } case "install": if installIdx == -1 { installIdx = i } } } if mkdirIdx == -1 { t.Fatalf("no `mkdir -p %s` op issued — on a fresh box the snippet install fails ENOENT (B2); calls: %v", SnippetDir, r.calls) } if installIdx == -1 { t.Fatalf("no install op issued; calls: %v", r.calls) } if mkdirIdx > installIdx { t.Fatalf("mkdir (call %d) must PRECEDE install (call %d) — order: %v", mkdirIdx, installIdx, r.calls) } }