From 44ef70ed3ecd03bb76ea4178a9f3b44ffd33faae Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2026 17:14:10 +0100 Subject: [PATCH] Exercise backend bootstrap portability and full terminal restoration Cover setup, Proxmox and container callers with a real PTY, isolated standalone and BusyBox tool selection, distinct fail-closed modes, private root/sudo history and one bounded command body. These controls expose the unchanged backend reader before its repair. Change-source: pulse-maintainer --- .../api/configapi/install_command_test.go | 15 +- .../api/configapi/portable_bootstrap_test.go | 272 ++++++++++++++++++ 2 files changed, 283 insertions(+), 4 deletions(-) create mode 100644 internal/api/configapi/portable_bootstrap_test.go diff --git a/internal/api/configapi/install_command_test.go b/internal/api/configapi/install_command_test.go index 9582204c84..0ed3e13afb 100644 --- a/internal/api/configapi/install_command_test.go +++ b/internal/api/configapi/install_command_test.go @@ -17,6 +17,11 @@ import errno, fcntl, json, os, pty, select, signal, subprocess, sys, termios, ti p = json.load(sys.stdin) master, slave = pty.openpty() tty_watch = os.dup(slave) +if p.get("alternate_tty"): + alternate = termios.tcgetattr(tty_watch) + alternate[0] &= ~termios.IXON + alternate[3] &= ~termios.ECHOK + termios.tcsetattr(tty_watch, termios.TCSANOW, alternate) initial_tty = termios.tcgetattr(tty_watch) def session(): os.setsid() @@ -76,7 +81,10 @@ while time.monotonic() < deadline: # the real bootstrap disables it before printing the input prompt. silent = not (termios.tcgetattr(tty_watch)[3] & termios.ECHO) if output.endswith(b"(paste at this prompt, not in the command): ") and command_ready and silent and not sent: - os.write(master, (p["input"] + "\n").encode()) + if p.get("signal_on_prompt"): + os.killpg(child.pid, getattr(signal, "SIG" + p["signal_on_prompt"])) + else: + os.write(master, (p["input"] + "\n").encode()) sent = True if child.poll() is not None and not ready: break else: @@ -84,9 +92,8 @@ else: output += b"\nPTY fixture deadline expired\n" code = child.wait() final_tty = termios.tcgetattr(tty_watch) -mode_mask = termios.ECHO | termios.ICANON -if (initial_tty[3] & mode_mask) != (final_tty[3] & mode_mask): - output += b"\nBootstrap did not restore terminal echo/canonical mode\n" +if initial_tty != final_tty: + output += b"\nBootstrap did not restore complete terminal settings\n" code = 1 os.close(tty_watch) os.close(master) diff --git a/internal/api/configapi/portable_bootstrap_test.go b/internal/api/configapi/portable_bootstrap_test.go new file mode 100644 index 0000000000..c7d249bb44 --- /dev/null +++ b/internal/api/configapi/portable_bootstrap_test.go @@ -0,0 +1,272 @@ +package configapi + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" +) + +type portableBootstrapCaller struct { + name string + build func(string) string +} + +func portableBootstrapCallers() []portableBootstrapCaller { + return []portableBootstrapCaller{ + {"proxmox", func(token string) string { + return BuildProxmoxAgentInstallCommand(AgentInstallCommandOptions{BaseURL: "https://pulse.example", Token: token, InstallType: "pbs", IncludeInstallType: true}) + }}, + {"setup", func(token string) string { + return BuildSetupScriptCommand("https://pulse.example/api/setup-script?type=pbs", token) + }}, + {"container", func(token string) string { + return BuildContainerRuntimeAgentInstallCommand("https://pulse.example", token, false) + }}, + } +} + +// PATH contains only the prerequisite tools and our closed download/privilege +// fixtures. The real controlling PTY is never a customer or appliance terminal. +// Explicit proof inputs can supply the exact static BusyBox used on CI hosts. +func portableBootstrapFixture(t *testing.T, mode string) (string, []string) { + t.Helper() + root, env := bootstrapFixture(t, recordingInstaller) + bin := filepath.Join(root, "bin") + for _, name := range []string{"bash", "cp", "rm", "rmdir", "cat", "stat", "dirname", "touch"} { + path, err := exec.LookPath(name) + if err != nil { + t.Fatal(err) + } + if err := os.Symlink(path, filepath.Join(bin, name)); err != nil { + t.Fatal(err) + } + } + if mode == "standalone" || mode == "prefer-standalone" { + stty, err := exec.LookPath("stty") + if err != nil { + t.Fatal(err) + } + writeExecutable(t, filepath.Join(bin, "stty"), `#!/bin/sh +printf '%s\n' "$*" >> "$FAKE_ROOT/tty-calls" +case "${FAKE_TTY_FAILURE:-}:$1" in + empty:-g) exit 0 ;; + snapshot:-g) exit 1 ;; + echo:-echo) exit 1 ;; +esac +if [ "${FAKE_TTY_FAILURE:-}" = restore ] && [ "$1" != -g ] && [ "$1" != -echo ] && [ ! -e "$FAKE_ROOT/restore-failed" ]; then + touch "$FAKE_ROOT/restore-failed" + exit 1 +fi +exec `+posixShellQuote(stty)+` "$@" +`) + } + if mode == "busybox" { + path := os.Getenv("PULSE_TEST_BUSYBOX") + if path == "" { + var err error + path, err = exec.LookPath("busybox") + if err != nil { + t.Skip("real BusyBox input unavailable; modeled tool and standalone controls still run") + } + } + body, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + if err := os.Mkdir(filepath.Join(root, "native"), 0700); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(root, "native", "busybox"), body, 0700); err != nil { + t.Fatal(err) + } + writeExecutable(t, filepath.Join(bin, "busybox"), `#!/bin/sh +printf '%s\n' "$*" >> "$FAKE_ROOT/tty-calls" +exec "$FAKE_ROOT/native/busybox" "$@" +`) + } else if mode == "unsupported-busybox" || mode == "prefer-standalone" { + writeExecutable(t, filepath.Join(bin, "busybox"), `#!/bin/sh +printf '%s\n' "$*" >> "$FAKE_ROOT/busybox-calls" +exit 97 +`) + } + return root, append(env, "PATH="+bin) +} + +func runPortableBootstrap(t *testing.T, command, input string, env []string, history, signalName string) ([]byte, error) { + t.Helper() + payload, err := json.Marshal(map[string]any{ + "command": command, "input": input, "history": history, + "signal_on_prompt": signalName, "alternate_tty": true, + }) + if err != nil { + t.Fatal(err) + } + python, err := exec.LookPath("python3") + if err != nil { + t.Fatal(err) + } + cmd := exec.Command(python, "-c", bootstrapPTYRunner) + cmd.Stdin = strings.NewReader(string(payload)) + cmd.Env = append(os.Environ(), env...) + return cmd.CombinedOutput() +} + +func assertPortableBootstrapPrivate(t *testing.T, root, token string, out []byte) { + t.Helper() + for _, name := range []string{"argv", "shell-history", "secret-env"} { + body, err := os.ReadFile(filepath.Join(root, name)) + if err != nil && !os.IsNotExist(err) { + t.Fatal(err) + } + if token != "" && strings.Contains(string(body), token) { + t.Fatalf("private input reached %s", name) + } + if name == "secret-env" && err == nil && string(body) != ":" { + t.Fatal("installer received a credential through the environment") + } + } + if token != "" && strings.Contains(string(out), token) { + t.Fatal("private input was echoed") + } + if strings.Contains(string(out), "Bootstrap did not restore") || strings.Contains(string(out), "PTY fixture deadline expired") { + t.Fatal("complete terminal restoration or bounded execution failed") + } + assertContainerBootstrapCleanup(t, root) +} + +func TestPrivateBootstrapPortableToolFailures(t *testing.T) { + for _, caller := range portableBootstrapCallers() { + for _, tc := range []struct { + name, mode, fault, message string + tty bool + }{ + {"no-terminal", "absent", "", "A terminal is required to enter the token.", false}, + {"no-tool", "absent", "", "Silent token input requires stty or BusyBox stty.", true}, + {"unsupported-applet", "unsupported-busybox", "", "Cannot read terminal settings with stty or BusyBox stty.", true}, + {"empty-state", "standalone", "empty", "Cannot read terminal settings with stty or BusyBox stty.", true}, + {"failed-state", "standalone", "snapshot", "Cannot read terminal settings with stty or BusyBox stty.", true}, + {"failed-echo", "standalone", "echo", "Cannot disable terminal echo.", true}, + {"failed-restore", "standalone", "restore", "Cannot restore terminal settings.", true}, + } { + t.Run(caller.name+"/"+tc.name, func(t *testing.T) { + root, env := portableBootstrapFixture(t, tc.mode) + env = append(env, "FAKE_TTY_FAILURE="+tc.fault) + command := caller.build("issued-but-not-in-command") + var out []byte + var err error + if tc.tty { + out, err = runPortableBootstrap(t, command, "synthetic-private-token", env, "", "") + } else { + out, err = runBootstrap(t, command, "", env, false) + } + if err == nil || !strings.Contains(string(out), tc.message) { + t.Fatalf("expected distinct fail-closed %s diagnostic; exit=%v, matched=%t", tc.name, err, strings.Contains(string(out), tc.message)) + } + if _, err := os.Stat(filepath.Join(root, "captured-token")); !os.IsNotExist(err) { + t.Fatal("installation preceded safe terminal restoration") + } + if tc.name != "failed-restore" && strings.Contains(string(out), "(paste at this prompt, not in the command): ") { + t.Fatal("credential requested before silent input was available") + } + if tc.name == "failed-restore" { + calls, err := os.ReadFile(filepath.Join(root, "tty-calls")) + lines := strings.Split(strings.TrimSpace(string(calls)), "\n") + if err != nil || len(lines) != 4 || lines[0] != "-g" || lines[1] != "-echo" || lines[2] != lines[3] { + t.Fatal("cleanup did not retry the same full state with the selected tool") + } + } + assertPortableBootstrapPrivate(t, root, "synthetic-private-token", out) + }) + } + } +} + +func TestPrivateBootstrapRealBusyBoxInput(t *testing.T) { + for _, caller := range portableBootstrapCallers() { + for _, tc := range []struct { + name, input, signalName string + failure bool + }{ + {"ordinary", strings.Repeat("p", 32), "", false}, + {"maximum", strings.Repeat("q", 4096), "", false}, + {"overflow", strings.Repeat("r", 10000) + "-DISCARDED-CREDENTIAL", "", true}, + {"empty", "", "", true}, + {"interrupt", "\x03", "", true}, + {"hangup", "", "HUP", true}, + {"terminate", "", "TERM", true}, + } { + t.Run(caller.name+"/"+tc.name, func(t *testing.T) { + root, env := portableBootstrapFixture(t, "busybox") + command := caller.build("issued-separately") + out, err := runPortableBootstrap(t, command, tc.input, env, "", tc.signalName) + if (err != nil) != tc.failure { + t.Fatalf("BusyBox bootstrap result=%v, expected failure=%t", err, tc.failure) + } + captured, captureErr := os.ReadFile(filepath.Join(root, "captured-token")) + if tc.failure { + if !os.IsNotExist(captureErr) { + t.Fatal("invalid or interrupted entry reached installation") + } + } else if captureErr != nil || string(captured) != tc.input { + t.Fatal("BusyBox reader lost accepted input") + } + calls, err := os.ReadFile(filepath.Join(root, "tty-calls")) + lines := strings.Split(strings.TrimSpace(string(calls)), "\n") + if err != nil || len(lines) != 3 || lines[0] != "stty -g" || lines[1] != "stty -echo" || !strings.HasPrefix(lines[2], "stty ") { + t.Fatal("snapshot, silent mode and full restoration did not use the selected real BusyBox applet") + } + assertPortableBootstrapPrivate(t, root, tc.input, out) + }) + } + } +} + +func TestPrivateBootstrapStandalonePreferredAndBusyBoxHistory(t *testing.T) { + for _, caller := range portableBootstrapCallers() { + for _, mode := range []string{"prefer-standalone", "busybox"} { + for _, uid := range []string{"0", "1000"} { + t.Run(fmt.Sprintf("%s/%s/uid-%s", caller.name, mode, uid), func(t *testing.T) { + root, env := portableBootstrapFixture(t, mode) + env = append(env, "FAKE_ID_UID="+uid) + token := "synthetic-history-token-0123456789" + command := caller.build(token) + out, err := runPortableBootstrap(t, command, token, env, filepath.Join(root, "shell-history"), "") + if err != nil { + t.Fatalf("interactive portable bootstrap failed: %v", err) + } + history, err := os.ReadFile(filepath.Join(root, "shell-history")) + if err != nil || !strings.Contains(string(history), command) || !strings.HasSuffix(string(history), "exit\n") { + t.Fatal("caller did not record the complete command and regain the terminal") + } + captured, err := os.ReadFile(filepath.Join(root, "captured-token")) + if err != nil || string(captured) != token { + t.Fatal("interactive private handoff lost input") + } + if _, err := os.Stat(filepath.Join(root, "busybox-calls")); !os.IsNotExist(err) { + t.Fatal("failed standalone tool selection fell back to another utility") + } + assertPortableBootstrapPrivate(t, root, token, out) + }) + } + } + } +} + +func TestPrivateBootstrapSinglePrivilegedBody(t *testing.T) { + for _, caller := range portableBootstrapCallers() { + t.Run(caller.name, func(t *testing.T) { + command := caller.build("private-not-in-command") + if strings.Count(command, "bash -c ") != 1 || !strings.Contains(command, `then set --; else set -- sudo; fi;`) || !strings.Contains(command, `"$@" bash -c `) { + t.Fatal("private reader is duplicated or root/sudo prefix escaped its subshell") + } + if len(command) >= 4096 || strings.ContainsAny(command, "\r\n") { + t.Fatalf("ordinary single-line command does not fit the common terminal limit: %d bytes", len(command)) + } + t.Logf("ordinary %s command: %d bytes", caller.name, len(command)) + }) + } +}