fix: harden workspace preprocessing contract
This commit is contained in:
@@ -95,7 +95,7 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
}
|
||||
result, operationErr := workspaceops.Run(ctx, installation, runner, workspaceCommand, nil)
|
||||
if operationErr != nil {
|
||||
if errors.Is(operationErr, compose.ErrOutputLimit) || strings.Contains(operationErr.Error(), "unsafe") || strings.Contains(operationErr.Error(), "exceeds") || strings.Contains(operationErr.Error(), "invalid workspace") {
|
||||
if workspaceUsageError(operationErr) {
|
||||
return commandUsageError(stderr, operationErr.Error())
|
||||
}
|
||||
fmt.Fprintf(stderr, "thothctl: %s\n", output.Sanitize(operationErr.Error(), secretValues))
|
||||
@@ -210,6 +210,17 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int {
|
||||
return writeResult(result, err, secretValues, stdout, stderr)
|
||||
}
|
||||
|
||||
func workspaceUsageError(err error) bool {
|
||||
if err == nil || errors.Is(err, compose.ErrOutputLimit) {
|
||||
return false
|
||||
}
|
||||
message := err.Error()
|
||||
// These are host-side grammar/local-file failures. Child envelope/result and
|
||||
// bounded-stream failures are operational and deliberately remain exit 1.
|
||||
return strings.Contains(message, "unsafe") || strings.Contains(message, "request exceeds") ||
|
||||
strings.Contains(message, "SQL input exceeds") || strings.Contains(message, "annotation input exceeds")
|
||||
}
|
||||
|
||||
func renderWorkspaceHuman(w io.Writer, result workspaceops.Result) {
|
||||
if result.Code == workspaceops.CodeRegistryBootstrapRecoveryConflict {
|
||||
fmt.Fprintln(w, "Bootstrap recovery is ambiguous or corrupt; inspect the installation registry jobs.")
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -104,6 +105,17 @@ func TestUsageDocumentsClosedConfigureUpdateSourcesAndMaintenanceRecovery(t *tes
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunWorkspaceRejectsInvalidCommandBeforeDocker(t *testing.T) {
|
||||
fixture := newCLIFixture(t, "")
|
||||
fixture.setEnvironment(t)
|
||||
var stdout, stderr bytes.Buffer
|
||||
code := run(context.Background(), []string{"--installation", fixture.installationPath, "workspace", "inspect", "--workspace", "PSd"}, &stdout, &stderr)
|
||||
if code != 2 || !strings.Contains(stderr.String(), "workspace") {
|
||||
t.Fatalf("exit=%d stderr=%q", code, stderr.String())
|
||||
}
|
||||
assertDockerNotInvoked(t, fixture)
|
||||
}
|
||||
|
||||
func TestRunSessionsMigrateRequiresExplicitConfirmationBeforeDocker(t *testing.T) {
|
||||
fixture := newCLIFixture(t, "")
|
||||
fixture.setProfile(t, "server")
|
||||
@@ -855,3 +867,16 @@ func TestRenderWorkspaceHumanHidesRecoveryIdentity(t *testing.T) {
|
||||
t.Fatalf("human output = %q", out.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkspaceOperationalEnvelopeFailuresAreExitOne(t *testing.T) {
|
||||
for _, err := range []error{compose.ErrOutputLimit, errors.New("invalid workspace result"), errors.New("invalid workspace request"), errors.New("invalid host export")} {
|
||||
if workspaceUsageError(err) {
|
||||
t.Errorf("classified operational error %q as usage", err)
|
||||
}
|
||||
}
|
||||
for _, err := range []error{errors.New("unsafe output file"), errors.New("request exceeds limit"), errors.New("unsafe SQL input")} {
|
||||
if !workspaceUsageError(err) {
|
||||
t.Errorf("classified host error %q as operational", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user