package podman import ( "bytes" "context" "os" "os/exec" "path/filepath" "reflect" "strings" "testing" "time" ) func TestCLIUsesSocketAsContainerHost(t *testing.T) { dir := t.TempDir() bin := filepath.Join(dir, "podman") shell, err := exec.LookPath("sh") if err != nil { t.Fatal(err) } if err := os.WriteFile(bin, []byte("#!"+shell+"\nprintf '%s' \"$CONTAINER_HOST\"\n"), 0o755); err != nil { t.Fatal(err) } t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH")) var stdout bytes.Buffer if err := (CLI{Socket: "unix:///tmp/podman.sock"}).Run(context.Background(), []string{"version"}, &stdout, &stdout); err != nil { t.Fatal(err) } if stdout.String() != "unix:///tmp/podman.sock" { t.Fatalf("CONTAINER_HOST = %q", stdout.String()) } } func TestStepScriptMarksAndTimesSteps(t *testing.T) { script := StepScript([]string{"echo first", "echo 'it works'"}) for _, want := range []string{ "TOTAL=2", "step 'echo first'", `step 'echo '\''it works'\'''`, "[luci] step %d/%d", } { if !strings.Contains(script, want) { t.Fatalf("script missing %q:\n%s", want, script) } } if strings.Contains(script, "step 'echo 'it works''") { t.Fatal("unescaped quote broke script") } } func TestStepScriptShell(t *testing.T) { if testing.Short() { t.Skip("shell round trip") } script := StepScript([]string{"X=set", "test \"$X\" = set", "true"}) cmd := exec.Command("/bin/sh", "-c", script) output, err := cmd.CombinedOutput() if err != nil { t.Fatalf("err=%v output=%s", err, output) } if !strings.Contains(string(output), "[luci] step 3/3 done in 0s") { t.Fatalf("output=%s", output) } failing := StepScript([]string{"false"}) cmd = exec.Command("/bin/sh", "-c", failing) output, err = cmd.CombinedOutput() if err == nil || !strings.Contains(string(output), "[luci] step 1/1 failed after") { t.Fatalf("err=%v output=%s", err, output) } } func TestBuildArgsUsesBaselineIsolation(t *testing.T) { got := BuildArgs(Spec{ Image: "docker.io/library/alpine:latest", Workspace: "/tmp/work", Memory: "2g", CPUs: "2", Env: map[string]string{"B": "2", "A": "1"}, Commands: []string{"echo ok"}, Timeout: time.Minute, }) want := []string{ "run", "--rm", "--pull=missing", "--userns=keep-id", "--cap-drop=all", "--security-opt=no-new-privileges", "--pids-limit=1024", "--memory=2g", "--cpus=2", "--timeout=60", "--workdir=/work", "--volume", "/tmp/work:/work:Z", "--env", "A", "--env", "B", "--", "docker.io/library/alpine:latest", "/bin/sh", "-c", StepScript([]string{"echo ok"}), } if !reflect.DeepEqual(got, want) { t.Fatalf("args = %#v", got) } if strings.Contains(strings.Join(got, "\x00"), "A=1") || strings.Contains(strings.Join(got, "\x00"), "B=2") { t.Fatalf("secret env value leaked into argv: %#v", got) } } func TestEnvironmentPassesValuesOnlyThroughChildEnvironment(t *testing.T) { dir := t.TempDir() bin := filepath.Join(dir, "podman") shell, err := exec.LookPath("sh") if err != nil { t.Fatal(err) } if err := os.WriteFile(bin, []byte("#!"+shell+"\nprintf '%s' \"$TOKEN\"\n"), 0o755); err != nil { t.Fatal(err) } t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH")) spec := Spec{Env: map[string]string{"TOKEN": "secret-value"}} args := BuildArgs(spec) if strings.Contains(strings.Join(args, "\x00"), "secret-value") { t.Fatalf("secret env value leaked into argv: %#v", args) } var stdout bytes.Buffer if err := (CLI{}).RunWithEnv(context.Background(), args, Environment(spec), &stdout, &stdout); err != nil { t.Fatal(err) } if stdout.String() != "secret-value" { t.Fatalf("child TOKEN = %q", stdout.String()) } }