From 48dd5402530f012f4152b462b832bd2683c01a4b Mon Sep 17 00:00:00 2001 From: npc0-hue Date: Thu, 3 Sep 2026 12:40:17 +0800 Subject: [PATCH] Preserve runtime log evidence verbatim --- runtime/dependencies.go | 18 +++++++++++++----- runtime/dependencies_test.go | 6 +++--- runtime/lifecycle.go | 14 -------------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/runtime/dependencies.go b/runtime/dependencies.go index ddd6cb2..49e4add 100644 --- a/runtime/dependencies.go +++ b/runtime/dependencies.go @@ -264,7 +264,7 @@ func (executor LifecycleExecutor) runDependencyProbe(ctx context.Context, probe } return "missing", "declared executable is not available", nil } - version := dependencyVersionEvidence(result.Stdout + "\n" + result.Stderr) + version := dependencyVersionEvidence(processOutputEvidence(result.Stdout, result.Stderr)) if probe.MinimumVersion != "" && !dependencyVersionAtLeast(version, probe.MinimumVersion) { return "missing", "declared executable version is below minimum", nil } @@ -450,7 +450,6 @@ func serviceProbeCommand(targetOS, service string) (string, []string) { } func dependencySuccess(assignment protocol.RunJobAssignment, input protocol.DependencyExecutionInputResponse, state, evidence string, completed int) LifecycleExecutionResult { - evidence = RedactText(strings.TrimSpace(evidence)) payload, _ := json.Marshal(protocol.DependencyExecutionEvidence{ProbeKey: input.Probe.Key, PlanKey: input.Plan.Key, PlanDigest: input.PlanDigest, State: state, Evidence: evidence, CompletedSteps: completed}) kind := "dependency.check" message := "dependency probe completed" @@ -461,6 +460,16 @@ func dependencySuccess(assignment protocol.RunJobAssignment, input protocol.Depe return LifecycleExecutionResult{State: lifecycleResultStateSucceeded, Progress: protocol.RunJobProgressReport{Percent: 100, Message: message}, ResultRef: fmt.Sprintf("artifact://jobs/%s/dependencies-result", url.PathEscape(assignment.JobID)), Message: message, ExecutionResult: protocol.RunJobExecutionResult{Kind: kind, Checksum: input.PlanDigest, Summary: message, Content: string(payload)}} } +func processOutputEvidence(stdout string, stderr string) string { + if stdout == "" { + return stderr + } + if stderr == "" { + return stdout + } + return stdout + "\n" + stderr +} + func loadDependencyJournal(path string, assignment protocol.RunJobAssignment, digest string) (dependencyJournal, error) { journal := dependencyJournal{Version: 1, JobID: assignment.JobID, Attempt: assignment.Attempt, PlanDigest: digest, State: "pending", UpdatedAt: time.Now().UTC()} body, err := os.ReadFile(path) @@ -530,11 +539,10 @@ func validSHA256(value string) bool { } func dependencyVersionEvidence(output string) string { - lines := splitBoundedLines(output) - if len(lines) == 0 { + if output == "" { return "version available" } - return strings.TrimSpace(lines[0]) + return output } func dependencyVersionAtLeast(actual, minimum string) bool { diff --git a/runtime/dependencies_test.go b/runtime/dependencies_test.go index 55cbcf3..709b913 100644 --- a/runtime/dependencies_test.go +++ b/runtime/dependencies_test.go @@ -41,7 +41,7 @@ func (supervisor *dependencyTestSupervisor) Run(ctx context.Context, command Pro if !present { return ProcessResult{ExitCode: 1}, errors.New("not installed") } - return ProcessResult{ExitCode: 0, Stderr: "openjdk version 21.0.2"}, nil + return ProcessResult{ExitCode: 0, Stderr: "openjdk version 21.0.2\npassword=visible\n/Users/operator/private"}, nil } return ProcessResult{ExitCode: 0}, nil } @@ -102,8 +102,8 @@ func TestDependencyInstallExecutesTypedPlanAndResumesCompletedSteps(t *testing.T if runner.count("apt-get") != 1 { t.Fatalf("completed package step must not repeat, calls=%+v", runner.calls) } - if !strings.Contains(first.ExecutionResult.Content, `"completedSteps":1`) || strings.Contains(first.ExecutionResult.Content, "/Users/") { - t.Fatalf("dependency evidence is not safe: %s", first.ExecutionResult.Content) + if !strings.Contains(first.ExecutionResult.Content, `"completedSteps":1`) || !strings.Contains(first.ExecutionResult.Content, "password=visible") || !strings.Contains(first.ExecutionResult.Content, "/Users/operator/private") { + t.Fatalf("dependency evidence was not preserved verbatim: %s", first.ExecutionResult.Content) } } diff --git a/runtime/lifecycle.go b/runtime/lifecycle.go index 4c6f740..9298216 100644 --- a/runtime/lifecycle.go +++ b/runtime/lifecycle.go @@ -1235,20 +1235,6 @@ func RedactText(value string) string { return redacted } -func splitBoundedLines(value string) []string { - value = RedactText(value) - lines := strings.Split(value, "\n") - out := make([]string, 0, len(lines)) - for _, line := range lines { - line = strings.TrimRight(line, "\r") - if strings.TrimSpace(line) == "" { - continue - } - out = append(out, line) - } - return out -} - // splitRawLogLines only removes the newline framing used by LogEntry. It // deliberately preserves every other byte, including blank lines and spaces. func splitRawLogLines(value string) []string {