From b6253d567bbe50e6aa146e148a333c0f2e104e51 Mon Sep 17 00:00:00 2001 From: mmetc <92726601+mmetc@users.noreply.github.com> Date: Thu, 16 May 2024 11:13:39 +0200 Subject: [PATCH] simulation for local scenarios (#3010) --- pkg/cwhub/hub.go | 26 ++++++++++------ pkg/cwhub/leakybucket.go | 53 --------------------------------- pkg/cwhub/sync.go | 5 +++- pkg/leakybucket/manager_load.go | 16 ++++------ test/bats/50_simulation.bats | 13 ++++++++ 5 files changed, 40 insertions(+), 73 deletions(-) delete mode 100644 pkg/cwhub/leakybucket.go diff --git a/pkg/cwhub/hub.go b/pkg/cwhub/hub.go index 87a6644bc72..e7d927f54b1 100644 --- a/pkg/cwhub/hub.go +++ b/pkg/cwhub/hub.go @@ -17,11 +17,12 @@ import ( // Hub is the main structure for the package. type Hub struct { - items HubItems // Items read from HubDir and InstallDir - local *csconfig.LocalHubCfg - remote *RemoteHubCfg - logger *logrus.Logger - Warnings []string // Warnings encountered during sync + items HubItems // Items read from HubDir and InstallDir + pathIndex map[string]*Item + local *csconfig.LocalHubCfg + remote *RemoteHubCfg + logger *logrus.Logger + Warnings []string // Warnings encountered during sync } // GetDataDir returns the data directory, where data sets are installed. @@ -43,9 +44,10 @@ func NewHub(local *csconfig.LocalHubCfg, remote *RemoteHubCfg, updateIndex bool, } hub := &Hub{ - local: local, - remote: remote, - logger: logger, + local: local, + remote: remote, + logger: logger, + pathIndex: make(map[string]*Item, 0), } if updateIndex { @@ -137,7 +139,7 @@ func (h *Hub) ItemStats() []string { } ret := []string{ - fmt.Sprintf("Loaded: %s", loaded), + "Loaded: " + loaded, } if local > 0 || tainted > 0 { @@ -169,6 +171,7 @@ func (h *Hub) addItem(item *Item) { } h.items[item.Type][item.Name] = item + h.pathIndex[item.State.LocalPath] = item } // GetItemMap returns the map of items for a given type. @@ -181,6 +184,11 @@ func (h *Hub) GetItem(itemType string, itemName string) *Item { return h.GetItemMap(itemType)[itemName] } +// GetItemByPath returns an item from hub based on its (absolute) local path. +func (h *Hub) GetItemByPath(itemPath string) *Item { + return h.pathIndex[itemPath] +} + // GetItemFQ returns an item from hub based on its type and name (type:author/name). func (h *Hub) GetItemFQ(itemFQName string) (*Item, error) { // type and name are separated by a colon diff --git a/pkg/cwhub/leakybucket.go b/pkg/cwhub/leakybucket.go deleted file mode 100644 index 8143e9433ee..00000000000 --- a/pkg/cwhub/leakybucket.go +++ /dev/null @@ -1,53 +0,0 @@ -package cwhub - -// Resolve a symlink to find the hub item it points to. -// This file is used only by pkg/leakybucket - -import ( - "fmt" - "os" - "path/filepath" - "strings" -) - -// itemKey extracts the map key of an item (i.e. author/name) from its pathname. Follows a symlink if necessary. -func itemKey(itemPath string) (string, error) { - f, err := os.Lstat(itemPath) - if err != nil { - return "", fmt.Errorf("while performing lstat on %s: %w", itemPath, err) - } - - if f.Mode()&os.ModeSymlink == 0 { - // it's not a symlink, so the filename itsef should be the key - return filepath.Base(itemPath), nil - } - - // resolve the symlink to hub file - pathInHub, err := os.Readlink(itemPath) - if err != nil { - return "", fmt.Errorf("while reading symlink of %s: %w", itemPath, err) - } - - author := filepath.Base(filepath.Dir(pathInHub)) - - fname := filepath.Base(pathInHub) - fname = strings.TrimSuffix(fname, ".yaml") - fname = strings.TrimSuffix(fname, ".yml") - - return fmt.Sprintf("%s/%s", author, fname), nil -} - -// GetItemByPath retrieves an item from the hub index based on its local path. -func (h *Hub) GetItemByPath(itemType string, itemPath string) (*Item, error) { - itemKey, err := itemKey(itemPath) - if err != nil { - return nil, err - } - - item := h.GetItem(itemType, itemKey) - if item == nil { - return nil, fmt.Errorf("%s not found in %s", itemKey, itemType) - } - - return item, nil -} diff --git a/pkg/cwhub/sync.go b/pkg/cwhub/sync.go index 42db255c883..fd5d6b81220 100644 --- a/pkg/cwhub/sync.go +++ b/pkg/cwhub/sync.go @@ -9,9 +9,10 @@ import ( "strings" "github.com/Masterminds/semver/v3" - "github.com/crowdsecurity/go-cs-lib/downloader" "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" + + "github.com/crowdsecurity/go-cs-lib/downloader" ) func isYAMLFileName(path string) bool { @@ -271,6 +272,8 @@ func (h *Hub) itemVisit(path string, f os.DirEntry, err error) error { return err } + h.pathIndex[path] = item + return nil } diff --git a/pkg/leakybucket/manager_load.go b/pkg/leakybucket/manager_load.go index bc259c18319..c94291100a4 100644 --- a/pkg/leakybucket/manager_load.go +++ b/pkg/leakybucket/manager_load.go @@ -253,7 +253,7 @@ func LoadBuckets(cscfg *csconfig.CrowdsecServiceCfg, hub *cwhub.Hub, files []str ok, err := cwversion.Satisfies(bucketFactory.FormatVersion, cwversion.Constraint_scenario) if err != nil { - return nil, nil, fmt.Errorf("failed to check version : %s", err) + return nil, nil, fmt.Errorf("failed to check version: %w", err) } if !ok { @@ -265,20 +265,16 @@ func LoadBuckets(cscfg *csconfig.CrowdsecServiceCfg, hub *cwhub.Hub, files []str bucketFactory.BucketName = seed.Generate() bucketFactory.ret = response - hubItem, err := hub.GetItemByPath(cwhub.SCENARIOS, bucketFactory.Filename) - if err != nil { - log.Errorf("scenario %s (%s) couldn't be find in hub (ignore if in unit tests)", bucketFactory.Name, bucketFactory.Filename) + hubItem := hub.GetItemByPath(bucketFactory.Filename) + if hubItem == nil { + log.Errorf("scenario %s (%s) could not be found in hub (ignore if in unit tests)", bucketFactory.Name, bucketFactory.Filename) } else { if cscfg.SimulationConfig != nil { bucketFactory.Simulated = cscfg.SimulationConfig.IsSimulated(hubItem.Name) } - if hubItem != nil { - bucketFactory.ScenarioVersion = hubItem.State.LocalVersion - bucketFactory.hash = hubItem.State.LocalHash - } else { - log.Errorf("scenario %s (%s) couldn't be find in hub (ignore if in unit tests)", bucketFactory.Name, bucketFactory.Filename) - } + bucketFactory.ScenarioVersion = hubItem.State.LocalVersion + bucketFactory.hash = hubItem.State.LocalHash } bucketFactory.wgDumpState = buckets.wgDumpState diff --git a/test/bats/50_simulation.bats b/test/bats/50_simulation.bats index ab4145551ff..2dc93e62d06 100644 --- a/test/bats/50_simulation.bats +++ b/test/bats/50_simulation.bats @@ -62,6 +62,19 @@ setup() { assert_json '[]' } +@test "simulated local scenario: expect no decision" { + CONFIG_DIR=$(dirname "$CONFIG_YAML") + HUB_DIR=$(config_get '.config_paths.hub_dir') + rune -0 mkdir -p "$CONFIG_DIR"/scenarios + # replace an installed scenario with a local version + rune -0 cp -r "$HUB_DIR"/scenarios/crowdsecurity/ssh-bf.yaml "$CONFIG_DIR"/scenarios/ssh-bf2.yaml + rune -0 cscli scenarios remove crowdsecurity/ssh-bf --force --purge + rune -0 cscli simulation enable crowdsecurity/ssh-bf + fake_log | "$CROWDSEC" -dsn file:///dev/fd/0 -type syslog -no-api + rune -0 cscli decisions list --no-simu -o json + assert_json '[]' +} + @test "global simulation, listing non-simulated: expect no decision" { rune -0 cscli simulation disable crowdsecurity/ssh-bf rune -0 cscli simulation enable --global