Hardened subprocess stdio wiring and failure diagnostics
This commit is contained in:
@@ -59,12 +59,17 @@ func TestRunFailureReturnsUsefulError(t *testing.T) {
|
||||
t.Fatalf("os.Executable() error = %v", err)
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
stdoutPath := filepath.Join(dir, "stdout.log")
|
||||
stderrPath := filepath.Join(dir, "stderr.log")
|
||||
req := RunRequest{
|
||||
Executable: exe,
|
||||
Args: []string{"-test.run=TestSubprocessHelper", "--", "fail"},
|
||||
EnvOverrides: map[string]string{
|
||||
"GO_WANT_SUBPROCESS_HELPER": "1",
|
||||
},
|
||||
StdoutLogPath: stdoutPath,
|
||||
StderrLogPath: stderrPath,
|
||||
}
|
||||
|
||||
res, err := Run(context.Background(), req)
|
||||
@@ -80,6 +85,41 @@ func TestRunFailureReturnsUsefulError(t *testing.T) {
|
||||
if !strings.Contains(err.Error(), exe) {
|
||||
t.Fatalf("error = %q, want executable context", err.Error())
|
||||
}
|
||||
if !strings.Contains(err.Error(), stdoutPath) || !strings.Contains(err.Error(), stderrPath) {
|
||||
t.Fatalf("error = %q, want stdout/stderr log paths", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunFailureRedactsSensitiveTail(t *testing.T) {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatalf("os.Executable() error = %v", err)
|
||||
}
|
||||
|
||||
secretValue := "super-secret-value"
|
||||
dir := t.TempDir()
|
||||
req := RunRequest{
|
||||
Executable: exe,
|
||||
Args: []string{"-test.run=TestSubprocessHelper", "--", "failsecret"},
|
||||
EnvOverrides: map[string]string{
|
||||
"GO_WANT_SUBPROCESS_HELPER": "1",
|
||||
"API_KEY": secretValue,
|
||||
"SUBPROCESS_HELPER_ENV_KEY": "API_KEY",
|
||||
},
|
||||
StdoutLogPath: filepath.Join(dir, "stdout.log"),
|
||||
StderrLogPath: filepath.Join(dir, "stderr.log"),
|
||||
}
|
||||
|
||||
_, err = Run(context.Background(), req)
|
||||
if err == nil {
|
||||
t.Fatal("Run() error = nil, want non-nil")
|
||||
}
|
||||
if strings.Contains(err.Error(), secretValue) {
|
||||
t.Fatalf("error leaked secret value: %q", err.Error())
|
||||
}
|
||||
if !strings.Contains(err.Error(), "<redacted>") {
|
||||
t.Fatalf("error = %q, want redacted stderr tail marker", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTimeout(t *testing.T) {
|
||||
@@ -170,6 +210,43 @@ func TestRunEnvOverridesWinOverInheritedValues(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunSharedStdoutStderrLogPath(t *testing.T) {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatalf("os.Executable() error = %v", err)
|
||||
}
|
||||
|
||||
dir := t.TempDir()
|
||||
sharedLogPath := filepath.Join(dir, "shared.log")
|
||||
req := RunRequest{
|
||||
Executable: exe,
|
||||
Args: []string{"-test.run=TestSubprocessHelper", "--", "success"},
|
||||
EnvOverrides: map[string]string{
|
||||
"GO_WANT_SUBPROCESS_HELPER": "1",
|
||||
"SUBPROCESS_HELPER_STDOUT": "shared-out",
|
||||
"SUBPROCESS_HELPER_STDERR": "shared-err",
|
||||
},
|
||||
StdoutLogPath: sharedLogPath,
|
||||
StderrLogPath: sharedLogPath,
|
||||
}
|
||||
|
||||
res, err := Run(context.Background(), req)
|
||||
if err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
if res.ExitCode != 0 {
|
||||
t.Fatalf("ExitCode = %d, want 0", res.ExitCode)
|
||||
}
|
||||
data, err := os.ReadFile(sharedLogPath)
|
||||
if err != nil {
|
||||
t.Fatalf("read shared log: %v", err)
|
||||
}
|
||||
text := string(data)
|
||||
if !strings.Contains(text, "shared-out") || !strings.Contains(text, "shared-err") {
|
||||
t.Fatalf("shared log = %q, want both stdout and stderr content", text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteYAMLAtomic(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "config.generated.yml")
|
||||
@@ -248,6 +325,10 @@ func TestSubprocessHelper(t *testing.T) {
|
||||
case "fail":
|
||||
_, _ = os.Stderr.WriteString("intentional failure\n")
|
||||
os.Exit(3)
|
||||
case "failsecret":
|
||||
key := os.Getenv("SUBPROCESS_HELPER_ENV_KEY")
|
||||
_, _ = os.Stderr.WriteString("secret:" + os.Getenv(key) + "\n")
|
||||
os.Exit(4)
|
||||
case "sleep":
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
os.Exit(0)
|
||||
|
||||
Reference in New Issue
Block a user