CI: refactoring pkg/csplugin tests (#2247)
This commit is contained in:
parent
f6544962ea
commit
12c32d507c
11 changed files with 449 additions and 550 deletions
2
go.mod
2
go.mod
|
@ -71,7 +71,7 @@ require (
|
||||||
github.com/bluele/gcache v0.0.2
|
github.com/bluele/gcache v0.0.2
|
||||||
github.com/cespare/xxhash/v2 v2.1.2
|
github.com/cespare/xxhash/v2 v2.1.2
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0
|
github.com/coreos/go-systemd/v22 v22.5.0
|
||||||
github.com/crowdsecurity/go-cs-lib v0.0.0-20230522120244-fa545c12e7ee
|
github.com/crowdsecurity/go-cs-lib v0.0.0-20230531105801-4c1535c2b3bd
|
||||||
github.com/goccy/go-yaml v1.9.7
|
github.com/goccy/go-yaml v1.9.7
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible
|
github.com/gofrs/uuid v4.0.0+incompatible
|
||||||
github.com/golang-jwt/jwt/v4 v4.2.0
|
github.com/golang-jwt/jwt/v4 v4.2.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -172,8 +172,8 @@ github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
|
||||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26 h1:r97WNVC30Uen+7WnLs4xDScS/Ex988+id2k6mDf8psU=
|
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26 h1:r97WNVC30Uen+7WnLs4xDScS/Ex988+id2k6mDf8psU=
|
||||||
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26/go.mod h1:zpv7r+7KXwgVUZnUNjyP22zc/D7LKjyoY02weH2RBbk=
|
github.com/crowdsecurity/dlog v0.0.0-20170105205344-4fb5f8204f26/go.mod h1:zpv7r+7KXwgVUZnUNjyP22zc/D7LKjyoY02weH2RBbk=
|
||||||
github.com/crowdsecurity/go-cs-lib v0.0.0-20230522120244-fa545c12e7ee h1:YD/SYJ0otjlyAxuLRzJNXTQyXcpccefTvaHextBT9mQ=
|
github.com/crowdsecurity/go-cs-lib v0.0.0-20230531105801-4c1535c2b3bd h1:Y70ceDKAKYFXTnxEjXuBDSh07umvDhbX3PCCYhdtsZ0=
|
||||||
github.com/crowdsecurity/go-cs-lib v0.0.0-20230522120244-fa545c12e7ee/go.mod h1:9JJLSpGj1ZXnROV3xAcJvS/HTaUvuA8K3gGOpO4tfVc=
|
github.com/crowdsecurity/go-cs-lib v0.0.0-20230531105801-4c1535c2b3bd/go.mod h1:9JJLSpGj1ZXnROV3xAcJvS/HTaUvuA8K3gGOpO4tfVc=
|
||||||
github.com/crowdsecurity/grokky v0.2.1 h1:t4VYnDlAd0RjDM2SlILalbwfCrQxtJSMGdQOR0zwkE4=
|
github.com/crowdsecurity/grokky v0.2.1 h1:t4VYnDlAd0RjDM2SlILalbwfCrQxtJSMGdQOR0zwkE4=
|
||||||
github.com/crowdsecurity/grokky v0.2.1/go.mod h1:33usDIYzGDsgX1kHAThCbseso6JuWNJXOzRQDGXHtWM=
|
github.com/crowdsecurity/grokky v0.2.1/go.mod h1:33usDIYzGDsgX1kHAThCbseso6JuWNJXOzRQDGXHtWM=
|
||||||
github.com/crowdsecurity/machineid v1.0.2 h1:wpkpsUghJF8Khtmn/tg6GxgdhLA1Xflerh5lirI+bdc=
|
github.com/crowdsecurity/machineid v1.0.2 h1:wpkpsUghJF8Khtmn/tg6GxgdhLA1Xflerh5lirI+bdc=
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
@ -375,22 +374,6 @@ func setRequiredFields(pluginCfg *PluginConfig) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper which gives paths to all files in the given directory non-recursively
|
|
||||||
func listFilesAtPath(path string) ([]string, error) {
|
|
||||||
filePaths := make([]string, 0)
|
|
||||||
files, err := os.ReadDir(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, file := range files {
|
|
||||||
if file.IsDir() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
filePaths = append(filePaths, filepath.Join(path, file.Name()))
|
|
||||||
}
|
|
||||||
return filePaths, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUUID() (string, error) {
|
func getUUID() (string, error) {
|
||||||
uuidv4, err := uuid.NewRandom()
|
uuidv4, err := uuid.NewRandom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
164
pkg/csplugin/broker_suite_test.go
Normal file
164
pkg/csplugin/broker_suite_test.go
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package csplugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type PluginSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
|
||||||
|
// where the plugin is built - temporary directory for the suite
|
||||||
|
buildDir string
|
||||||
|
// full path to the built plugin binary
|
||||||
|
builtBinary string
|
||||||
|
|
||||||
|
runDir string // temporary directory for each test
|
||||||
|
pluginDir string // (config_paths.plugin_dir)
|
||||||
|
notifDir string // (config_paths.notification_dir)
|
||||||
|
pluginBinary string // full path to the plugin binary (unique for each test)
|
||||||
|
pluginConfig string // full path to the notification config (unique for each test)
|
||||||
|
|
||||||
|
pluginBroker *PluginBroker
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func TestPluginSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(PluginSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (s *PluginSuite) SetupSuite() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
t := s.T()
|
||||||
|
|
||||||
|
s.buildDir, err = os.MkdirTemp("", "cs_plugin_test_build")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
s.builtBinary = path.Join(s.buildDir, "notification-dummy")
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
s.builtBinary += ".exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("go", "build", "-o", s.builtBinary, "../../plugins/notifications/dummy/")
|
||||||
|
err = cmd.Run()
|
||||||
|
require.NoError(t, err, "while building dummy plugin")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (s *PluginSuite) TearDownSuite() {
|
||||||
|
t := s.T()
|
||||||
|
err := os.RemoveAll(s.buildDir)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func copyFile(src string, dst string) error {
|
||||||
|
s, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
d, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(d, s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = d.Sync()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PluginSuite) SetupTest() {
|
||||||
|
s.SetupSubTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PluginSuite) TearDownTest() {
|
||||||
|
s.TearDownSubTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (s *PluginSuite) SetupSubTest() {
|
||||||
|
var err error
|
||||||
|
t := s.T()
|
||||||
|
|
||||||
|
s.runDir, err = os.MkdirTemp("", "cs_plugin_test")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
s.pluginDir = path.Join(s.runDir, "bin")
|
||||||
|
err = os.MkdirAll(path.Join(s.runDir, "bin"), 0o755)
|
||||||
|
require.NoError(t, err, "while creating bin dir")
|
||||||
|
|
||||||
|
s.notifDir = path.Join(s.runDir, "config")
|
||||||
|
err = os.MkdirAll(s.notifDir, 0o755)
|
||||||
|
require.NoError(t, err, "while creating config dir")
|
||||||
|
|
||||||
|
s.pluginBinary = path.Join(s.pluginDir, "notification-dummy")
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
s.pluginBinary += ".exe"
|
||||||
|
}
|
||||||
|
|
||||||
|
err = copyFile(s.builtBinary, s.pluginBinary)
|
||||||
|
require.NoError(t, err, "while copying built binary")
|
||||||
|
err = os.Chmod(s.pluginBinary, 0o744)
|
||||||
|
require.NoError(t, err, "chmod 0744 %s", s.pluginBinary)
|
||||||
|
|
||||||
|
s.pluginConfig = path.Join(s.notifDir, "dummy.yaml")
|
||||||
|
err = copyFile("testdata/dummy.yaml", s.pluginConfig)
|
||||||
|
require.NoError(t, err, "while copying plugin config")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PluginSuite) TearDownSubTest() {
|
||||||
|
t := s.T()
|
||||||
|
if s.pluginBroker != nil {
|
||||||
|
s.pluginBroker.Kill()
|
||||||
|
s.pluginBroker = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := os.RemoveAll(s.runDir)
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Remove("./out")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *PluginSuite) InitBroker(procCfg *csconfig.PluginCfg) (*PluginBroker, error) {
|
||||||
|
pb := PluginBroker{}
|
||||||
|
if procCfg == nil {
|
||||||
|
procCfg = &csconfig.PluginCfg{}
|
||||||
|
}
|
||||||
|
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
||||||
|
profiles = append(profiles, &csconfig.ProfileCfg{
|
||||||
|
Notifications: []string{"dummy_default"},
|
||||||
|
})
|
||||||
|
err := pb.Init(procCfg, profiles, &csconfig.ConfigurationPaths{
|
||||||
|
PluginDir: s.pluginDir,
|
||||||
|
NotificationDir: s.notifDir,
|
||||||
|
})
|
||||||
|
s.pluginBroker = &pb
|
||||||
|
return s.pluginBroker, err
|
||||||
|
}
|
|
@ -5,11 +5,6 @@ package csplugin
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -25,116 +20,39 @@ import (
|
||||||
"github.com/crowdsecurity/crowdsec/pkg/models"
|
"github.com/crowdsecurity/crowdsec/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testPath string
|
|
||||||
|
|
||||||
func setPluginPermTo744(t *testing.T) {
|
func (s *PluginSuite) permissionSetter(perm os.FileMode) func(*testing.T) {
|
||||||
setPluginPermTo(t, "744")
|
return func(t *testing.T) {
|
||||||
}
|
err := os.Chmod(s.pluginBinary, perm)
|
||||||
|
require.NoError(t, err, "chmod %s %s", perm, s.pluginBinary)
|
||||||
func setPluginPermTo722(t *testing.T) {
|
|
||||||
setPluginPermTo(t, "722")
|
|
||||||
}
|
|
||||||
|
|
||||||
func setPluginPermTo724(t *testing.T) {
|
|
||||||
setPluginPermTo(t, "724")
|
|
||||||
}
|
|
||||||
func TestGetPluginNameAndTypeFromPath(t *testing.T) {
|
|
||||||
setUp(t)
|
|
||||||
defer tearDown(t)
|
|
||||||
type args struct {
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
want1 string
|
|
||||||
expectedErr string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid plugin name, single dash",
|
|
||||||
args: args{
|
|
||||||
path: path.Join(testPath, "notification-gitter"),
|
|
||||||
},
|
|
||||||
want: "notification",
|
|
||||||
want1: "gitter",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid plugin name",
|
|
||||||
args: args{
|
|
||||||
path: "./tests/gitter",
|
|
||||||
},
|
|
||||||
expectedErr: "plugin name ./tests/gitter is invalid. Name should be like {type-name}",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "valid plugin name, multiple dash",
|
|
||||||
args: args{
|
|
||||||
path: "./tests/notification-instant-slack",
|
|
||||||
},
|
|
||||||
want: "notification-instant",
|
|
||||||
want1: "slack",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tc := range tests {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
got, got1, err := getPluginTypeAndSubtypeFromPath(tc.args.path)
|
|
||||||
cstest.RequireErrorContains(t, err, tc.expectedErr)
|
|
||||||
|
|
||||||
assert.Equal(t, tc.want, got)
|
|
||||||
assert.Equal(t, tc.want1, got1)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListFilesAtPath(t *testing.T) {
|
func (s *PluginSuite) readconfig() (PluginConfig) {
|
||||||
setUp(t)
|
var config PluginConfig
|
||||||
defer tearDown(t)
|
t := s.T()
|
||||||
type args struct {
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want []string
|
|
||||||
expectedErr string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
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/",
|
|
||||||
},
|
|
||||||
expectedErr: "open ./foo/bar/: " + cstest.FileNotFoundMessage,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tc := range tests {
|
|
||||||
tc := tc
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
got, err := listFilesAtPath(tc.args.path)
|
|
||||||
cstest.RequireErrorContains(t, err, tc.expectedErr)
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, tc.want) {
|
orig, err := os.ReadFile(s.pluginConfig)
|
||||||
t.Errorf("listFilesAtPath() = %v, want %v", got, tc.want)
|
require.NoError(t, err,"unable to read config file %s", s.pluginConfig)
|
||||||
}
|
|
||||||
})
|
err = yaml.Unmarshal(orig, &config)
|
||||||
}
|
require.NoError(t, err,"unable to unmarshal config file")
|
||||||
|
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokerInit(t *testing.T) {
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
t.Skip("Skipping test on windows")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
func (s *PluginSuite) writeconfig(config PluginConfig) {
|
||||||
|
t := s.T()
|
||||||
|
data, err := yaml.Marshal(&config)
|
||||||
|
require.NoError(t, err,"unable to marshal config file")
|
||||||
|
|
||||||
|
err = os.WriteFile(s.pluginConfig, data, 0644)
|
||||||
|
require.NoError(t, err,"unable to write config file %s", s.pluginConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (s *PluginSuite) TestBrokerInit() {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
action func(*testing.T)
|
action func(*testing.T)
|
||||||
|
@ -143,28 +61,30 @@ func TestBrokerInit(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "valid config",
|
name: "valid config",
|
||||||
action: setPluginPermTo744,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "group writable binary",
|
name: "group writable binary",
|
||||||
expectedErr: "notification-dummy is world writable",
|
expectedErr: "notification-dummy is world writable",
|
||||||
action: setPluginPermTo722,
|
action: s.permissionSetter(0o722),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "group writable binary",
|
name: "group writable binary",
|
||||||
expectedErr: "notification-dummy is group writable",
|
expectedErr: "notification-dummy is group writable",
|
||||||
action: setPluginPermTo724,
|
action: s.permissionSetter(0o724),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no plugin dir",
|
name: "no plugin dir",
|
||||||
expectedErr: cstest.FileNotFoundMessage,
|
expectedErr: cstest.FileNotFoundMessage,
|
||||||
action: tearDown,
|
action: func(t *testing.T) {
|
||||||
|
err := os.RemoveAll(s.runDir)
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no plugin binary",
|
name: "no plugin binary",
|
||||||
expectedErr: "binary for plugin dummy_default not found",
|
expectedErr: "binary for plugin dummy_default not found",
|
||||||
action: func(t *testing.T) {
|
action: func(t *testing.T) {
|
||||||
err := os.Remove(path.Join(testPath, "notification-dummy"))
|
err := os.Remove(s.pluginBinary)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -174,7 +94,6 @@ func TestBrokerInit(t *testing.T) {
|
||||||
procCfg: csconfig.PluginCfg{
|
procCfg: csconfig.PluginCfg{
|
||||||
User: "123445555551122toto",
|
User: "123445555551122toto",
|
||||||
},
|
},
|
||||||
action: setPluginPermTo744,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "only specify group",
|
name: "only specify group",
|
||||||
|
@ -182,7 +101,6 @@ func TestBrokerInit(t *testing.T) {
|
||||||
procCfg: csconfig.PluginCfg{
|
procCfg: csconfig.PluginCfg{
|
||||||
Group: "123445555551122toto",
|
Group: "123445555551122toto",
|
||||||
},
|
},
|
||||||
action: setPluginPermTo744,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Fails to run as root",
|
name: "Fails to run as root",
|
||||||
|
@ -191,7 +109,6 @@ func TestBrokerInit(t *testing.T) {
|
||||||
User: "root",
|
User: "root",
|
||||||
Group: "root",
|
Group: "root",
|
||||||
},
|
},
|
||||||
action: setPluginPermTo744,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Invalid user and group",
|
name: "Invalid user and group",
|
||||||
|
@ -200,7 +117,6 @@ func TestBrokerInit(t *testing.T) {
|
||||||
User: "toto1234",
|
User: "toto1234",
|
||||||
Group: "toto1234",
|
Group: "toto1234",
|
||||||
},
|
},
|
||||||
action: setPluginPermTo744,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Valid user and invalid group",
|
name: "Valid user and invalid group",
|
||||||
|
@ -209,79 +125,33 @@ func TestBrokerInit(t *testing.T) {
|
||||||
User: "nobody",
|
User: "nobody",
|
||||||
Group: "toto1234",
|
Group: "toto1234",
|
||||||
},
|
},
|
||||||
action: setPluginPermTo744,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range tests {
|
for _, tc := range tests {
|
||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
s.Run(tc.name, func() {
|
||||||
defer tearDown(t)
|
t := s.T()
|
||||||
buildDummyPlugin(t)
|
|
||||||
if tc.action != nil {
|
if tc.action != nil {
|
||||||
tc.action(t)
|
tc.action(t)
|
||||||
}
|
}
|
||||||
pb := PluginBroker{}
|
_, err := s.InitBroker(&tc.procCfg)
|
||||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
|
||||||
profiles = append(profiles, &csconfig.ProfileCfg{
|
|
||||||
Notifications: []string{"dummy_default"},
|
|
||||||
})
|
|
||||||
err := pb.Init(&tc.procCfg, profiles, &csconfig.ConfigurationPaths{
|
|
||||||
PluginDir: testPath,
|
|
||||||
NotificationDir: "./tests/notifications",
|
|
||||||
})
|
|
||||||
defer pb.Kill()
|
|
||||||
cstest.RequireErrorContains(t, err, tc.expectedErr)
|
cstest.RequireErrorContains(t, err, tc.expectedErr)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readconfig(t *testing.T, path string) ([]byte, PluginConfig) {
|
func (s *PluginSuite) TestBrokerNoThreshold() {
|
||||||
var config PluginConfig
|
|
||||||
orig, err := os.ReadFile("tests/notifications/dummy.yaml")
|
|
||||||
require.NoError(t, err,"unable to read config file %s", path)
|
|
||||||
|
|
||||||
err = yaml.Unmarshal(orig, &config)
|
|
||||||
require.NoError(t, err,"unable to unmarshal config file")
|
|
||||||
|
|
||||||
return orig, config
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeconfig(t *testing.T, config PluginConfig, path string) {
|
|
||||||
data, err := yaml.Marshal(&config)
|
|
||||||
require.NoError(t, err,"unable to marshal config file")
|
|
||||||
|
|
||||||
err = os.WriteFile(path, data, 0644)
|
|
||||||
require.NoError(t, err,"unable to write config file %s", path)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBrokerNoThreshold(t *testing.T) {
|
|
||||||
var alerts []models.Alert
|
var alerts []models.Alert
|
||||||
DefaultEmptyTicker = 50 * time.Millisecond
|
DefaultEmptyTicker = 50 * time.Millisecond
|
||||||
|
|
||||||
buildDummyPlugin(t)
|
t := s.T()
|
||||||
setPluginPermTo744(t)
|
|
||||||
defer tearDown(t)
|
|
||||||
|
|
||||||
// init
|
|
||||||
pluginCfg := csconfig.PluginCfg{}
|
|
||||||
pb := PluginBroker{}
|
|
||||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
|
||||||
profiles = append(profiles, &csconfig.ProfileCfg{
|
|
||||||
Notifications: []string{"dummy_default"},
|
|
||||||
})
|
|
||||||
|
|
||||||
// default config
|
|
||||||
err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{
|
|
||||||
PluginDir: testPath,
|
|
||||||
NotificationDir: "./tests/notifications",
|
|
||||||
})
|
|
||||||
|
|
||||||
|
pb, err := s.InitBroker(nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
tomb := tomb.Tomb{}
|
|
||||||
|
|
||||||
|
tomb := tomb.Tomb{}
|
||||||
go pb.Run(&tomb)
|
go pb.Run(&tomb)
|
||||||
defer pb.Kill()
|
|
||||||
|
|
||||||
// send one item, it should be processed right now
|
// send one item, it should be processed right now
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
|
@ -292,7 +162,7 @@ func TestBrokerNoThreshold(t *testing.T) {
|
||||||
require.NoError(t, err, "Error reading file")
|
require.NoError(t, err, "Error reading file")
|
||||||
|
|
||||||
err = json.Unmarshal(content, &alerts)
|
err = json.Unmarshal(content, &alerts)
|
||||||
assert.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Len(t, alerts, 1)
|
assert.Len(t, alerts, 1)
|
||||||
|
|
||||||
// remove it
|
// remove it
|
||||||
|
@ -313,34 +183,24 @@ func TestBrokerNoThreshold(t *testing.T) {
|
||||||
assert.Len(t, alerts, 1)
|
assert.Len(t, alerts, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokerRunGroupAndTimeThreshold_TimeFirst(t *testing.T) {
|
func (s *PluginSuite) TestBrokerRunGroupAndTimeThreshold_TimeFirst() {
|
||||||
// test grouping by "time"
|
// test grouping by "time"
|
||||||
DefaultEmptyTicker = 50 * time.Millisecond
|
DefaultEmptyTicker = 50 * time.Millisecond
|
||||||
buildDummyPlugin(t)
|
|
||||||
setPluginPermTo744(t)
|
|
||||||
defer tearDown(t)
|
|
||||||
|
|
||||||
// init
|
t := s.T()
|
||||||
pluginCfg := csconfig.PluginCfg{}
|
|
||||||
pb := PluginBroker{}
|
|
||||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
|
||||||
profiles = append(profiles, &csconfig.ProfileCfg{
|
|
||||||
Notifications: []string{"dummy_default"},
|
|
||||||
})
|
|
||||||
// set groupwait and groupthreshold, should honor whichever comes first
|
// set groupwait and groupthreshold, should honor whichever comes first
|
||||||
raw, cfg := readconfig(t, "tests/notifications/dummy.yaml")
|
cfg := s.readconfig()
|
||||||
cfg.GroupThreshold = 4
|
cfg.GroupThreshold = 4
|
||||||
cfg.GroupWait = 1 * time.Second
|
cfg.GroupWait = 1 * time.Second
|
||||||
writeconfig(t, cfg, "tests/notifications/dummy.yaml")
|
s.writeconfig(cfg)
|
||||||
err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{
|
|
||||||
PluginDir: testPath,
|
|
||||||
NotificationDir: "./tests/notifications",
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
tomb := tomb.Tomb{}
|
|
||||||
|
|
||||||
|
pb, err := s.InitBroker(nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
tomb := tomb.Tomb{}
|
||||||
go pb.Run(&tomb)
|
go pb.Run(&tomb)
|
||||||
defer pb.Kill()
|
|
||||||
// send data
|
// send data
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
|
@ -357,40 +217,24 @@ func TestBrokerRunGroupAndTimeThreshold_TimeFirst(t *testing.T) {
|
||||||
err = json.Unmarshal(content, &alerts)
|
err = json.Unmarshal(content, &alerts)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, alerts, 3)
|
assert.Len(t, alerts, 3)
|
||||||
|
|
||||||
// restore config
|
|
||||||
err = os.WriteFile("tests/notifications/dummy.yaml", raw, 0644)
|
|
||||||
require.NoError(t, err,"unable to write config file")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokerRunGroupAndTimeThreshold_CountFirst(t *testing.T) {
|
func (s *PluginSuite) TestBrokerRunGroupAndTimeThreshold_CountFirst() {
|
||||||
DefaultEmptyTicker = 50 * time.Millisecond
|
DefaultEmptyTicker = 50 * time.Millisecond
|
||||||
buildDummyPlugin(t)
|
|
||||||
setPluginPermTo(t, "744")
|
|
||||||
defer tearDown(t)
|
|
||||||
|
|
||||||
// init
|
t := s.T()
|
||||||
pluginCfg := csconfig.PluginCfg{}
|
|
||||||
pb := PluginBroker{}
|
|
||||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
|
||||||
profiles = append(profiles, &csconfig.ProfileCfg{
|
|
||||||
Notifications: []string{"dummy_default"},
|
|
||||||
})
|
|
||||||
|
|
||||||
// set groupwait and groupthreshold, should honor whichever comes first
|
// set groupwait and groupthreshold, should honor whichever comes first
|
||||||
raw, cfg := readconfig(t, "tests/notifications/dummy.yaml")
|
cfg := s.readconfig()
|
||||||
cfg.GroupThreshold = 4
|
cfg.GroupThreshold = 4
|
||||||
cfg.GroupWait = 4 * time.Second
|
cfg.GroupWait = 4 * time.Second
|
||||||
writeconfig(t, cfg, "tests/notifications/dummy.yaml")
|
s.writeconfig(cfg)
|
||||||
err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{
|
|
||||||
PluginDir: testPath,
|
|
||||||
NotificationDir: "./tests/notifications",
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
tomb := tomb.Tomb{}
|
|
||||||
|
|
||||||
|
pb, err := s.InitBroker(nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
tomb := tomb.Tomb{}
|
||||||
go pb.Run(&tomb)
|
go pb.Run(&tomb)
|
||||||
defer pb.Kill()
|
|
||||||
|
|
||||||
// send data
|
// send data
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
|
@ -411,41 +255,24 @@ func TestBrokerRunGroupAndTimeThreshold_CountFirst(t *testing.T) {
|
||||||
err = json.Unmarshal(content, &alerts)
|
err = json.Unmarshal(content, &alerts)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, alerts, 4)
|
assert.Len(t, alerts, 4)
|
||||||
|
|
||||||
// restore config
|
|
||||||
err = os.WriteFile("tests/notifications/dummy.yaml", raw, 0644)
|
|
||||||
require.NoError(t, err,"unable to write config file")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokerRunGroupThreshold(t *testing.T) {
|
func (s *PluginSuite) TestBrokerRunGroupThreshold() {
|
||||||
// test grouping by "size"
|
// test grouping by "size"
|
||||||
DefaultEmptyTicker = 50 * time.Millisecond
|
DefaultEmptyTicker = 50 * time.Millisecond
|
||||||
buildDummyPlugin(t)
|
|
||||||
setPluginPermTo(t, "744")
|
|
||||||
defer tearDown(t)
|
|
||||||
|
|
||||||
// init
|
t := s.T()
|
||||||
pluginCfg := csconfig.PluginCfg{}
|
|
||||||
pb := PluginBroker{}
|
|
||||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
|
||||||
profiles = append(profiles, &csconfig.ProfileCfg{
|
|
||||||
Notifications: []string{"dummy_default"},
|
|
||||||
})
|
|
||||||
|
|
||||||
// set groupwait
|
// set groupwait
|
||||||
raw, cfg := readconfig(t, "tests/notifications/dummy.yaml")
|
cfg := s.readconfig()
|
||||||
cfg.GroupThreshold = 4
|
cfg.GroupThreshold = 4
|
||||||
writeconfig(t, cfg, "tests/notifications/dummy.yaml")
|
s.writeconfig(cfg)
|
||||||
err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{
|
|
||||||
PluginDir: testPath,
|
|
||||||
NotificationDir: "./tests/notifications",
|
|
||||||
})
|
|
||||||
|
|
||||||
|
pb, err := s.InitBroker(nil)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
tomb := tomb.Tomb{}
|
|
||||||
|
|
||||||
|
tomb := tomb.Tomb{}
|
||||||
go pb.Run(&tomb)
|
go pb.Run(&tomb)
|
||||||
defer pb.Kill()
|
|
||||||
|
|
||||||
// send data
|
// send data
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
|
@ -466,39 +293,23 @@ func TestBrokerRunGroupThreshold(t *testing.T) {
|
||||||
err = json.Unmarshal(content, &alerts)
|
err = json.Unmarshal(content, &alerts)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, alerts, 4)
|
assert.Len(t, alerts, 4)
|
||||||
|
|
||||||
// restore config
|
|
||||||
err = os.WriteFile("tests/notifications/dummy.yaml", raw, 0644)
|
|
||||||
require.NoError(t, err, "unable to write config file")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokerRunTimeThreshold(t *testing.T) {
|
func (s *PluginSuite) TestBrokerRunTimeThreshold() {
|
||||||
DefaultEmptyTicker = 50 * time.Millisecond
|
DefaultEmptyTicker = 50 * time.Millisecond
|
||||||
buildDummyPlugin(t)
|
|
||||||
setPluginPermTo(t, "744")
|
|
||||||
defer tearDown(t)
|
|
||||||
|
|
||||||
// init
|
t := s.T()
|
||||||
pluginCfg := csconfig.PluginCfg{}
|
|
||||||
pb := PluginBroker{}
|
|
||||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
|
||||||
profiles = append(profiles, &csconfig.ProfileCfg{
|
|
||||||
Notifications: []string{"dummy_default"},
|
|
||||||
})
|
|
||||||
|
|
||||||
// set groupwait
|
// set groupwait
|
||||||
raw, cfg := readconfig(t, "tests/notifications/dummy.yaml")
|
cfg := s.readconfig()
|
||||||
cfg.GroupWait = 1 * time.Second
|
cfg.GroupWait = 1 * time.Second
|
||||||
writeconfig(t, cfg, "tests/notifications/dummy.yaml")
|
s.writeconfig(cfg)
|
||||||
err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{
|
|
||||||
PluginDir: testPath,
|
|
||||||
NotificationDir: "./tests/notifications",
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
tomb := tomb.Tomb{}
|
|
||||||
|
|
||||||
|
pb, err := s.InitBroker(nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
tomb := tomb.Tomb{}
|
||||||
go pb.Run(&tomb)
|
go pb.Run(&tomb)
|
||||||
defer pb.Kill()
|
|
||||||
|
|
||||||
// send data
|
// send data
|
||||||
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
|
||||||
|
@ -516,32 +327,18 @@ func TestBrokerRunTimeThreshold(t *testing.T) {
|
||||||
err = json.Unmarshal(content, &alerts)
|
err = json.Unmarshal(content, &alerts)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, alerts, 1)
|
assert.Len(t, alerts, 1)
|
||||||
|
|
||||||
// restore config
|
|
||||||
err = os.WriteFile("tests/notifications/dummy.yaml", raw, 0644)
|
|
||||||
require.NoError(t, err, "unable to write config file %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBrokerRunSimple(t *testing.T) {
|
func (s *PluginSuite) TestBrokerRunSimple() {
|
||||||
DefaultEmptyTicker = 50 * time.Millisecond
|
DefaultEmptyTicker = 50 * time.Millisecond
|
||||||
buildDummyPlugin(t)
|
|
||||||
setPluginPermTo(t, "744")
|
|
||||||
defer tearDown(t)
|
|
||||||
pluginCfg := csconfig.PluginCfg{}
|
|
||||||
pb := PluginBroker{}
|
|
||||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
|
||||||
profiles = append(profiles, &csconfig.ProfileCfg{
|
|
||||||
Notifications: []string{"dummy_default"},
|
|
||||||
})
|
|
||||||
err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{
|
|
||||||
PluginDir: testPath,
|
|
||||||
NotificationDir: "./tests/notifications",
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
tomb := tomb.Tomb{}
|
|
||||||
|
|
||||||
|
t := s.T()
|
||||||
|
|
||||||
|
pb, err := s.InitBroker(nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
tomb := tomb.Tomb{}
|
||||||
go pb.Run(&tomb)
|
go pb.Run(&tomb)
|
||||||
defer pb.Kill()
|
|
||||||
|
|
||||||
assert.NoFileExists(t, "./out")
|
assert.NoFileExists(t, "./out")
|
||||||
|
|
||||||
|
@ -559,47 +356,3 @@ func TestBrokerRunSimple(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, alerts, 2)
|
assert.Len(t, alerts, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildDummyPlugin(t *testing.T) {
|
|
||||||
dir, err := os.MkdirTemp("./tests", "cs_plugin_test")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
cmd := exec.Command("go", "build", "-o", path.Join(dir, "notification-dummy"), "../../plugins/notifications/dummy/")
|
|
||||||
err = cmd.Run()
|
|
||||||
require.NoError(t, err, "while building dummy plugin")
|
|
||||||
|
|
||||||
testPath = dir
|
|
||||||
os.Remove("./out")
|
|
||||||
}
|
|
||||||
|
|
||||||
func setPluginPermTo(t *testing.T, perm string) {
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
err := exec.Command("chmod", perm, path.Join(testPath, "notification-dummy")).Run()
|
|
||||||
require.NoError(t, err, "chmod 744 %s", path.Join(testPath, "notification-dummy"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setUp(t *testing.T) {
|
|
||||||
dir, err := os.MkdirTemp("./", "cs_plugin_test")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
f, err := os.Create(path.Join(dir, "slack"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
f.Close()
|
|
||||||
f, err = os.Create(path.Join(dir, "notification-gitter"))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
f.Close()
|
|
||||||
err = os.Mkdir(path.Join(dir, "dummy_dir"), 0666)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
testPath = dir
|
|
||||||
}
|
|
||||||
|
|
||||||
func tearDown(t *testing.T) {
|
|
||||||
err := os.RemoveAll(testPath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
os.Remove("./out")
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,16 +3,12 @@
|
||||||
package csplugin
|
package csplugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/tomb.v2"
|
"gopkg.in/tomb.v2"
|
||||||
|
|
||||||
"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
|
"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
|
||||||
|
@ -27,188 +23,55 @@ Due to the complexity of file permission modification with go on windows, we onl
|
||||||
not if it will actually reject plugins with invalid permissions
|
not if it will actually reject plugins with invalid permissions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var testPath string
|
func (s *PluginSuite) TestBrokerInit() {
|
||||||
|
|
||||||
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 {
|
|
||||||
tt := tt
|
|
||||||
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 {
|
|
||||||
tt := tt
|
|
||||||
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 {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
action func()
|
action func(*testing.T)
|
||||||
errContains string
|
|
||||||
wantErr bool
|
|
||||||
procCfg csconfig.PluginCfg
|
procCfg csconfig.PluginCfg
|
||||||
|
expectedErr string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "valid config",
|
name: "valid config",
|
||||||
wantErr: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no plugin dir",
|
name: "no plugin dir",
|
||||||
wantErr: true,
|
expectedErr: cstest.PathNotFoundMessage,
|
||||||
errContains: cstest.FileNotFoundMessage,
|
action: func(t *testing.T) {
|
||||||
action: tearDown,
|
err := os.RemoveAll(s.runDir)
|
||||||
|
require.NoError(t, err)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no plugin binary",
|
name: "no plugin binary",
|
||||||
wantErr: true,
|
expectedErr: "binary for plugin dummy_default not found",
|
||||||
errContains: "binary for plugin dummy_default not found",
|
action: func(t *testing.T) {
|
||||||
action: func() {
|
err := os.Remove(s.pluginBinary)
|
||||||
err := os.Remove(path.Join(testPath, "notification-dummy.exe"))
|
require.NoError(t, err)
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, tc := range tests {
|
||||||
test := test
|
tc := tc
|
||||||
t.Run(test.name, func(t *testing.T) {
|
s.Run(tc.name, func() {
|
||||||
defer tearDown()
|
t := s.T()
|
||||||
buildDummyPlugin()
|
if tc.action != nil {
|
||||||
if test.action != nil {
|
tc.action(t)
|
||||||
test.action()
|
|
||||||
}
|
}
|
||||||
pb := PluginBroker{}
|
_, err := s.InitBroker(&tc.procCfg)
|
||||||
profiles := csconfig.NewDefaultConfig().API.Server.Profiles
|
cstest.RequireErrorContains(t, err, tc.expectedErr)
|
||||||
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) {
|
func (s *PluginSuite) TestBrokerRun() {
|
||||||
buildDummyPlugin()
|
t := s.T()
|
||||||
defer tearDown()
|
|
||||||
procCfg := csconfig.PluginCfg{}
|
pb, err := s.InitBroker(nil)
|
||||||
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)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
tomb := tomb.Tomb{}
|
tomb := tomb.Tomb{}
|
||||||
go pb.Run(&tomb)
|
go pb.Run(&tomb)
|
||||||
defer pb.Kill()
|
|
||||||
|
|
||||||
assert.NoFileExists(t, "./out")
|
assert.NoFileExists(t, "./out")
|
||||||
defer os.Remove("./out")
|
defer os.Remove("./out")
|
||||||
|
@ -220,44 +83,3 @@ func TestBrokerRun(t *testing.T) {
|
||||||
assert.FileExists(t, ".\\out")
|
assert.FileExists(t, ".\\out")
|
||||||
assert.Equal(t, types.GetLineCountForFile(".\\out"), 2)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
22
pkg/csplugin/listfiles.go
Normal file
22
pkg/csplugin/listfiles.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package csplugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// helper which gives paths to all files in the given directory non-recursively
|
||||||
|
func listFilesAtPath(path string) ([]string, error) {
|
||||||
|
filePaths := make([]string, 0)
|
||||||
|
files, err := os.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
if ! file.IsDir() {
|
||||||
|
filePaths = append(filePaths, filepath.Join(path, file.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filePaths, nil
|
||||||
|
}
|
||||||
|
|
57
pkg/csplugin/listfiles_test.go
Normal file
57
pkg/csplugin/listfiles_test.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package csplugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListFilesAtPath(t *testing.T) {
|
||||||
|
dir, err := os.MkdirTemp("", "test-listfiles")
|
||||||
|
require.NoError(t, err)
|
||||||
|
t.Cleanup(func() {
|
||||||
|
os.RemoveAll(dir)
|
||||||
|
})
|
||||||
|
_, err = os.Create(filepath.Join(dir, "notification-gitter"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = os.Create(filepath.Join(dir, "slack"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = os.Mkdir(filepath.Join(dir, "somedir"), 0755)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = os.Create(filepath.Join(dir, "somedir", "inner"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
want []string
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid directory",
|
||||||
|
path: dir,
|
||||||
|
want: []string{
|
||||||
|
filepath.Join(dir, "notification-gitter"),
|
||||||
|
filepath.Join(dir, "slack"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid directory",
|
||||||
|
path: "./foo/bar/",
|
||||||
|
expectedErr: "open ./foo/bar/: " + cstest.PathNotFoundMessage,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
got, err := listFilesAtPath(tc.path)
|
||||||
|
cstest.RequireErrorContains(t, err, tc.expectedErr)
|
||||||
|
assert.ElementsMatch(t, tc.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
49
pkg/csplugin/utils_test.go
Normal file
49
pkg/csplugin/utils_test.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//go:build linux || freebsd || netbsd || openbsd || solaris || !windows
|
||||||
|
|
||||||
|
package csplugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetPluginNameAndTypeFromPath(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
want string
|
||||||
|
want1 string
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid plugin name, single dash",
|
||||||
|
path: "/path/to/notification-gitter",
|
||||||
|
want: "notification",
|
||||||
|
want1: "gitter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid plugin name",
|
||||||
|
path: "/path/to/gitter",
|
||||||
|
expectedErr: "plugin name /path/to/gitter is invalid. Name should be like {type-name}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid plugin name, multiple dash",
|
||||||
|
path: "/path/to/notification-instant-slack",
|
||||||
|
want: "notification-instant",
|
||||||
|
want1: "slack",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
got, got1, err := getPluginTypeAndSubtypeFromPath(tc.path)
|
||||||
|
cstest.RequireErrorContains(t, err, tc.expectedErr)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.want, got)
|
||||||
|
assert.Equal(t, tc.want1, got1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
49
pkg/csplugin/utils_windows_test.go
Normal file
49
pkg/csplugin/utils_windows_test.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package csplugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/crowdsecurity/go-cs-lib/pkg/cstest"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetPluginNameAndTypeFromPath(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
want string
|
||||||
|
want1 string
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid plugin name, single dash",
|
||||||
|
path: "c:\\path\\to\\notification-gitter",
|
||||||
|
want: "notification",
|
||||||
|
want1: "gitter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid plugin name",
|
||||||
|
path: "c:\\path\\to\\gitter.exe",
|
||||||
|
expectedErr: "plugin name c:\\path\\to\\gitter.exe is invalid. Name should be like {type-name}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid plugin name, multiple dash",
|
||||||
|
path: "c:\\path\\to\\notification-instant-slack.exe",
|
||||||
|
want: "notification-instant",
|
||||||
|
want1: "slack",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tc := range tests {
|
||||||
|
tc := tc
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
got, got1, err := getPluginTypeAndSubtypeFromPath(tc.path)
|
||||||
|
cstest.RequireErrorContains(t, err, tc.expectedErr)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.want, got)
|
||||||
|
assert.Equal(t, tc.want1, got1)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue