broker_win_test.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. //go:build windows
  2. package csplugin
  3. import (
  4. "log"
  5. "os"
  6. "os/exec"
  7. "path"
  8. "path/filepath"
  9. "reflect"
  10. "testing"
  11. "time"
  12. "github.com/crowdsecurity/crowdsec/pkg/csconfig"
  13. "github.com/crowdsecurity/crowdsec/pkg/models"
  14. "github.com/crowdsecurity/crowdsec/pkg/types"
  15. "github.com/stretchr/testify/assert"
  16. "gopkg.in/tomb.v2"
  17. )
  18. /*
  19. Due to the complexity of file permission modification with go on windows, we only test the basic behavior the broker,
  20. not if it will actually reject plugins with invalid permissions
  21. */
  22. var testPath string
  23. func TestGetPluginNameAndTypeFromPath(t *testing.T) {
  24. setUp()
  25. defer tearDown()
  26. type args struct {
  27. path string
  28. }
  29. tests := []struct {
  30. name string
  31. args args
  32. want string
  33. want1 string
  34. wantErr bool
  35. }{
  36. {
  37. name: "valid plugin name, single dash",
  38. args: args{
  39. path: path.Join(testPath, "notification-gitter"),
  40. },
  41. want: "notification",
  42. want1: "gitter",
  43. wantErr: false,
  44. },
  45. {
  46. name: "invalid plugin name",
  47. args: args{
  48. path: ".\\tests\\gitter.exe",
  49. },
  50. want: "",
  51. want1: "",
  52. wantErr: true,
  53. },
  54. {
  55. name: "valid plugin name, multiple dash",
  56. args: args{
  57. path: ".\\tests\\notification-instant-slack.exe",
  58. },
  59. want: "notification-instant",
  60. want1: "slack",
  61. wantErr: false,
  62. },
  63. }
  64. for _, tt := range tests {
  65. t.Run(tt.name, func(t *testing.T) {
  66. got, got1, err := getPluginTypeAndSubtypeFromPath(tt.args.path)
  67. if (err != nil) != tt.wantErr {
  68. t.Errorf("getPluginNameAndTypeFromPath() error = %v, wantErr %v", err, tt.wantErr)
  69. return
  70. }
  71. if got != tt.want {
  72. t.Errorf("getPluginNameAndTypeFromPath() got = %v, want %v", got, tt.want)
  73. }
  74. if got1 != tt.want1 {
  75. t.Errorf("getPluginNameAndTypeFromPath() got1 = %v, want %v", got1, tt.want1)
  76. }
  77. })
  78. }
  79. }
  80. func TestListFilesAtPath(t *testing.T) {
  81. setUp()
  82. defer tearDown()
  83. type args struct {
  84. path string
  85. }
  86. tests := []struct {
  87. name string
  88. args args
  89. want []string
  90. wantErr bool
  91. }{
  92. {
  93. name: "valid directory",
  94. args: args{
  95. path: testPath,
  96. },
  97. want: []string{
  98. filepath.Join(testPath, "notification-gitter"),
  99. filepath.Join(testPath, "slack"),
  100. },
  101. },
  102. {
  103. name: "invalid directory",
  104. args: args{
  105. path: "./foo/bar/",
  106. },
  107. wantErr: true,
  108. },
  109. }
  110. for _, tt := range tests {
  111. t.Run(tt.name, func(t *testing.T) {
  112. got, err := listFilesAtPath(tt.args.path)
  113. if (err != nil) != tt.wantErr {
  114. t.Errorf("listFilesAtPath() error = %v, wantErr %v", err, tt.wantErr)
  115. return
  116. }
  117. if !reflect.DeepEqual(got, tt.want) {
  118. t.Errorf("listFilesAtPath() = %v, want %v", got, tt.want)
  119. }
  120. })
  121. }
  122. }
  123. func TestBrokerInit(t *testing.T) {
  124. tests := []struct {
  125. name string
  126. action func()
  127. errContains string
  128. wantErr bool
  129. procCfg csconfig.PluginCfg
  130. }{
  131. {
  132. name: "valid config",
  133. wantErr: false,
  134. },
  135. {
  136. name: "no plugin dir",
  137. wantErr: true,
  138. errContains: "The system cannot find the file specified.",
  139. action: tearDown,
  140. },
  141. {
  142. name: "no plugin binary",
  143. wantErr: true,
  144. errContains: "binary for plugin dummy_default not found",
  145. action: func() {
  146. err := os.Remove(path.Join(testPath, "notification-dummy.exe"))
  147. if err != nil {
  148. t.Fatal(err)
  149. }
  150. },
  151. },
  152. }
  153. for _, test := range tests {
  154. t.Run(test.name, func(t *testing.T) {
  155. defer tearDown()
  156. buildDummyPlugin()
  157. if test.action != nil {
  158. test.action()
  159. }
  160. pb := PluginBroker{}
  161. profiles := csconfig.NewDefaultConfig().API.Server.Profiles
  162. profiles = append(profiles, &csconfig.ProfileCfg{
  163. Notifications: []string{"dummy_default"},
  164. })
  165. err := pb.Init(&test.procCfg, profiles, &csconfig.ConfigurationPaths{
  166. PluginDir: testPath,
  167. NotificationDir: "./tests/notifications",
  168. })
  169. defer pb.Kill()
  170. if test.wantErr {
  171. assert.ErrorContains(t, err, test.errContains)
  172. } else {
  173. assert.NoError(t, err)
  174. }
  175. })
  176. }
  177. }
  178. func TestBrokerRun(t *testing.T) {
  179. buildDummyPlugin()
  180. defer tearDown()
  181. procCfg := csconfig.PluginCfg{}
  182. pb := PluginBroker{}
  183. profiles := csconfig.NewDefaultConfig().API.Server.Profiles
  184. profiles = append(profiles, &csconfig.ProfileCfg{
  185. Notifications: []string{"dummy_default"},
  186. })
  187. err := pb.Init(&procCfg, profiles, &csconfig.ConfigurationPaths{
  188. PluginDir: testPath,
  189. NotificationDir: "./tests/notifications",
  190. })
  191. assert.NoError(t, err)
  192. tomb := tomb.Tomb{}
  193. go pb.Run(&tomb)
  194. defer pb.Kill()
  195. assert.NoFileExists(t, "./out")
  196. defer os.Remove("./out")
  197. pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
  198. pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
  199. time.Sleep(time.Second * 4)
  200. assert.FileExists(t, ".\\out")
  201. assert.Equal(t, types.GetLineCountForFile(".\\out"), 2)
  202. }
  203. func buildDummyPlugin() {
  204. dir, err := os.MkdirTemp(".\\tests", "cs_plugin_test")
  205. if err != nil {
  206. log.Fatal(err)
  207. }
  208. cmd := exec.Command("go", "build", "-o", path.Join(dir, "notification-dummy.exe"), "../../plugins/notifications/dummy/")
  209. if err := cmd.Run(); err != nil {
  210. log.Fatal(err)
  211. }
  212. testPath = dir
  213. }
  214. func setUp() {
  215. dir, err := os.MkdirTemp("./", "cs_plugin_test")
  216. if err != nil {
  217. log.Fatal(err)
  218. }
  219. f, err := os.Create(path.Join(dir, "slack"))
  220. if err != nil {
  221. log.Fatal(err)
  222. }
  223. f.Close()
  224. f, err = os.Create(path.Join(dir, "notification-gitter"))
  225. if err != nil {
  226. log.Fatal(err)
  227. }
  228. f.Close()
  229. err = os.Mkdir(path.Join(dir, "dummy_dir"), 0666)
  230. if err != nil {
  231. log.Fatal(err)
  232. }
  233. testPath = dir
  234. }
  235. func tearDown() {
  236. err := os.RemoveAll(testPath)
  237. if err != nil {
  238. log.Fatal(err)
  239. }
  240. }