main_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // License: GPLv3 Copyright: 2023, Kovid Goyal, <kovid at kovidgoyal.net>
  2. package ssh
  3. import (
  4. "encoding/binary"
  5. "encoding/json"
  6. "fmt"
  7. "io/fs"
  8. "kitty"
  9. "kitty/tools/utils/shm"
  10. "os"
  11. "os/exec"
  12. "path"
  13. "path/filepath"
  14. "strings"
  15. "testing"
  16. "github.com/google/go-cmp/cmp"
  17. "golang.org/x/sys/unix"
  18. )
  19. var _ = fmt.Print
  20. func TestCloneEnv(t *testing.T) {
  21. env := map[string]string{"a": "1", "b": "2"}
  22. data, err := json.Marshal(env)
  23. if err != nil {
  24. t.Fatal(err)
  25. }
  26. mmap, err := shm.CreateTemp("", 128)
  27. if err != nil {
  28. t.Fatal(err)
  29. }
  30. defer mmap.Unlink()
  31. copy(mmap.Slice()[4:], data)
  32. binary.BigEndian.PutUint32(mmap.Slice(), uint32(len(data)))
  33. mmap.Close()
  34. x, err := add_cloned_env(mmap.Name())
  35. if err != nil {
  36. t.Fatal(err)
  37. }
  38. diff := cmp.Diff(env, x)
  39. if diff != "" {
  40. t.Fatalf("Failed to deserialize env\n%s", diff)
  41. }
  42. }
  43. func basic_connection_data(overrides ...string) *connection_data {
  44. ans := &connection_data{
  45. script_type: "sh", request_id: "123-123", remote_args: []string{},
  46. username: "testuser", hostname_for_match: "host.test",
  47. dont_create_shm: true,
  48. }
  49. opts, bad_lines, err := load_config(ans.hostname_for_match, ans.username, overrides)
  50. if err != nil {
  51. panic(err)
  52. }
  53. if len(bad_lines) != 0 {
  54. panic(fmt.Sprintf("Bad config lines: %s with error: %s", bad_lines[0].Line, bad_lines[0].Err))
  55. }
  56. ans.host_opts = opts
  57. return ans
  58. }
  59. func TestSSHBootstrapScriptLimit(t *testing.T) {
  60. cd := basic_connection_data()
  61. err := get_remote_command(cd)
  62. if err != nil {
  63. t.Fatal(err)
  64. }
  65. total := 0
  66. for _, x := range cd.rcmd {
  67. total += len(x)
  68. }
  69. if total > 9000 {
  70. t.Fatalf("Bootstrap script too large: %d bytes", total)
  71. }
  72. }
  73. func TestSSHTarfile(t *testing.T) {
  74. tdir := t.TempDir()
  75. cd := basic_connection_data()
  76. data, err := make_tarfile(cd, func(key string) (val string, found bool) { return })
  77. if err != nil {
  78. t.Fatal(err)
  79. }
  80. cmd := exec.Command("tar", "xpzf", "-", "-C", tdir)
  81. cmd.Stderr = os.Stderr
  82. inp, err := cmd.StdinPipe()
  83. if err != nil {
  84. t.Fatal(err)
  85. }
  86. err = cmd.Start()
  87. if err != nil {
  88. t.Fatal(err)
  89. }
  90. _, err = inp.Write(data)
  91. if err != nil {
  92. t.Fatal(err)
  93. }
  94. inp.Close()
  95. err = cmd.Wait()
  96. if err != nil {
  97. t.Fatal(err)
  98. }
  99. seen := map[string]bool{}
  100. err = filepath.WalkDir(tdir, func(name string, d fs.DirEntry, werr error) error {
  101. if werr != nil {
  102. return werr
  103. }
  104. rname, werr := filepath.Rel(tdir, name)
  105. if werr != nil {
  106. return werr
  107. }
  108. rname = strings.ReplaceAll(rname, "\\", "/")
  109. if rname == "." {
  110. return nil
  111. }
  112. fi, werr := d.Info()
  113. if werr != nil {
  114. return werr
  115. }
  116. if fi.Mode().Perm()&0o600 == 0 {
  117. return fmt.Errorf("%s is not rw for its owner. Actual permissions: %s", rname, fi.Mode().String())
  118. }
  119. seen[rname] = true
  120. return nil
  121. })
  122. if err != nil {
  123. t.Fatal(err)
  124. }
  125. if !seen["data.sh"] {
  126. t.Fatalf("data.sh missing")
  127. }
  128. for _, x := range []string{".terminfo/kitty.terminfo", ".terminfo/x/" + kitty.DefaultTermName} {
  129. if !seen["home/"+x] {
  130. t.Fatalf("%s missing", x)
  131. }
  132. }
  133. for _, x := range []string{"shell-integration/bash/kitty.bash", "shell-integration/fish/vendor_completions.d/kitty.fish"} {
  134. if !seen[path.Join("home", cd.host_opts.Remote_dir, x)] {
  135. t.Fatalf("%s missing", x)
  136. }
  137. }
  138. for _, x := range []string{"kitty", "kitten"} {
  139. p := filepath.Join(tdir, "home", cd.host_opts.Remote_dir, "kitty", "bin", x)
  140. if err = unix.Access(p, unix.X_OK); err != nil {
  141. t.Fatalf("Cannot execute %s with error: %s", x, err)
  142. }
  143. }
  144. if seen[path.Join("home", cd.host_opts.Remote_dir, "shell-integration", "ssh", "kitten")] {
  145. t.Fatalf("Contents of shell-integration/ssh not excluded")
  146. }
  147. }