diff options
author | JJ | 2024-07-19 20:56:29 +0000 |
---|---|---|
committer | JJ | 2024-07-19 20:56:29 +0000 |
commit | 2cffb8800d857e031178e084f602a78282490937 (patch) | |
tree | a069c36349b834100322a0966c1cf5ca8e6f206b | |
parent | b3b126389079889cf62be5ee1aa92779ca1fcd20 (diff) |
remove tests
-rw-r--r-- | application_context_test.go | 75 | ||||
-rw-r--r-- | application_router_test.go | 209 | ||||
-rw-r--r-- | caronte_test.go | 71 | ||||
-rw-r--r-- | connection_handler_test.go | 227 | ||||
-rw-r--r-- | pcap_importer_test.go | 177 | ||||
-rw-r--r-- | rules_manager_test.go | 302 | ||||
-rw-r--r-- | storage_test.go | 258 | ||||
-rw-r--r-- | stream_handler_test.go | 359 |
8 files changed, 0 insertions, 1678 deletions
diff --git a/application_context_test.go b/application_context_test.go deleted file mode 100644 index 11d1ed4..0000000 --- a/application_context_test.go +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of caronte (https://github.com/eciavatta/caronte). - * Copyright (c) 2020 Emiliano Ciavatta. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package main - -import ( - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - "testing" -) - -func TestCreateApplicationContext(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(Settings) - - appContext, err := CreateApplicationContext(wrapper.Storage, "test") - assert.NoError(t, err) - assert.False(t, appContext.IsConfigured) - assert.Zero(t, appContext.Config) - assert.Len(t, appContext.Accounts, 0) - assert.Nil(t, appContext.PcapImporter) - assert.Nil(t, appContext.RulesManager) - - notificationController := NewNotificationController(appContext) - appContext.SetNotificationController(notificationController) - assert.Equal(t, notificationController, appContext.NotificationController) - - config := Config{ - ServerAddress: "10.10.10.10", - FlagRegex: "FLAG{test}", - AuthRequired: true, - } - accounts := gin.Accounts{ - "username": "password", - } - appContext.SetConfig(config) - appContext.SetAccounts(accounts) - assert.Equal(t, appContext.Config, config) - assert.Equal(t, appContext.Accounts, accounts) - assert.NotNil(t, appContext.PcapImporter) - assert.NotNil(t, appContext.RulesManager) - assert.True(t, appContext.IsConfigured) - - config.FlagRegex = "FLAG{test2}" - accounts["username"] = "password2" - appContext.SetConfig(config) - appContext.SetAccounts(accounts) - - checkAppContext, err := CreateApplicationContext(wrapper.Storage, "test") - assert.NoError(t, err) - checkAppContext.SetNotificationController(notificationController) - checkAppContext.Configure() - assert.True(t, checkAppContext.IsConfigured) - assert.Equal(t, checkAppContext.Config, config) - assert.Equal(t, checkAppContext.Accounts, accounts) - assert.NotNil(t, checkAppContext.PcapImporter) - assert.NotNil(t, checkAppContext.RulesManager) - assert.Equal(t, notificationController, appContext.NotificationController) - - wrapper.Destroy(t) -} diff --git a/application_router_test.go b/application_router_test.go deleted file mode 100644 index d4b545f..0000000 --- a/application_router_test.go +++ /dev/null @@ -1,209 +0,0 @@ -/* - * This file is part of caronte (https://github.com/eciavatta/caronte). - * Copyright (c) 2020 Emiliano Ciavatta. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package main - -import ( - "bytes" - "encoding/json" - "github.com/gin-gonic/gin" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "io" - "net/http" - "net/http/httptest" - "testing" - "time" -) - -func TestSetupApplication(t *testing.T) { - toolkit := NewRouterTestToolkit(t, false) - - settings := make(map[string]interface{}) - assert.Equal(t, http.StatusServiceUnavailable, toolkit.MakeRequest("GET", "/api/rules", nil).Code) - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("POST", "/setup", settings).Code) - settings["config"] = Config{ServerAddress: "1.2.3.4", FlagRegex: "FLAG{test}", AuthRequired: true} - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("POST", "/setup", settings).Code) - settings["accounts"] = gin.Accounts{"username": "password"} - assert.Equal(t, http.StatusAccepted, toolkit.MakeRequest("POST", "/setup", settings).Code) - assert.Equal(t, http.StatusNotFound, toolkit.MakeRequest("POST", "/setup", settings).Code) - - toolkit.wrapper.Destroy(t) -} - -func TestAuthRequired(t *testing.T) { - toolkit := NewRouterTestToolkit(t, true) - - assert.Equal(t, http.StatusOK, toolkit.MakeRequest("GET", "/api/rules", nil).Code) - config := toolkit.appContext.Config - config.AuthRequired = true - toolkit.appContext.SetConfig(config) - toolkit.appContext.SetAccounts(gin.Accounts{"username": "password"}) - assert.Equal(t, http.StatusUnauthorized, toolkit.MakeRequest("GET", "/api/rules", nil).Code) - - toolkit.wrapper.Destroy(t) -} - -func TestRulesApi(t *testing.T) { - toolkit := NewRouterTestToolkit(t, true) - - // AddRule - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("POST", "/api/rules", Rule{}).Code) - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("POST", "/api/rules", - Rule{Name: "testRule"}).Code) - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("POST", "/api/rules", - Rule{Name: "testRule", Color: "invalidColor"}).Code) - w := toolkit.MakeRequest("POST", "/api/rules", Rule{Name: "testRule", Color: "#fff"}) - var testRuleID struct{ ID string } - assert.Equal(t, http.StatusOK, w.Code) - assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &testRuleID)) - assert.Equal(t, http.StatusUnprocessableEntity, toolkit.MakeRequest("POST", "/api/rules", - Rule{Name: "testRule", Color: "#fff"}).Code) // same name - - // UpdateRule - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("PUT", "/api/rules/invalidID", - Rule{Name: "invalidRule", Color: "#000"}).Code) - assert.Equal(t, http.StatusNotFound, toolkit.MakeRequest("PUT", "/api/rules/000000000000000000000000", - Rule{Name: "invalidRule", Color: "#000"}).Code) - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("PUT", "/api/rules/"+testRuleID.ID, Rule{}).Code) - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("PUT", "/api/rules/"+testRuleID.ID, - Rule{Name: "invalidRule", Color: "invalidColor"}).Code) - w = toolkit.MakeRequest("POST", "/api/rules", Rule{Name: "testRule2", Color: "#eee"}) - var testRule2ID struct{ ID string } - assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &testRule2ID)) - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("PUT", "/api/rules/"+testRule2ID.ID, - Rule{Name: "testRule", Color: "#fff"}).Code) // duplicate - w = toolkit.MakeRequest("PUT", "/api/rules/"+testRuleID.ID, Rule{Name: "newRule1", Color: "#ddd"}) - var testRule Rule - assert.Equal(t, http.StatusOK, w.Code) - assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &testRule)) - assert.Equal(t, "newRule1", testRule.Name) - assert.Equal(t, "#ddd", testRule.Color) - - // GetRule - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("GET", "/api/rules/invalidID", nil).Code) - assert.Equal(t, http.StatusNotFound, toolkit.MakeRequest("GET", "/api/rules/000000000000000000000000", nil).Code) - w = toolkit.MakeRequest("GET", "/api/rules/"+testRuleID.ID, nil) - assert.Equal(t, http.StatusOK, w.Code) - assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &testRule)) - assert.Equal(t, testRuleID.ID, testRule.ID.Hex()) - assert.Equal(t, "newRule1", testRule.Name) - assert.Equal(t, "#ddd", testRule.Color) - - // GetRules - w = toolkit.MakeRequest("GET", "/api/rules", nil) - var rules []Rule - assert.Equal(t, http.StatusOK, w.Code) - assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &rules)) - assert.Len(t, rules, 4) - - toolkit.wrapper.Destroy(t) -} - -func TestPcapImporterApi(t *testing.T) { - toolkit := NewRouterTestToolkit(t, true) - - // Import pcap - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("POST", "/api/pcap/file", nil).Code) - assert.Equal(t, http.StatusBadRequest, toolkit.MakeRequest("POST", "/api/pcap/file", - gin.H{"file": "invalidPath"}).Code) - w := toolkit.MakeRequest("POST", "/api/pcap/file", gin.H{"file": "test_data/ping_pong_10000.pcap"}) - var sessionID struct{ Session string } - assert.Equal(t, http.StatusAccepted, w.Code) - assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &sessionID)) - assert.Equal(t, "369ef4b6abb6214b4ee2e0c81ecb93c49e275c26c85e30493b37727d408cf280", sessionID.Session) - assert.Equal(t, http.StatusUnprocessableEntity, toolkit.MakeRequest("POST", "/api/pcap/file", - gin.H{"file": "test_data/ping_pong_10000.pcap"}).Code) // duplicate - - // Get sessions - var sessions []ImportingSession - w = toolkit.MakeRequest("GET", "/api/pcap/sessions", nil) - assert.Equal(t, http.StatusOK, w.Code) - assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &sessions)) - assert.Len(t, sessions, 1) - assert.Equal(t, sessionID.Session, sessions[0].ID) - - // Get session - var session ImportingSession - w = toolkit.MakeRequest("GET", "/api/pcap/sessions/"+sessionID.Session, nil) - assert.NoError(t, json.Unmarshal(w.Body.Bytes(), &session)) - assert.Equal(t, sessionID.Session, session.ID) - - // Cancel session - assert.Equal(t, http.StatusNotFound, toolkit.MakeRequest("DELETE", "/api/pcap/sessions/invalidSession", - nil).Code) - assert.Equal(t, http.StatusAccepted, toolkit.MakeRequest("DELETE", "/api/pcap/sessions/"+sessionID.Session, - nil).Code) - - time.Sleep(1 * time.Second) // wait for termination - - toolkit.wrapper.Destroy(t) -} - -type RouterTestToolkit struct { - appContext *ApplicationContext - wrapper *TestStorageWrapper - router *gin.Engine - t *testing.T -} - -func NewRouterTestToolkit(t *testing.T, withSetup bool) *RouterTestToolkit { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(Settings) - - appContext, err := CreateApplicationContext(wrapper.Storage, "test") - require.NoError(t, err) - gin.SetMode(gin.ReleaseMode) - notificationController := NewNotificationController(appContext) - go notificationController.Run() - resourcesController := NewResourcesController(notificationController) - router := CreateApplicationRouter(appContext, notificationController, resourcesController) - - toolkit := RouterTestToolkit{ - appContext: appContext, - wrapper: wrapper, - router: router, - t: t, - } - - if withSetup { - settings := gin.H{ - "config": Config{ServerAddress: "1.2.3.4", FlagRegex: "FLAG{test}", AuthRequired: false}, - "accounts": gin.Accounts{}, - } - toolkit.MakeRequest("POST", "/setup", settings) - } - - return &toolkit -} - -func (rtt *RouterTestToolkit) MakeRequest(method string, url string, body interface{}) *httptest.ResponseRecorder { - var r io.Reader - - if body != nil { - buf, err := json.Marshal(body) - require.NoError(rtt.t, err) - r = bytes.NewBuffer(buf) - } - - w := httptest.NewRecorder() - req, err := http.NewRequest(method, url, r) - require.NoError(rtt.t, err) - rtt.router.ServeHTTP(w, req) - - return w -} diff --git a/caronte_test.go b/caronte_test.go deleted file mode 100644 index 76c776f..0000000 --- a/caronte_test.go +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of caronte (https://github.com/eciavatta/caronte). - * Copyright (c) 2020 Emiliano Ciavatta. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package main - -import ( - "context" - "fmt" - "os" - "strconv" - "testing" - "time" - - log "github.com/sirupsen/logrus" - "github.com/stretchr/testify/require" -) - -type TestStorageWrapper struct { - DbName string - Storage *MongoStorage - Context context.Context -} - -func NewTestStorageWrapper(t *testing.T) *TestStorageWrapper { - mongoHost, ok := os.LookupEnv("MONGO_HOST") - if !ok { - mongoHost = "localhost" - } - mongoPort, ok := os.LookupEnv("MONGO_PORT") - if !ok { - mongoPort = "27017" - } - port, err := strconv.Atoi(mongoPort) - require.NoError(t, err, "invalid port") - - dbName := fmt.Sprintf("%x", time.Now().UnixNano()) - log.WithField("dbName", dbName).Info("creating new storage") - - storage, err := NewMongoStorage(mongoHost, port, dbName) - require.NoError(t, err) - ctx, _ := context.WithTimeout(context.Background(), 10*time.Second) - - return &TestStorageWrapper{ - DbName: dbName, - Storage: storage, - Context: ctx, - } -} - -func (tsw TestStorageWrapper) AddCollection(collectionName string) { - tsw.Storage.collections[collectionName] = tsw.Storage.client.Database(tsw.DbName).Collection(collectionName) -} - -func (tsw TestStorageWrapper) Destroy(t *testing.T) { - err := tsw.Storage.client.Disconnect(tsw.Context) - require.NoError(t, err, "failed to disconnect to database") -} diff --git a/connection_handler_test.go b/connection_handler_test.go deleted file mode 100644 index 942310a..0000000 --- a/connection_handler_test.go +++ /dev/null @@ -1,227 +0,0 @@ -/* - * This file is part of caronte (https://github.com/eciavatta/caronte). - * Copyright (c) 2020 Emiliano Ciavatta. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package main - -import ( - "context" - "encoding/binary" - "github.com/flier/gohs/hyperscan" - "github.com/google/gopacket" - "github.com/google/gopacket/layers" - "github.com/google/gopacket/tcpassembly" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "math/rand" - "net" - "testing" - "time" -) - -func TestTakeReleaseScanners(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - serverNet := ParseIPNet(testDstIP) - ruleManager := TestRulesManager{ - databaseUpdated: make(chan RulesDatabase), - } - - database, err := hyperscan.NewStreamDatabase(hyperscan.NewPattern("/nope/", 0)) - require.NoError(t, err) - - factory := NewBiDirectionalStreamFactory(wrapper.Storage, *serverNet, &ruleManager) - version := NewRowID() - ruleManager.DatabaseUpdateChannel() <- RulesDatabase{database, 0, version} - time.Sleep(10 * time.Millisecond) - - n := 1000 - for i := 0; i < n; i++ { - scanner := factory.takeScanner() - assert.Equal(t, scanner.version, version) - - if i%50 == 0 { - version = NewRowID() - ruleManager.DatabaseUpdateChannel() <- RulesDatabase{database, 0, version} - time.Sleep(10 * time.Millisecond) - } - factory.releaseScanner(scanner) - } - assert.Len(t, factory.scanners, 1) - - scanners := make([]Scanner, n) - for i := 0; i < n; i++ { - scanners[i] = factory.takeScanner() - assert.Equal(t, scanners[i].version, version) - } - for i := 0; i < n; i++ { - factory.releaseScanner(scanners[i]) - } - assert.Len(t, factory.scanners, n) - - version = NewRowID() - ruleManager.DatabaseUpdateChannel() <- RulesDatabase{database, 0, version} - time.Sleep(10 * time.Millisecond) - - for i := 0; i < n; i++ { - scanners[i] = factory.takeScanner() - assert.Equal(t, scanners[i].version, version) - factory.releaseScanner(scanners[i]) - } - - close(ruleManager.DatabaseUpdateChannel()) - wrapper.Destroy(t) -} - -func TestConnectionFactory(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(Connections) - wrapper.AddCollection(ConnectionStreams) - - ruleManager := TestRulesManager{ - databaseUpdated: make(chan RulesDatabase), - } - - clientIP := layers.NewIPEndpoint(net.ParseIP(testSrcIP)) - serverIP := layers.NewIPEndpoint(net.ParseIP(testDstIP)) - serverPort := layers.NewTCPPortEndpoint(dstPort) - clientServerNetFlow, err := gopacket.FlowFromEndpoints(clientIP, serverIP) - require.NoError(t, err) - serverClientNetFlow, err := gopacket.FlowFromEndpoints(serverIP, clientIP) - require.NoError(t, err) - - database, err := hyperscan.NewStreamDatabase(hyperscan.NewPattern("/nope/", 0)) - require.NoError(t, err) - - factory := NewBiDirectionalStreamFactory(wrapper.Storage, *ParseIPNet(testDstIP), &ruleManager) - version := NewRowID() - ruleManager.DatabaseUpdateChannel() <- RulesDatabase{database, 0, version} - time.Sleep(10 * time.Millisecond) - - testInteraction := func(netFlow gopacket.Flow, transportFlow gopacket.Flow, otherSeenChan chan time.Time, - completed chan bool) { - - time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) - stream := factory.New(netFlow, transportFlow) - seen := time.Now() - stream.Reassembled([]tcpassembly.Reassembly{{[]byte{}, 0, true, true, seen}}) - stream.ReassemblyComplete() - - var startedAt, closedAt time.Time - if netFlow == serverClientNetFlow { - otherSeenChan <- seen - return - } - - otherSeen, ok := <-otherSeenChan - require.True(t, ok) - - if seen.Before(otherSeen) { - startedAt = seen - closedAt = otherSeen - } else { - startedAt = otherSeen - closedAt = seen - } - close(otherSeenChan) - - var result Connection - connectionFlow := StreamFlow{netFlow.Src(), netFlow.Dst(), transportFlow.Src(), transportFlow.Dst()} - connectionID := CustomRowID(connectionFlow.Hash(), startedAt) - op := wrapper.Storage.Find(Connections).Context(wrapper.Context) - err := op.Filter(OrderedDocument{{"_id", connectionID}}).First(&result) - require.NoError(t, err) - - assert.NotNil(t, result) - assert.Equal(t, CustomRowID(connectionFlow.Hash(), result.StartedAt), result.ID) - assert.Equal(t, netFlow.Src().String(), result.SourceIP) - assert.Equal(t, netFlow.Dst().String(), result.DestinationIP) - assert.Equal(t, binary.BigEndian.Uint16(transportFlow.Src().Raw()), result.SourcePort) - assert.Equal(t, binary.BigEndian.Uint16(transportFlow.Dst().Raw()), result.DestinationPort) - assert.Equal(t, startedAt.Unix(), result.StartedAt.Unix()) - assert.Equal(t, closedAt.Unix(), result.ClosedAt.Unix()) - - completed <- true - } - - completed := make(chan bool) - n := 1000 - - for port := 40000; port < 40000+n; port++ { - clientPort := layers.NewTCPPortEndpoint(layers.TCPPort(port)) - clientServerTransportFlow, err := gopacket.FlowFromEndpoints(clientPort, serverPort) - require.NoError(t, err) - serverClientTransportFlow, err := gopacket.FlowFromEndpoints(serverPort, clientPort) - require.NoError(t, err) - - otherSeenChan := make(chan time.Time) - go testInteraction(clientServerNetFlow, clientServerTransportFlow, otherSeenChan, completed) - go testInteraction(serverClientNetFlow, serverClientTransportFlow, otherSeenChan, completed) - } - - timeout := time.Tick(10 * time.Second) - for i := 0; i < n; i++ { - select { - case <-completed: - continue - case <-timeout: - t.Fatal("timeout") - } - } - - assert.Len(t, factory.connections, 0) - - close(ruleManager.DatabaseUpdateChannel()) - wrapper.Destroy(t) -} - -type TestRulesManager struct { - databaseUpdated chan RulesDatabase -} - -func (rm TestRulesManager) LoadRules() error { - return nil -} - -func (rm TestRulesManager) AddRule(_ context.Context, _ Rule) (RowID, error) { - return RowID{}, nil -} - -func (rm TestRulesManager) DeleteRule(_ context.Context, _ RowID) error { - return nil -} - -func (rm TestRulesManager) GetRule(_ RowID) (Rule, bool) { - return Rule{}, false -} - -func (rm TestRulesManager) UpdateRule(_ context.Context, _ RowID, _ Rule) (bool, error) { - return false, nil -} - -func (rm TestRulesManager) GetRules() []Rule { - return nil -} - -func (rm TestRulesManager) SetFlag(_ context.Context, _ string) error { - return nil -} - -func (rm TestRulesManager) FillWithMatchedRules(_ *Connection, _ map[uint][]PatternSlice, _ map[uint][]PatternSlice) { -} - -func (rm TestRulesManager) DatabaseUpdateChannel() chan RulesDatabase { - return rm.databaseUpdated -} diff --git a/pcap_importer_test.go b/pcap_importer_test.go deleted file mode 100644 index 4761927..0000000 --- a/pcap_importer_test.go +++ /dev/null @@ -1,177 +0,0 @@ -/* - * This file is part of caronte (https://github.com/eciavatta/caronte). - * Copyright (c) 2020 Emiliano Ciavatta. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package main - -import ( - "bufio" - "fmt" - "github.com/google/gopacket" - "github.com/google/gopacket/tcpassembly" - "github.com/google/gopacket/tcpassembly/tcpreader" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "os" - "sync" - "testing" - "time" -) - -func TestImportPcap(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - pcapImporter := newTestPcapImporter(wrapper, "172.17.0.3") - - pcapImporter.releaseAssembler(pcapImporter.takeAssembler()) - - fileName := copyToProcessing(t, "ping_pong_10000.pcap") - sessionID, err := pcapImporter.ImportPcap(fileName, false) - require.NoError(t, err) - - duplicatePcapFileName := copyToProcessing(t, "ping_pong_10000.pcap") - duplicateSessionID, err := pcapImporter.ImportPcap(duplicatePcapFileName, false) - require.Error(t, err) - assert.Equal(t, sessionID, duplicateSessionID) - assert.Error(t, os.Remove(ProcessingPcapsBasePath+duplicatePcapFileName)) - - _, isPresent := pcapImporter.GetSession("invalid") - assert.False(t, isPresent) - - session := waitSessionCompletion(t, pcapImporter, sessionID) - assert.Equal(t, 15008, session.ProcessedPackets) - assert.Equal(t, 0, session.InvalidPackets) - assert.Equal(t, map[uint16]flowCount{9999: {10004, 5004}}, session.PacketsPerService) - assert.Zero(t, session.ImportingError) - - checkSessionEquals(t, wrapper, session) - - assert.Error(t, os.Remove(ProcessingPcapsBasePath+fileName)) - assert.NoError(t, os.Remove(PcapsBasePath+session.ID+".pcap")) - - wrapper.Destroy(t) -} - -func TestCancelImportSession(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - pcapImporter := newTestPcapImporter(wrapper, "172.17.0.3") - - fileName := copyToProcessing(t, "ping_pong_10000.pcap") - sessionID, err := pcapImporter.ImportPcap(fileName, false) - require.NoError(t, err) - - assert.False(t, pcapImporter.CancelSession("invalid")) - assert.True(t, pcapImporter.CancelSession(sessionID)) - - session := waitSessionCompletion(t, pcapImporter, sessionID) - assert.Zero(t, session.CompletedAt) - assert.Equal(t, int64(1270696), session.Size) - // assert.Equal(t, 0, session.ProcessedPackets) // TODO: investigate - assert.Equal(t, 0, session.InvalidPackets) - // assert.Equal(t, map[uint16]flowCount{}, session.PacketsPerService) - assert.NotZero(t, session.ImportingError) - - checkSessionEquals(t, wrapper, session) - - assert.Error(t, os.Remove(ProcessingPcapsBasePath+fileName)) - assert.Error(t, os.Remove(PcapsBasePath+sessionID+".pcap")) - - wrapper.Destroy(t) -} - -func TestImportNoTcpPackets(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - pcapImporter := newTestPcapImporter(wrapper, "172.17.0.4") - - fileName := copyToProcessing(t, "icmp.pcap") - sessionID, err := pcapImporter.ImportPcap(fileName, false) - require.NoError(t, err) - - session := waitSessionCompletion(t, pcapImporter, sessionID) - assert.Equal(t, int64(228024), session.Size) - assert.Equal(t, 2000, session.ProcessedPackets) - assert.Equal(t, 2000, session.InvalidPackets) - assert.Equal(t, map[uint16]flowCount{}, session.PacketsPerService) - assert.Zero(t, session.ImportingError) - - checkSessionEquals(t, wrapper, session) - - assert.Error(t, os.Remove(ProcessingPcapsBasePath+fileName)) - assert.NoError(t, os.Remove(PcapsBasePath+sessionID+".pcap")) - - wrapper.Destroy(t) -} - -func newTestPcapImporter(wrapper *TestStorageWrapper, serverAddress string) *PcapImporter { - wrapper.AddCollection(ImportingSessions) - - streamPool := tcpassembly.NewStreamPool(&testStreamFactory{}) - - return &PcapImporter{ - storage: wrapper.Storage, - streamPool: streamPool, - assemblers: make([]*tcpassembly.Assembler, 0, initialAssemblerPoolSize), - sessions: make(map[string]ImportingSession), - mAssemblers: sync.Mutex{}, - mSessions: sync.Mutex{}, - serverNet: *ParseIPNet(serverAddress), - notificationController: NewNotificationController(nil), - } -} - -func waitSessionCompletion(t *testing.T, pcapImporter *PcapImporter, sessionID string) ImportingSession { - session, isPresent := pcapImporter.GetSession(sessionID) - require.True(t, isPresent) - <-session.completed - - session, isPresent = pcapImporter.GetSession(sessionID) - assert.True(t, isPresent) - assert.Equal(t, sessionID, session.ID) - - return session -} - -func checkSessionEquals(t *testing.T, wrapper *TestStorageWrapper, session ImportingSession) { - var result ImportingSession - assert.NoError(t, wrapper.Storage.Find(ImportingSessions).Filter(OrderedDocument{{"_id", session.ID}}). - Context(wrapper.Context).First(&result)) - assert.Equal(t, session.StartedAt.Unix(), result.StartedAt.Unix()) - assert.Equal(t, session.CompletedAt.Unix(), result.CompletedAt.Unix()) - session.StartedAt = time.Time{} - result.StartedAt = time.Time{} - session.CompletedAt = time.Time{} - result.CompletedAt = time.Time{} - session.cancelFunc = nil - session.completed = nil - assert.Equal(t, session, result) -} - -func copyToProcessing(t *testing.T, fileName string) string { - newFile := fmt.Sprintf("test-%v-%s", time.Now().UnixNano(), fileName) - require.NoError(t, CopyFile(ProcessingPcapsBasePath+newFile, "test_data/"+fileName)) - return newFile -} - -type testStreamFactory struct { -} - -func (sf *testStreamFactory) New(_, _ gopacket.Flow) tcpassembly.Stream { - reader := tcpreader.NewReaderStream() - go func() { - buffer := bufio.NewReader(&reader) - tcpreader.DiscardBytesToEOF(buffer) - }() - return &reader -} diff --git a/rules_manager_test.go b/rules_manager_test.go deleted file mode 100644 index d3f6d20..0000000 --- a/rules_manager_test.go +++ /dev/null @@ -1,302 +0,0 @@ -/* - * This file is part of caronte (https://github.com/eciavatta/caronte). - * Copyright (c) 2020 Emiliano Ciavatta. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package main - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestAddAndGetAllRules(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(Rules) - - rulesManager, err := LoadRulesManager(wrapper.Storage, "FLAG{test}") - require.NoError(t, err) - impl := rulesManager.(*rulesManagerImpl) - checkVersion(t, rulesManager, impl.rulesByName["flag_out"].ID) - checkVersion(t, rulesManager, impl.rulesByName["flag_in"].ID) - emptyRule := Rule{Name: "empty", Color: "#fff", Enabled: true} - emptyID, err := rulesManager.AddRule(wrapper.Context, emptyRule) - assert.NoError(t, err) - assert.NotNil(t, emptyID) - checkVersion(t, rulesManager, emptyID) - - duplicateRule, err := rulesManager.AddRule(wrapper.Context, Rule{Name: "empty", Color: "#eee"}) - assert.Error(t, err) - assert.Zero(t, duplicateRule) - - invalidPattern, err := rulesManager.AddRule(wrapper.Context, Rule{ - Name: "invalidPattern", - Color: "#eee", - Patterns: []Pattern{ - { - Regex: "invalid)", - }, - }, - }) - assert.Error(t, err) - assert.Zero(t, invalidPattern) - - rule1 := Rule{ - Name: "rule1", - Color: "#eee", - Patterns: []Pattern{ - { - Regex: "pattern1", - Flags: RegexFlags{ - Caseless: true, - DotAll: true, - MultiLine: true, - Utf8Mode: true, - UnicodeProperty: true, - }, - MinOccurrences: 1, - MaxOccurrences: 3, - Direction: DirectionBoth, - }}, - Enabled: true, - } - rule1ID, err := rulesManager.AddRule(wrapper.Context, rule1) - assert.NoError(t, err) - assert.NotNil(t, rule1ID) - checkVersion(t, rulesManager, rule1ID) - - rule2 := Rule{ - Name: "rule2", - Color: "#ddd", - Patterns: []Pattern{ - {Regex: "pattern1"}, - {Regex: "pattern2"}, - }, - Enabled: true, - } - rule2ID, err := rulesManager.AddRule(wrapper.Context, rule2) - assert.NoError(t, err) - assert.NotNil(t, rule2ID) - checkVersion(t, rulesManager, rule2ID) - - rule3 := Rule{ - Name: "rule3", - Color: "#ccc", - Patterns: []Pattern{ - {Regex: "pattern2"}, - {Regex: "pattern3"}, - }, - Enabled: true, - } - rule3ID, err := rulesManager.AddRule(wrapper.Context, rule3) - assert.NoError(t, err) - assert.NotNil(t, rule3ID) - checkVersion(t, rulesManager, rule3ID) - - checkRule := func(expected Rule, patternIDs []int) { - var rule Rule - err := wrapper.Storage.Find(Rules).Context(wrapper.Context). - Filter(OrderedDocument{{"_id", expected.ID}}).First(&rule) - require.NoError(t, err) - - for i, id := range patternIDs { - rule.Patterns[i].internalID = uint(id) - } - assert.Equal(t, expected, rule) - assert.Equal(t, expected, impl.rules[expected.ID]) - assert.Equal(t, expected, impl.rulesByName[expected.Name]) - } - - assert.Len(t, impl.rules, 6) - assert.Len(t, impl.rulesByName, 6) - assert.Len(t, impl.patterns, 5) - assert.Len(t, impl.patternsIds, 5) - - emptyRule.ID = emptyID - rule1.ID = rule1ID - rule2.ID = rule2ID - rule3.ID = rule3ID - - checkRule(emptyRule, []int{}) - checkRule(rule1, []int{1}) - checkRule(rule2, []int{2, 3}) - checkRule(rule3, []int{3, 4}) - - assert.Len(t, rulesManager.GetRules(), 6) - assert.ElementsMatch(t, []Rule{impl.rulesByName["flag_out"], impl.rulesByName["flag_in"], emptyRule, - rule1, rule2, rule3}, rulesManager.GetRules()) - - wrapper.Destroy(t) -} - -func TestLoadAndUpdateRules(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(Rules) - - expectedIds := []RowID{NewRowID(), NewRowID(), NewRowID(), NewRowID()} - rules := []interface{}{ - Rule{ID: expectedIds[0], Name: "rule1", Color: "#fff", Patterns: []Pattern{ - {Regex: "/pattern1/", Flags: RegexFlags{Caseless: true}, Direction: DirectionToClient, internalID: 0}, - }}, - Rule{ID: expectedIds[1], Name: "rule2", Color: "#eee", Patterns: []Pattern{ - {Regex: "/pattern2/", MinOccurrences: 1, MaxOccurrences: 3, Direction: DirectionToServer, internalID: 1}, - }}, - Rule{ID: expectedIds[2], Name: "rule3", Color: "#ddd", Patterns: []Pattern{ - {Regex: "/pattern2/", Direction: DirectionBoth, internalID: 1}, - {Regex: "/pattern3/", Flags: RegexFlags{MultiLine: true}, internalID: 2}, - }}, - Rule{ID: expectedIds[3], Name: "rule4", Color: "#ccc", Patterns: []Pattern{ - {Regex: "/pattern3/", internalID: 3}, - }}, - } - ids, err := wrapper.Storage.Insert(Rules).Context(wrapper.Context).Many(rules) - require.NoError(t, err) - assert.ElementsMatch(t, expectedIds, ids) - - rulesManager, err := LoadRulesManager(wrapper.Storage, "FLAG{nope}") - require.NoError(t, err) - - rule, isPresent := rulesManager.GetRule(NewRowID()) - assert.Zero(t, rule) - assert.False(t, isPresent) - - for _, objRule := range rules { - expected := objRule.(Rule) - rule, isPresent := rulesManager.GetRule(expected.ID) - assert.True(t, isPresent) - assert.Equal(t, expected, rule) - } - - updated, err := rulesManager.UpdateRule(wrapper.Context, NewRowID(), Rule{}) - assert.False(t, updated) - assert.NoError(t, err) - - updated, err = rulesManager.UpdateRule(wrapper.Context, expectedIds[0], Rule{Name: "rule2", Color: "#fff"}) - assert.False(t, updated) - assert.Error(t, err) - - for _, objRule := range rules { - expected := objRule.(Rule) - expected.Name = expected.ID.Hex() - expected.Color = "#000" - updated, err := rulesManager.UpdateRule(wrapper.Context, expected.ID, expected) - assert.True(t, updated) - assert.NoError(t, err) - - rule, isPresent := rulesManager.GetRule(expected.ID) - assert.True(t, isPresent) - assert.Equal(t, expected, rule) - } - - wrapper.Destroy(t) -} - -func TestFillWithMatchedRules(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(Rules) - - rulesManager, err := LoadRulesManager(wrapper.Storage, "FLAG{test}") - require.NoError(t, err) - impl := rulesManager.(*rulesManagerImpl) - checkVersion(t, rulesManager, impl.rulesByName["flag_out"].ID) - checkVersion(t, rulesManager, impl.rulesByName["flag_in"].ID) - - emptyRule, err := rulesManager.AddRule(wrapper.Context, Rule{Name: "empty", Color: "#fff"}) - require.NoError(t, err) - checkVersion(t, rulesManager, emptyRule) - - conn := &Connection{} - rulesManager.FillWithMatchedRules(conn, map[uint][]PatternSlice{}, map[uint][]PatternSlice{}) - assert.ElementsMatch(t, []RowID{emptyRule}, conn.MatchedRules) - - filterRule, err := rulesManager.AddRule(wrapper.Context, Rule{ - Name: "filter", - Color: "#fff", - Filter: Filter{ - ServicePort: 80, - ClientAddress: "10.10.10.10", - ClientPort: 60000, - MinDuration: 2000, - MaxDuration: 4000, - MinBytes: 64, - MaxBytes: 64, - }, - }) - require.NoError(t, err) - checkVersion(t, rulesManager, filterRule) - conn = &Connection{ - SourceIP: "10.10.10.10", - SourcePort: 60000, - DestinationPort: 80, - ClientBytes: 32, - ServerBytes: 32, - StartedAt: time.Now(), - ClosedAt: time.Now().Add(3 * time.Second), - } - rulesManager.FillWithMatchedRules(conn, map[uint][]PatternSlice{}, map[uint][]PatternSlice{}) - assert.ElementsMatch(t, []RowID{emptyRule, filterRule}, conn.MatchedRules) - - patternRule, err := rulesManager.AddRule(wrapper.Context, Rule{ - Name: "pattern", - Color: "#fff", - Patterns: []Pattern{ - {Regex: "pattern1", Direction: DirectionToClient, MinOccurrences: 1}, - {Regex: "pattern2", Direction: DirectionToServer, MaxOccurrences: 2}, - {Regex: "pattern3", Direction: DirectionBoth, MinOccurrences: 2, MaxOccurrences: 2}, - }, - }) - require.NoError(t, err) - checkVersion(t, rulesManager, patternRule) - conn = &Connection{} - rulesManager.FillWithMatchedRules(conn, map[uint][]PatternSlice{2: {{0, 0}, {0, 0}}, 3: {{0, 0}}}, - map[uint][]PatternSlice{1: {{0, 0}}, 3: {{0, 0}}}) - assert.ElementsMatch(t, []RowID{emptyRule, patternRule}, conn.MatchedRules) - - rulesManager.FillWithMatchedRules(conn, map[uint][]PatternSlice{2: {{0, 0}, {0, 0}}}, - map[uint][]PatternSlice{1: {{0, 0}}, 3: {{0, 0}, {0, 0}}}) - assert.ElementsMatch(t, []RowID{emptyRule, patternRule}, conn.MatchedRules) - - rulesManager.FillWithMatchedRules(conn, map[uint][]PatternSlice{2: {{0, 0}, {0, 0}}, 3: {{0, 0}, {0, 0}}}, - map[uint][]PatternSlice{1: {{0, 0}}}) - assert.ElementsMatch(t, []RowID{emptyRule, patternRule}, conn.MatchedRules) - - rulesManager.FillWithMatchedRules(conn, map[uint][]PatternSlice{2: {{0, 0}, {0, 0}}, 3: {{0, 0}}}, - map[uint][]PatternSlice{3: {{0, 0}}}) - assert.ElementsMatch(t, []RowID{emptyRule}, conn.MatchedRules) - - rulesManager.FillWithMatchedRules(conn, map[uint][]PatternSlice{2: {{0, 0}, {0, 0}, {0, 0}}, 3: {{0, 0}}}, - map[uint][]PatternSlice{1: {{0, 0}}, 3: {{0, 0}}}) - assert.ElementsMatch(t, []RowID{emptyRule}, conn.MatchedRules) - - rulesManager.FillWithMatchedRules(conn, map[uint][]PatternSlice{2: {{0, 0}, {0, 0}}, 3: {{0, 0}}}, - map[uint][]PatternSlice{1: {{0, 0}}, 3: {{0, 0}, {0, 0}}}) - assert.ElementsMatch(t, []RowID{emptyRule}, conn.MatchedRules) - - wrapper.Destroy(t) -} - -func checkVersion(t *testing.T, rulesManager RulesManager, id RowID) { - timeout := time.Tick(1 * time.Second) - - select { - case database := <-rulesManager.DatabaseUpdateChannel(): - assert.Equal(t, id, database.version) - case <-timeout: - t.Fatal("timeout") - } -} diff --git a/storage_test.go b/storage_test.go deleted file mode 100644 index dd91e97..0000000 --- a/storage_test.go +++ /dev/null @@ -1,258 +0,0 @@ -/* - * This file is part of caronte (https://github.com/eciavatta/caronte). - * Copyright (c) 2020 Emiliano Ciavatta. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package main - -import ( - "github.com/stretchr/testify/assert" - "go.mongodb.org/mongo-driver/bson/primitive" - "testing" - "time" -) - -type a struct { - ID primitive.ObjectID `bson:"_id,omitempty"` - A string `bson:"a,omitempty"` - B int `bson:"b,omitempty"` - C time.Time `bson:"c,omitempty"` - D map[string]b `bson:"d"` - E []b `bson:"e,omitempty"` -} - -type b struct { - A string `bson:"a,omitempty"` - B int `bson:"b,omitempty"` -} - -func TestOperationOnInvalidCollection(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - - simpleDoc := UnorderedDocument{"key": "a", "value": 0} - insertOp := wrapper.Storage.Insert("invalid_collection").Context(wrapper.Context) - insertedID, err := insertOp.One(simpleDoc) - assert.Nil(t, insertedID) - assert.Error(t, err) - - insertedIDs, err := insertOp.Many([]interface{}{simpleDoc}) - assert.Nil(t, insertedIDs) - assert.Error(t, err) - - updateOp := wrapper.Storage.Update("invalid_collection").Context(wrapper.Context) - isUpdated, err := updateOp.One(simpleDoc) - assert.False(t, isUpdated) - assert.Error(t, err) - - updated, err := updateOp.Many(simpleDoc) - assert.Zero(t, updated) - assert.Error(t, err) - - findOp := wrapper.Storage.Find("invalid_collection").Context(wrapper.Context) - var result interface{} - err = findOp.First(&result) - assert.Nil(t, result) - assert.Error(t, err) - - var results interface{} - err = findOp.All(&result) - assert.Nil(t, results) - assert.Error(t, err) - - wrapper.Destroy(t) -} - -func TestSimpleInsertAndFind(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - collectionName := "simple_insert_find" - wrapper.AddCollection(collectionName) - - insertOp := wrapper.Storage.Insert(collectionName).Context(wrapper.Context) - simpleDocA := UnorderedDocument{"key": "a"} - idA, err := insertOp.One(simpleDocA) - assert.Len(t, idA, 12) - assert.Nil(t, err) - - simpleDocB := UnorderedDocument{"_id": "idb", "key": "b"} - idB, err := insertOp.One(simpleDocB) - assert.Equal(t, "idb", idB) - assert.Nil(t, err) - - var result UnorderedDocument - findOp := wrapper.Storage.Find(collectionName).Context(wrapper.Context) - err = findOp.Filter(OrderedDocument{{"key", "a"}}).First(&result) - assert.Nil(t, err) - assert.Equal(t, idA, result["_id"]) - assert.Equal(t, simpleDocA["key"], result["key"]) - - err = findOp.Filter(OrderedDocument{{"_id", idB}}).First(&result) - assert.Nil(t, err) - assert.Equal(t, idB, result["_id"]) - assert.Equal(t, simpleDocB["key"], result["key"]) - - wrapper.Destroy(t) -} - -func TestSimpleInsertManyAndFindMany(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - collectionName := "simple_insert_many_find_many" - wrapper.AddCollection(collectionName) - - insertOp := wrapper.Storage.Insert(collectionName).Context(wrapper.Context) - simpleDocs := []interface{}{ - UnorderedDocument{"key": "a"}, - UnorderedDocument{"_id": "idb", "key": "b"}, - UnorderedDocument{"key": "c"}, - } - ids, err := insertOp.Many(simpleDocs) - assert.Nil(t, err) - assert.Len(t, ids, 3) - assert.Equal(t, "idb", ids[1]) - - var results []UnorderedDocument - findOp := wrapper.Storage.Find(collectionName).Context(wrapper.Context) - err = findOp.Sort("key", false).All(&results) // test sort ascending - assert.Nil(t, err) - assert.Len(t, results, 3) - assert.Equal(t, "c", results[0]["key"]) - assert.Equal(t, "b", results[1]["key"]) - assert.Equal(t, "a", results[2]["key"]) - - err = findOp.Sort("key", true).All(&results) // test sort descending - assert.Nil(t, err) - assert.Len(t, results, 3) - assert.Equal(t, "c", results[2]["key"]) - assert.Equal(t, "b", results[1]["key"]) - assert.Equal(t, "a", results[0]["key"]) - - err = findOp.Filter(OrderedDocument{{"key", OrderedDocument{{"$gte", "b"}}}}). - Sort("key", true).All(&results) // test filter - assert.Nil(t, err) - assert.Len(t, results, 2) - assert.Equal(t, "b", results[0]["key"]) - assert.Equal(t, "c", results[1]["key"]) - - wrapper.Destroy(t) -} - -func TestSimpleUpdateOneUpdateMany(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - collectionName := "simple_update_one_update_many" - wrapper.AddCollection(collectionName) - - insertOp := wrapper.Storage.Insert(collectionName).Context(wrapper.Context) - simpleDocs := []interface{}{ - UnorderedDocument{"_id": "ida", "key": "a"}, - UnorderedDocument{"key": "b"}, - UnorderedDocument{"key": "c"}, - } - _, err := insertOp.Many(simpleDocs) - assert.Nil(t, err) - - updateOp := wrapper.Storage.Update(collectionName).Context(wrapper.Context) - isUpdated, err := updateOp.Filter(OrderedDocument{{"_id", "ida"}}). - One(OrderedDocument{{"key", "aa"}}) - assert.Nil(t, err) - assert.True(t, isUpdated) - - updated, err := updateOp.Filter(OrderedDocument{{"key", OrderedDocument{{"$gte", "b"}}}}). - Many(OrderedDocument{{"key", "bb"}}) - assert.Nil(t, err) - assert.Equal(t, int64(2), updated) - - var upsertID interface{} - isUpdated, err = updateOp.Upsert(&upsertID).Filter(OrderedDocument{{"key", "d"}}). - One(OrderedDocument{{"key", "d"}}) - assert.Nil(t, err) - assert.False(t, isUpdated) - assert.NotNil(t, upsertID) - - var results []UnorderedDocument - findOp := wrapper.Storage.Find(collectionName).Context(wrapper.Context) - err = findOp.Sort("key", true).All(&results) // test sort ascending - assert.Nil(t, err) - assert.Len(t, results, 4) - assert.Equal(t, "aa", results[0]["key"]) - assert.Equal(t, "bb", results[1]["key"]) - assert.Equal(t, "bb", results[2]["key"]) - assert.Equal(t, "d", results[3]["key"]) - - wrapper.Destroy(t) -} - -func TestComplexInsertManyFindMany(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - collectionName := "complex_insert_many_find_many" - wrapper.AddCollection(collectionName) - - testTime := time.Now() - oid1, err := primitive.ObjectIDFromHex("ffffffffffffffffffffffff") - assert.Nil(t, err) - - docs := []interface{}{ - a{ - A: "test0", - B: 0, - C: testTime, - D: map[string]b{ - "first": {A: "0", B: 0}, - "second": {A: "1", B: 1}, - }, - E: []b{ - {A: "0", B: 0}, {A: "1", B: 0}, - }, - }, - a{ - ID: oid1, - A: "test1", - B: 1, - C: testTime, - D: map[string]b{}, - E: []b{}, - }, - a{}, - } - - ids, err := wrapper.Storage.Insert(collectionName).Context(wrapper.Context).Many(docs) - assert.Nil(t, err) - assert.Len(t, ids, 3) - assert.Equal(t, ids[1], oid1) - - var results []a - err = wrapper.Storage.Find(collectionName).Context(wrapper.Context).All(&results) - assert.Nil(t, err) - assert.Len(t, results, 3) - doc0, doc1, doc2 := docs[0].(a), docs[1].(a), docs[2].(a) - assert.Equal(t, ids[0], results[0].ID) - assert.Equal(t, doc1.ID, results[1].ID) - assert.Equal(t, ids[2], results[2].ID) - assert.Equal(t, doc0.A, results[0].A) - assert.Equal(t, doc1.A, results[1].A) - assert.Equal(t, doc2.A, results[2].A) - assert.Equal(t, doc0.B, results[0].B) - assert.Equal(t, doc1.B, results[1].B) - assert.Equal(t, doc2.B, results[2].B) - assert.Equal(t, doc0.C.Unix(), results[0].C.Unix()) - assert.Equal(t, doc1.C.Unix(), results[1].C.Unix()) - assert.Equal(t, doc2.C.Unix(), results[2].C.Unix()) - assert.Equal(t, doc0.D, results[0].D) - assert.Equal(t, doc1.D, results[1].D) - assert.Equal(t, doc2.D, results[2].D) - assert.Equal(t, doc0.E, results[0].E) - assert.Nil(t, results[1].E) - assert.Nil(t, results[2].E) - - wrapper.Destroy(t) -} diff --git a/stream_handler_test.go b/stream_handler_test.go deleted file mode 100644 index b185483..0000000 --- a/stream_handler_test.go +++ /dev/null @@ -1,359 +0,0 @@ -/* - * This file is part of caronte (https://github.com/eciavatta/caronte). - * Copyright (c) 2020 Emiliano Ciavatta. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package main - -import ( - "context" - "crypto/rand" - "net" - "testing" - "time" - - "github.com/flier/gohs/hyperscan" - "github.com/google/gopacket/layers" - "github.com/google/gopacket/tcpassembly" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -const testSrcIP = "10.10.10.100" -const testDstIP = "10.10.10.1" -const srcPort = 44444 -const dstPort = 8080 - -func TestReassemblingEmptyStream(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(ConnectionStreams) - patterns, err := hyperscan.NewStreamDatabase(hyperscan.NewPattern("/nope/", 0)) - require.NoError(t, err) - scratch, err := hyperscan.NewScratch(patterns) - require.NoError(t, err) - streamHandler := createTestStreamHandler(wrapper, patterns, scratch) - - streamHandler.Reassembled([]tcpassembly.Reassembly{{ - Bytes: []byte{}, - Skip: 0, - Start: true, - End: true, - }}) - assert.Len(t, streamHandler.indexes, 0, "indexes") - assert.Len(t, streamHandler.timestamps, 0, "timestamps") - assert.Len(t, streamHandler.lossBlocks, 0) - assert.Zero(t, streamHandler.currentIndex) - assert.Zero(t, streamHandler.firstPacketSeen) - assert.Zero(t, streamHandler.lastPacketSeen) - assert.Len(t, streamHandler.documentsIDs, 0) - assert.Zero(t, streamHandler.streamLength) - assert.Len(t, streamHandler.patternMatches, 0) - - completed := false - streamHandler.connection.(*testConnectionHandler).onComplete = func(handler *StreamHandler) { - completed = true - } - streamHandler.ReassemblyComplete() - assert.Equal(t, true, completed) - - err = scratch.Free() - require.NoError(t, err, "free scratch") - err = patterns.Close() - require.NoError(t, err, "close stream database") - wrapper.Destroy(t) -} - -func TestReassemblingSingleDocument(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(ConnectionStreams) - patterns, err := hyperscan.NewStreamDatabase(hyperscan.NewPattern("/impossible_to_match/", 0)) - require.NoError(t, err) - scratch, err := hyperscan.NewScratch(patterns) - require.NoError(t, err) - streamHandler := createTestStreamHandler(wrapper, patterns, scratch) - - payloadLen := 256 - firstTime := time.Unix(0, 0) - middleTime := time.Unix(10, 0) - lastTime := time.Unix(20, 0) - data := make([]byte, MaxDocumentSize) - rand.Read(data) - reassembles := make([]tcpassembly.Reassembly, MaxDocumentSize/payloadLen) - indexes := make([]int, MaxDocumentSize/payloadLen) - timestamps := make([]time.Time, MaxDocumentSize/payloadLen) - lossBlocks := make([]bool, MaxDocumentSize/payloadLen) - for i := 0; i < len(reassembles); i++ { - var seen time.Time - if i == 0 { - seen = firstTime - } else if i == len(reassembles)-1 { - seen = lastTime - } else { - seen = middleTime - } - - reassembles[i] = tcpassembly.Reassembly{ - Bytes: data[i*payloadLen : (i+1)*payloadLen], - Skip: 0, - Start: i == 0, - End: i == len(reassembles)-1, - Seen: seen, - } - indexes[i] = i * payloadLen - timestamps[i] = seen - } - - var results []ConnectionStream - - streamHandler.Reassembled(reassembles) - err = wrapper.Storage.Find(ConnectionStreams).Context(wrapper.Context).All(&results) - require.NoError(t, err) - assert.Len(t, results, 0) - - completed := false - streamHandler.connection.(*testConnectionHandler).onComplete = func(handler *StreamHandler) { - completed = true - } - streamHandler.ReassemblyComplete() - - err = wrapper.Storage.Find(ConnectionStreams).Context(wrapper.Context).All(&results) - require.NoError(t, err) - assert.Len(t, results, 1) - assert.Equal(t, firstTime.Unix(), results[0].ID.Timestamp().Unix()) - assert.Zero(t, results[0].ConnectionID) - assert.Equal(t, 0, results[0].DocumentIndex) - assert.Equal(t, data, results[0].Payload) - assert.Equal(t, indexes, results[0].BlocksIndexes) - assert.Len(t, results[0].BlocksTimestamps, len(timestamps)) // should be compared one by one - assert.Equal(t, lossBlocks, results[0].BlocksLoss) - assert.Len(t, results[0].PatternMatches, 0) - - assert.Equal(t, len(data), streamHandler.currentIndex) - assert.Equal(t, firstTime, streamHandler.firstPacketSeen) - assert.Equal(t, lastTime, streamHandler.lastPacketSeen) - assert.Len(t, streamHandler.documentsIDs, 1) - assert.Equal(t, len(data), streamHandler.streamLength) - assert.Len(t, streamHandler.patternMatches, 0) - - assert.Equal(t, true, completed, "completed") - - err = scratch.Free() - require.NoError(t, err, "free scratch") - err = patterns.Close() - require.NoError(t, err, "close stream database") - wrapper.Destroy(t) -} - -func TestReassemblingMultipleDocuments(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(ConnectionStreams) - patterns, err := hyperscan.NewStreamDatabase(hyperscan.NewPattern("/impossible_to_match/", 0)) - require.NoError(t, err) - scratch, err := hyperscan.NewScratch(patterns) - require.NoError(t, err) - streamHandler := createTestStreamHandler(wrapper, patterns, scratch) - - payloadLen := 256 - firstTime := time.Unix(0, 0) - middleTime := time.Unix(10, 0) - lastTime := time.Unix(20, 0) - dataSize := MaxDocumentSize * 2 - data := make([]byte, dataSize) - rand.Read(data) - reassembles := make([]tcpassembly.Reassembly, dataSize/payloadLen) - indexes := make([]int, dataSize/payloadLen) - timestamps := make([]time.Time, dataSize/payloadLen) - lossBlocks := make([]bool, dataSize/payloadLen) - for i := 0; i < len(reassembles); i++ { - var seen time.Time - if i == 0 { - seen = firstTime - } else if i == len(reassembles)-1 { - seen = lastTime - } else { - seen = middleTime - } - - reassembles[i] = tcpassembly.Reassembly{ - Bytes: data[i*payloadLen : (i+1)*payloadLen], - Skip: 0, - Start: i == 0, - End: i == len(reassembles)-1, - Seen: seen, - } - indexes[i] = i * payloadLen % MaxDocumentSize - timestamps[i] = seen - } - - streamHandler.Reassembled(reassembles) - - var results []ConnectionStream - err = wrapper.Storage.Find(ConnectionStreams).Context(wrapper.Context).All(&results) - require.NoError(t, err) - assert.Len(t, results, 1) - - completed := false - streamHandler.connection.(*testConnectionHandler).onComplete = func(handler *StreamHandler) { - completed = true - } - streamHandler.ReassemblyComplete() - - err = wrapper.Storage.Find(ConnectionStreams).Context(wrapper.Context).All(&results) - require.NoError(t, err) - assert.Len(t, results, 2) - for i := 0; i < 2; i++ { - blockLen := MaxDocumentSize / payloadLen - assert.Equal(t, firstTime.Unix(), results[i].ID.Timestamp().Unix()) - assert.Zero(t, results[i].ConnectionID) - assert.Equal(t, i, results[i].DocumentIndex) - assert.Equal(t, data[MaxDocumentSize*i:MaxDocumentSize*(i+1)], results[i].Payload) - assert.Equal(t, indexes[blockLen*i:blockLen*(i+1)], results[i].BlocksIndexes) - assert.Len(t, results[i].BlocksTimestamps, len(timestamps[blockLen*i:blockLen*(i+1)])) // should be compared one by one - assert.Equal(t, lossBlocks[blockLen*i:blockLen*(i+1)], results[i].BlocksLoss) - assert.Len(t, results[i].PatternMatches, 0) - } - - assert.Equal(t, MaxDocumentSize, streamHandler.currentIndex) - assert.Equal(t, firstTime, streamHandler.firstPacketSeen) - assert.Equal(t, lastTime, streamHandler.lastPacketSeen) - assert.Len(t, streamHandler.documentsIDs, 2) - assert.Equal(t, len(data), streamHandler.streamLength) - assert.Len(t, streamHandler.patternMatches, 0) - - assert.Equal(t, true, completed, "completed") - - err = scratch.Free() - require.NoError(t, err, "free scratch") - err = patterns.Close() - require.NoError(t, err, "close stream database") - wrapper.Destroy(t) -} - -func TestReassemblingPatternMatching(t *testing.T) { - wrapper := NewTestStorageWrapper(t) - wrapper.AddCollection(ConnectionStreams) - a, err := hyperscan.ParsePattern("/a{8}/i") - require.NoError(t, err) - a.Id = 0 - a.Flags |= hyperscan.SomLeftMost - b, err := hyperscan.ParsePattern("/b[c]+b/i") - require.NoError(t, err) - b.Id = 1 - b.Flags |= hyperscan.SomLeftMost - d, err := hyperscan.ParsePattern("/[d]+e[d]+/i") - require.NoError(t, err) - d.Id = 2 - d.Flags |= hyperscan.SomLeftMost - - payload := "aaaaaaaa0aaaaaaaaaa0bbbcccbbb0dddeddddedddd" - expected := map[uint][]PatternSlice{ - 0: {{0, 8}, {9, 17}, {10, 18}, {11, 19}}, - 1: {{22, 27}}, - 2: {{30, 38}, {34, 43}}, - } - - patterns, err := hyperscan.NewStreamDatabase(a, b, d) - require.NoError(t, err) - scratch, err := hyperscan.NewScratch(patterns) - require.NoError(t, err) - streamHandler := createTestStreamHandler(wrapper, patterns, scratch) - - seen := time.Unix(0, 0) - streamHandler.Reassembled([]tcpassembly.Reassembly{{ - Bytes: []byte(payload), - Skip: 0, - Start: true, - End: true, - Seen: seen, - }}) - - var results []ConnectionStream - err = wrapper.Storage.Find(ConnectionStreams).Context(wrapper.Context).All(&results) - require.NoError(t, err) - assert.Len(t, results, 0) - - completed := false - streamHandler.connection.(*testConnectionHandler).onComplete = func(handler *StreamHandler) { - completed = true - } - streamHandler.ReassemblyComplete() - - err = wrapper.Storage.Find(ConnectionStreams).Context(wrapper.Context).All(&results) - require.NoError(t, err) - assert.Len(t, results, 1) - assert.Equal(t, seen.Unix(), results[0].ID.Timestamp().Unix()) - assert.Zero(t, results[0].ConnectionID) - assert.Equal(t, 0, results[0].DocumentIndex) - assert.Equal(t, []byte(payload), results[0].Payload) - assert.Equal(t, []int{0}, results[0].BlocksIndexes) - assert.Len(t, results[0].BlocksTimestamps, 1) // should be compared one by one - assert.Equal(t, []bool{false}, results[0].BlocksLoss) - assert.Equal(t, expected, results[0].PatternMatches) - - assert.Equal(t, len(payload), streamHandler.currentIndex) - assert.Equal(t, seen, streamHandler.firstPacketSeen) - assert.Equal(t, seen, streamHandler.lastPacketSeen) - assert.Len(t, streamHandler.documentsIDs, 1) - assert.Equal(t, len(payload), streamHandler.streamLength) - - assert.Equal(t, true, completed, "completed") - - err = scratch.Free() - require.NoError(t, err, "free scratch") - err = patterns.Close() - require.NoError(t, err, "close stream database") - wrapper.Destroy(t) -} - -func createTestStreamHandler(wrapper *TestStorageWrapper, patterns hyperscan.StreamDatabase, scratch *hyperscan.Scratch) StreamHandler { - testConnectionHandler := &testConnectionHandler{ - wrapper: wrapper, - patterns: patterns, - } - - srcIP := layers.NewIPEndpoint(net.ParseIP(testSrcIP)) - dstIP := layers.NewIPEndpoint(net.ParseIP(testDstIP)) - srcPort := layers.NewTCPPortEndpoint(srcPort) - dstPort := layers.NewTCPPortEndpoint(dstPort) - - scanner := Scanner{scratch: scratch, version: ZeroRowID} - return NewStreamHandler(testConnectionHandler, StreamFlow{srcIP, dstIP, srcPort, dstPort}, scanner, true) // TODO: test isClient -} - -type testConnectionHandler struct { - wrapper *TestStorageWrapper - patterns hyperscan.StreamDatabase - onComplete func(*StreamHandler) -} - -func (tch *testConnectionHandler) Storage() Storage { - return tch.wrapper.Storage -} - -func (tch *testConnectionHandler) Context() context.Context { - return tch.wrapper.Context -} - -func (tch *testConnectionHandler) PatternsDatabase() hyperscan.StreamDatabase { - return tch.patterns -} - -func (tch *testConnectionHandler) PatternsDatabaseSize() int { - return 8 -} - -func (tch *testConnectionHandler) Complete(handler *StreamHandler) { - tch.onComplete(handler) -} |