package localapi import ( "context" "io" "os" "regexp" "testing" ) // stagingRecorderRunner is a fake proxmox.Runner recording every call vector and snapshotting each // install SOURCE file's content at call time (the deferred os.Remove erases it afterwards). type stagingRecorderRunner struct { calls [][]string srcContent map[string]string // install dest → staged source content } func (r *stagingRecorderRunner) Run(_ context.Context, name string, args ...string) ([]byte, []byte, error) { r.calls = append(r.calls, append([]string{name}, args...)) if name == "install" && len(args) >= 2 { src, dest := args[len(args)-2], args[len(args)-1] if r.srcContent == nil { r.srcContent = map[string]string{} } b, _ := os.ReadFile(src) r.srcContent[dest] = string(b) } return nil, nil, nil } func (r *stagingRecorderRunner) RunStdin(ctx context.Context, _ io.Reader, name string, args ...string) ([]byte, []byte, error) { return r.Run(ctx, name, args...) } // installSources returns the install-call source paths keyed by destination. func (r *stagingRecorderRunner) installSources() map[string][]string { out := map[string][]string{} for _, c := range r.calls { if c[0] == "install" && len(c) >= 3 { src, dest := c[len(c)-2], c[len(c)-1] out[dest] = append(out[dest], src) } } return out } // TestInstallSharedParent_RandomTempName is the audit-B1 negative test for the shared-parent boot // persistence install: both staged install SOURCES (script + unit) must be RANDOM os.CreateTemp names // (felhom-shared-parent-.sh / .service), never the fixed, pre-creatable /tmp names (a local // TOCTOU into a root-executed boot script), and two consecutive installs must use DIFFERENT paths. func TestInstallSharedParent_RandomTempName(t *testing.T) { r := &stagingRecorderRunner{} b := NewGuestBinder(r, nil) if err := b.installSharedParentUnit(context.Background()); err != nil { t.Fatalf("installSharedParentUnit #1: %v", err) } if err := b.installSharedParentUnit(context.Background()); err != nil { t.Fatalf("installSharedParentUnit #2: %v", err) } srcs := r.installSources() cases := []struct { dest string random *regexp.Regexp fixed *regexp.Regexp content string }{ {sharedParentScriptPath, regexp.MustCompile(`felhom-shared-parent-[^/\\]+\.sh$`), regexp.MustCompile(`felhom-shared-parent\.sh$`), sharedParentScript}, {sharedParentUnitPath, regexp.MustCompile(`felhom-shared-parent-[^/\\]+\.service$`), regexp.MustCompile(`felhom-shared-parent\.service$`), sharedParentUnit}, } for _, tc := range cases { got := srcs[tc.dest] if len(got) != 2 { t.Fatalf("dest %s: expected 2 install calls, got %d (%v)", tc.dest, len(got), got) } for i, src := range got { if !tc.random.MatchString(src) { t.Errorf("dest %s call %d: source %q does not match the random temp pattern", tc.dest, i, src) } if tc.fixed.MatchString(src) { t.Errorf("dest %s call %d: source %q is the FIXED predictable temp name (B1 TOCTOU)", tc.dest, i, src) } if _, err := os.Stat(src); err == nil { t.Errorf("dest %s: staged temp %q left behind (defer os.Remove missing)", tc.dest, src) } } if got[0] == got[1] { t.Errorf("dest %s: two consecutive installs staged through the SAME source %q — must be random per call", tc.dest, got[0]) } // Non-hollow: the staged file carried the real content at install time. if r.srcContent[tc.dest] != tc.content { t.Errorf("dest %s: staged content mismatch (got %d bytes, want %d)", tc.dest, len(r.srcContent[tc.dest]), len(tc.content)) } } }