crowdsec/pkg/csplugin/broker_win_test.go
2022-08-26 13:31:49 +02:00

257 lines
5.5 KiB
Go

//go:build windows
package csplugin
import (
"log"
"os"
"os/exec"
"path"
"path/filepath"
"reflect"
"testing"
"time"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/stretchr/testify/assert"
"gopkg.in/tomb.v2"
)
/*
Due to the complexity of file permission modification with go on windows, we only test the basic behavior the broker,
not if it will actually reject plugins with invalid permissions
*/
var testPath string
func TestGetPluginNameAndTypeFromPath(t *testing.T) {
setUp()
defer tearDown()
type args struct {
path string
}
tests := []struct {
name string
args args
want string
want1 string
wantErr bool
}{
{
name: "valid plugin name, single dash",
args: args{
path: path.Join(testPath, "notification-gitter"),
},
want: "notification",
want1: "gitter",
wantErr: false,
},
{
name: "invalid plugin name",
args: args{
path: ".\\tests\\gitter.exe",
},
want: "",
want1: "",
wantErr: true,
},
{
name: "valid plugin name, multiple dash",
args: args{
path: ".\\tests\\notification-instant-slack.exe",
},
want: "notification-instant",
want1: "slack",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1, err := getPluginTypeAndSubtypeFromPath(tt.args.path)
if (err != nil) != tt.wantErr {
t.Errorf("getPluginNameAndTypeFromPath() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("getPluginNameAndTypeFromPath() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("getPluginNameAndTypeFromPath() got1 = %v, want %v", got1, tt.want1)
}
})
}
}
func TestListFilesAtPath(t *testing.T) {
setUp()
defer tearDown()
type args struct {
path string
}
tests := []struct {
name string
args args
want []string
wantErr bool
}{
{
name: "valid directory",
args: args{
path: testPath,
},
want: []string{
filepath.Join(testPath, "notification-gitter"),
filepath.Join(testPath, "slack"),
},
},
{
name: "invalid directory",
args: args{
path: "./foo/bar/",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := listFilesAtPath(tt.args.path)
if (err != nil) != tt.wantErr {
t.Errorf("listFilesAtPath() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("listFilesAtPath() = %v, want %v", got, tt.want)
}
})
}
}
func TestBrokerInit(t *testing.T) {
tests := []struct {
name string
action func()
errContains string
wantErr bool
procCfg csconfig.PluginCfg
}{
{
name: "valid config",
wantErr: false,
},
{
name: "no plugin dir",
wantErr: true,
errContains: "The system cannot find the file specified.",
action: tearDown,
},
{
name: "no plugin binary",
wantErr: true,
errContains: "binary for plugin dummy_default not found",
action: func() {
err := os.Remove(path.Join(testPath, "notification-dummy.exe"))
if err != nil {
t.Fatal(err)
}
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
defer tearDown()
buildDummyPlugin()
if test.action != nil {
test.action()
}
pb := PluginBroker{}
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
profiles = append(profiles, &csconfig.ProfileCfg{
Notifications: []string{"dummy_default"},
})
err := pb.Init(&test.procCfg, profiles, &csconfig.ConfigurationPaths{
PluginDir: testPath,
NotificationDir: "./tests/notifications",
})
defer pb.Kill()
if test.wantErr {
assert.ErrorContains(t, err, test.errContains)
} else {
assert.NoError(t, err)
}
})
}
}
func TestBrokerRun(t *testing.T) {
buildDummyPlugin()
defer tearDown()
procCfg := csconfig.PluginCfg{}
pb := PluginBroker{}
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
profiles = append(profiles, &csconfig.ProfileCfg{
Notifications: []string{"dummy_default"},
})
err := pb.Init(&procCfg, profiles, &csconfig.ConfigurationPaths{
PluginDir: testPath,
NotificationDir: "./tests/notifications",
})
assert.NoError(t, err)
tomb := tomb.Tomb{}
go pb.Run(&tomb)
defer pb.Kill()
assert.NoFileExists(t, "./out")
defer os.Remove("./out")
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
time.Sleep(time.Second * 4)
assert.FileExists(t, ".\\out")
assert.Equal(t, types.GetLineCountForFile(".\\out"), 2)
}
func buildDummyPlugin() {
dir, err := os.MkdirTemp(".\\tests", "cs_plugin_test")
if err != nil {
log.Fatal(err)
}
cmd := exec.Command("go", "build", "-o", path.Join(dir, "notification-dummy.exe"), "../../plugins/notifications/dummy/")
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
testPath = dir
}
func setUp() {
dir, err := os.MkdirTemp("./", "cs_plugin_test")
if err != nil {
log.Fatal(err)
}
f, err := os.Create(path.Join(dir, "slack"))
if err != nil {
log.Fatal(err)
}
f.Close()
f, err = os.Create(path.Join(dir, "notification-gitter"))
if err != nil {
log.Fatal(err)
}
f.Close()
err = os.Mkdir(path.Join(dir, "dummy_dir"), 0666)
if err != nil {
log.Fatal(err)
}
testPath = dir
}
func tearDown() {
err := os.RemoveAll(testPath)
if err != nil {
log.Fatal(err)
}
}