package cli import ( "crypto/ed25519" "crypto/rand" "errors" "net" "strings" "testing" "time" "golang.org/x/crypto/ssh" "debtik/internal/auth" ) func TestSSHExecReportsFailureAndPreservesLongCommands(t *testing.T) { _, private, err := ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatal(err) } signer, err := ssh.NewSignerFromKey(private) if err != nil { t.Fatal(err) } config := &ssh.ServerConfig{NoClientAuth: true} config.AddHostKey(signer) l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } defer l.Close() server := &SSHServer{executor: &Executor{}} done := make(chan struct{}) go func() { defer close(done) raw, err := l.Accept() if err != nil { return } defer raw.Close() conn, chans, reqs, err := ssh.NewServerConn(raw, config) if err != nil { return } defer conn.Close() go ssh.DiscardRequests(reqs) for ch := range chans { channel, requests, err := ch.Accept() if err == nil { go server.handleSession(channel, requests, &auth.PublicUser{Role: auth.RoleFull}) } } }() client, err := ssh.Dial("tcp", l.Addr().String(), &ssh.ClientConfig{User: "test", HostKeyCallback: ssh.FixedHostKey(signer.PublicKey()), Timeout: 3 * time.Second}) if err != nil { t.Fatal(err) } defer func() { client.Close(); <-done }() for _, command := range []string{"/log", "/help", "unknown", "/interface print", strings.Repeat("x", 70000)} { session, err := client.NewSession() if err != nil { t.Fatal(err) } output, err := session.CombinedOutput(command) session.Close() if command == "/log" || command == "/help" { if err != nil { t.Fatalf("valid command failed: %v", err) } } else { var exit *ssh.ExitError if !errors.As(err, &exit) || exit.ExitStatus() != 1 { t.Fatalf("failure reported success: %v", err) } if len(command) > 65535 && !strings.Contains(string(output), command) { t.Fatal("long command truncated") } } } }