lint: pkg/cwhub (#2510)
no functional changes - reformat - comments - whitespace - removed a dot or two in log messages - some "var x=y" -> x:=y
This commit is contained in:
parent
6dadfcb2ef
commit
8b5ad6990d
|
@ -15,7 +15,7 @@ import (
|
|||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
/*managed configuration types*/
|
||||
// managed configuration types
|
||||
var PARSERS = "parsers"
|
||||
var PARSERS_OVFLW = "postoverflows"
|
||||
var SCENARIOS = "scenarios"
|
||||
|
@ -44,7 +44,7 @@ type ItemHubStatus struct {
|
|||
|
||||
// Item can be: parsed, scenario, collection
|
||||
type Item struct {
|
||||
/*descriptive info*/
|
||||
// descriptive info
|
||||
Type string `yaml:"type,omitempty" json:"type,omitempty"` // parser|postoverflows|scenario|collection(|enrich)
|
||||
Stage string `json:"stage,omitempty" yaml:"stage,omitempty,omitempty"` // Stage for parser|postoverflow: s00-raw/s01-...
|
||||
Name string `json:"name,omitempty"` // as seen in .config.json, usually "author/name"
|
||||
|
@ -52,16 +52,16 @@ type Item struct {
|
|||
Description string `yaml:"description,omitempty" json:"description,omitempty"` // as seen in .config.json
|
||||
Author string `json:"author,omitempty"` // as seen in .config.json
|
||||
References []string `yaml:"references,omitempty" json:"references,omitempty"` // as seen in .config.json
|
||||
BelongsToCollections []string `yaml:"belongs_to_collections,omitempty" json:"belongs_to_collections,omitempty"` /*if it's part of collections, track name here*/
|
||||
BelongsToCollections []string `yaml:"belongs_to_collections,omitempty" json:"belongs_to_collections,omitempty"` // if it's part of collections, track name here
|
||||
|
||||
/*remote (hub) infos*/
|
||||
// remote (hub) infos
|
||||
RemoteURL string `yaml:"remoteURL,omitempty" json:"remoteURL,omitempty"` // the full remote uri of file in http
|
||||
RemotePath string `json:"path,omitempty" yaml:"remote_path,omitempty"` // the path relative to git ie. /parsers/stage/author/file.yaml
|
||||
RemoteHash string `yaml:"hash,omitempty" json:"hash,omitempty"` // the meow
|
||||
Version string `json:"version,omitempty"` // the last version
|
||||
Versions map[string]ItemVersion `json:"versions,omitempty" yaml:"-"` // the list of existing versions
|
||||
|
||||
/*local (deployed) infos*/
|
||||
// local (deployed) infos
|
||||
LocalPath string `yaml:"local_path,omitempty" json:"local_path,omitempty"` // the local path relative to ${CFG_DIR}
|
||||
// LocalHubPath string
|
||||
LocalVersion string `json:"local_version,omitempty"`
|
||||
|
@ -72,7 +72,7 @@ type Item struct {
|
|||
Tainted bool `json:"tainted,omitempty"` // has it been locally modified
|
||||
Local bool `json:"local,omitempty"` // if it's a non versioned control one
|
||||
|
||||
/*if it's a collection, it not a single file*/
|
||||
// if it's a collection, it not a single file
|
||||
Parsers []string `yaml:"parsers,omitempty" json:"parsers,omitempty"`
|
||||
PostOverflows []string `yaml:"postoverflows,omitempty" json:"postoverflows,omitempty"`
|
||||
Scenarios []string `yaml:"scenarios,omitempty" json:"scenarios,omitempty"`
|
||||
|
@ -88,6 +88,7 @@ func (i *Item) toHubStatus() ItemHubStatus {
|
|||
|
||||
status, ok, warning, managed := ItemStatus(*i)
|
||||
hubStatus.Status = status
|
||||
|
||||
if !managed {
|
||||
hubStatus.UTF8_Status = fmt.Sprintf("%v %s", emoji.House, status)
|
||||
} else if !i.Installed {
|
||||
|
@ -97,13 +98,14 @@ func (i *Item) toHubStatus() ItemHubStatus {
|
|||
} else if ok {
|
||||
hubStatus.UTF8_Status = fmt.Sprintf("%v %s", emoji.CheckMark, status)
|
||||
}
|
||||
|
||||
return hubStatus
|
||||
}
|
||||
|
||||
var skippedLocal = 0
|
||||
var skippedTainted = 0
|
||||
|
||||
/*To be used when reference(s) (is/are) missing in a collection*/
|
||||
// To be used when reference(s) (is/are) missing in a collection
|
||||
var ReferenceMissingError = errors.New("Reference(s) missing in collection")
|
||||
var MissingHubIndex = errors.New("hub index can't be found")
|
||||
|
||||
|
@ -114,7 +116,7 @@ func GetVersionStatus(v *Item) int {
|
|||
|
||||
// calculate sha256 of a file
|
||||
func getSHA256(filepath string) (string, error) {
|
||||
/* Digest of file */
|
||||
// Digest of file
|
||||
f, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to open '%s': %s", filepath, err)
|
||||
|
@ -131,29 +133,33 @@ func getSHA256(filepath string) (string, error) {
|
|||
}
|
||||
|
||||
func GetItemMap(itemType string) map[string]Item {
|
||||
var m map[string]Item
|
||||
var ok bool
|
||||
var (
|
||||
m map[string]Item
|
||||
ok bool
|
||||
)
|
||||
|
||||
if m, ok = hubIdx[itemType]; !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// GetItemByPath retrieves the item from hubIdx based on the path. To achieve this it will resolve symlink to find associated hub item.
|
||||
func GetItemByPath(itemType string, itemPath string) (*Item, error) {
|
||||
/*try to resolve symlink*/
|
||||
// try to resolve symlink
|
||||
finalName := ""
|
||||
|
||||
f, err := os.Lstat(itemPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while performing lstat on %s: %w", itemPath, err)
|
||||
}
|
||||
|
||||
if f.Mode()&os.ModeSymlink == 0 {
|
||||
/*it's not a symlink, it should be the filename itsef the key*/
|
||||
// it's not a symlink, it should be the filename itsef the key
|
||||
finalName = filepath.Base(itemPath)
|
||||
} else {
|
||||
/*resolve the symlink to hub file*/
|
||||
// resolve the symlink to hub file
|
||||
pathInHub, err := os.Readlink(itemPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while reading symlink of %s: %w", itemPath, err)
|
||||
|
@ -167,13 +173,15 @@ func GetItemByPath(itemType string, itemPath string) (*Item, error) {
|
|||
finalName = fmt.Sprintf("%s/%s", author, fname)
|
||||
}
|
||||
|
||||
/*it's not a symlink, it should be the filename itsef the key*/
|
||||
// it's not a symlink, it should be the filename itsef the key
|
||||
if m := GetItemMap(itemType); m != nil {
|
||||
if v, ok := m[finalName]; ok {
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%s not found in %s", finalName, itemType)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("item type %s doesn't exist", itemType)
|
||||
}
|
||||
|
||||
|
@ -181,26 +189,32 @@ func GetItem(itemType string, itemName string) *Item {
|
|||
if m, ok := GetItemMap(itemType)[itemName]; ok {
|
||||
return &m
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddItem(itemType string, item Item) error {
|
||||
in := false
|
||||
|
||||
for _, itype := range ItemTypes {
|
||||
if itype == itemType {
|
||||
in = true
|
||||
}
|
||||
}
|
||||
|
||||
if !in {
|
||||
return fmt.Errorf("ItemType %s is unknown", itemType)
|
||||
}
|
||||
|
||||
hubIdx[itemType][item.Name] = item
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DisplaySummary() {
|
||||
log.Printf("Loaded %d collecs, %d parsers, %d scenarios, %d post-overflow parsers", len(hubIdx[COLLECTIONS]),
|
||||
len(hubIdx[PARSERS]), len(hubIdx[SCENARIOS]), len(hubIdx[PARSERS_OVFLW]))
|
||||
|
||||
if skippedLocal > 0 || skippedTainted > 0 {
|
||||
log.Printf("unmanaged items: %d local, %d tainted", skippedLocal, skippedTainted)
|
||||
}
|
||||
|
@ -210,6 +224,7 @@ func DisplaySummary() {
|
|||
func ItemStatus(v Item) (string, bool, bool, bool) {
|
||||
strret := "disabled"
|
||||
Ok := false
|
||||
|
||||
if v.Installed {
|
||||
Ok = true
|
||||
strret = "enabled"
|
||||
|
@ -230,6 +245,7 @@ func ItemStatus(v Item) (string, bool, bool, bool) {
|
|||
strret += ",update-available"
|
||||
Warning = true
|
||||
}
|
||||
|
||||
return strret, Ok, Warning, Managed
|
||||
}
|
||||
|
||||
|
@ -240,9 +256,11 @@ func GetInstalledScenariosAsString() ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("while fetching scenarios: %w", err)
|
||||
}
|
||||
|
||||
for _, it := range items {
|
||||
retStr = append(retStr, it.Name)
|
||||
}
|
||||
|
||||
return retStr, nil
|
||||
}
|
||||
|
||||
|
@ -252,11 +270,13 @@ func GetInstalledScenarios() ([]Item, error) {
|
|||
if _, ok := hubIdx[SCENARIOS]; !ok {
|
||||
return nil, fmt.Errorf("no scenarios in hubIdx")
|
||||
}
|
||||
|
||||
for _, item := range hubIdx[SCENARIOS] {
|
||||
if item.Installed {
|
||||
retItems = append(retItems, item)
|
||||
}
|
||||
}
|
||||
|
||||
return retItems, nil
|
||||
}
|
||||
|
||||
|
@ -266,11 +286,13 @@ func GetInstalledParsers() ([]Item, error) {
|
|||
if _, ok := hubIdx[PARSERS]; !ok {
|
||||
return nil, fmt.Errorf("no parsers in hubIdx")
|
||||
}
|
||||
|
||||
for _, item := range hubIdx[PARSERS] {
|
||||
if item.Installed {
|
||||
retItems = append(retItems, item)
|
||||
}
|
||||
}
|
||||
|
||||
return retItems, nil
|
||||
}
|
||||
|
||||
|
@ -281,9 +303,11 @@ func GetInstalledParsersAsString() ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("while fetching parsers: %w", err)
|
||||
}
|
||||
|
||||
for _, it := range items {
|
||||
retStr = append(retStr, it.Name)
|
||||
}
|
||||
|
||||
return retStr, nil
|
||||
}
|
||||
|
||||
|
@ -293,11 +317,13 @@ func GetInstalledPostOverflows() ([]Item, error) {
|
|||
if _, ok := hubIdx[PARSERS_OVFLW]; !ok {
|
||||
return nil, fmt.Errorf("no post overflows in hubIdx")
|
||||
}
|
||||
|
||||
for _, item := range hubIdx[PARSERS_OVFLW] {
|
||||
if item.Installed {
|
||||
retItems = append(retItems, item)
|
||||
}
|
||||
}
|
||||
|
||||
return retItems, nil
|
||||
}
|
||||
|
||||
|
@ -308,9 +334,11 @@ func GetInstalledPostOverflowsAsString() ([]string, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("while fetching post overflows: %w", err)
|
||||
}
|
||||
|
||||
for _, it := range items {
|
||||
retStr = append(retStr, it.Name)
|
||||
}
|
||||
|
||||
return retStr, nil
|
||||
}
|
||||
|
||||
|
@ -325,6 +353,7 @@ func GetInstalledCollectionsAsString() ([]string, error) {
|
|||
for _, it := range items {
|
||||
retStr = append(retStr, it.Name)
|
||||
}
|
||||
|
||||
return retStr, nil
|
||||
}
|
||||
|
||||
|
@ -334,11 +363,13 @@ func GetInstalledCollections() ([]Item, error) {
|
|||
if _, ok := hubIdx[COLLECTIONS]; !ok {
|
||||
return nil, fmt.Errorf("no collection in hubIdx")
|
||||
}
|
||||
|
||||
for _, item := range hubIdx[COLLECTIONS] {
|
||||
if item.Installed {
|
||||
retItems = append(retItems, item)
|
||||
}
|
||||
}
|
||||
|
||||
return retItems, nil
|
||||
}
|
||||
|
||||
|
@ -351,7 +382,8 @@ func GetHubStatusForItemType(itemType string, name string, all bool) []ItemHubSt
|
|||
}
|
||||
|
||||
var ret = make([]ItemHubStatus, 0)
|
||||
/*remember, you do it for the user :)*/
|
||||
|
||||
// remember, you do it for the user :)
|
||||
for _, item := range hubIdx[itemType] {
|
||||
if name != "" && name != item.Name {
|
||||
// user has requested a specific name
|
||||
|
@ -364,6 +396,8 @@ func GetHubStatusForItemType(itemType string, name string, all bool) []ItemHubSt
|
|||
// Check the item status
|
||||
ret = append(ret, item.toHubStatus())
|
||||
}
|
||||
|
||||
sort.Slice(ret, func(i, j int) bool { return ret[i].Name < ret[j].Name })
|
||||
|
||||
return ret
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -33,6 +34,7 @@ func TestItemStatus(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to download index : %s", err)
|
||||
}
|
||||
|
||||
if err := GetHubIdx(cfg.Hub); err != nil {
|
||||
t.Fatalf("failed to load hub index : %s", err)
|
||||
}
|
||||
|
@ -49,10 +51,12 @@ func TestItemStatus(t *testing.T) {
|
|||
if item == nil {
|
||||
t.Fatalf("expected item")
|
||||
}
|
||||
|
||||
item.Installed = true
|
||||
item.UpToDate = false
|
||||
item.Local = false
|
||||
item.Tainted = false
|
||||
|
||||
txt, _, _, _ := ItemStatus(*item)
|
||||
if txt != "enabled,update-available" {
|
||||
t.Fatalf("got '%s'", txt)
|
||||
|
@ -62,6 +66,7 @@ func TestItemStatus(t *testing.T) {
|
|||
item.UpToDate = false
|
||||
item.Local = true
|
||||
item.Tainted = false
|
||||
|
||||
txt, _, _, _ = ItemStatus(*item)
|
||||
if txt != "disabled,local" {
|
||||
t.Fatalf("got '%s'", txt)
|
||||
|
@ -69,6 +74,7 @@ func TestItemStatus(t *testing.T) {
|
|||
|
||||
break
|
||||
}
|
||||
|
||||
DisplaySummary()
|
||||
}
|
||||
|
||||
|
@ -81,6 +87,7 @@ func TestGetters(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to download index : %s", err)
|
||||
}
|
||||
|
||||
if err := GetHubIdx(cfg.Hub); err != nil {
|
||||
t.Fatalf("failed to load hub index : %s", err)
|
||||
}
|
||||
|
@ -90,6 +97,7 @@ func TestGetters(t *testing.T) {
|
|||
if empty != nil {
|
||||
t.Fatalf("expected nil result")
|
||||
}
|
||||
|
||||
// get existing map
|
||||
x := GetItemMap(COLLECTIONS)
|
||||
if len(x) == 0 {
|
||||
|
@ -130,7 +138,6 @@ func TestGetters(t *testing.T) {
|
|||
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestIndexDownload(t *testing.T) {
|
||||
|
@ -142,6 +149,7 @@ func TestIndexDownload(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to download index : %s", err)
|
||||
}
|
||||
|
||||
if err := GetHubIdx(cfg.Hub); err != nil {
|
||||
t.Fatalf("failed to load hub index : %s", err)
|
||||
}
|
||||
|
@ -152,15 +160,18 @@ func getTestCfg() (cfg *csconfig.Config) {
|
|||
cfg.Hub.ConfigDir, _ = filepath.Abs("./install")
|
||||
cfg.Hub.HubDir, _ = filepath.Abs("./hubdir")
|
||||
cfg.Hub.HubIndexFile = filepath.Clean("./hubdir/.index.json")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func envSetup(t *testing.T) *csconfig.Config {
|
||||
resetResponseByPath()
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
cfg := getTestCfg()
|
||||
|
||||
defaultTransport := http.DefaultClient.Transport
|
||||
|
||||
t.Cleanup(func() {
|
||||
http.DefaultClient.Transport = defaultTransport
|
||||
})
|
||||
|
@ -189,7 +200,6 @@ func envSetup(t *testing.T) *csconfig.Config {
|
|||
return cfg
|
||||
}
|
||||
|
||||
|
||||
func envTearDown(cfg *csconfig.Config) {
|
||||
if err := os.RemoveAll(cfg.Hub.ConfigDir); err != nil {
|
||||
log.Fatalf("failed to remove %s : %s", cfg.Hub.ConfigDir, err)
|
||||
|
@ -200,23 +210,25 @@ func envTearDown(cfg *csconfig.Config) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
func testInstallItem(cfg *csconfig.Hub, t *testing.T, item Item) {
|
||||
|
||||
// Install the parser
|
||||
item, err := DownloadLatest(cfg, item, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("error while downloading %s : %v", item.Name, err)
|
||||
}
|
||||
|
||||
if err, _ := LocalSync(cfg); err != nil {
|
||||
t.Fatalf("taint: failed to run localSync : %s", err)
|
||||
}
|
||||
|
||||
if !hubIdx[item.Type][item.Name].UpToDate {
|
||||
t.Fatalf("download: %s should be up-to-date", item.Name)
|
||||
}
|
||||
|
||||
if hubIdx[item.Type][item.Name].Installed {
|
||||
t.Fatalf("download: %s should not be installed", item.Name)
|
||||
}
|
||||
|
||||
if hubIdx[item.Type][item.Name].Tainted {
|
||||
t.Fatalf("download: %s should not be tainted", item.Name)
|
||||
}
|
||||
|
@ -225,9 +237,11 @@ func testInstallItem(cfg *csconfig.Hub, t *testing.T, item Item) {
|
|||
if err != nil {
|
||||
t.Fatalf("error while enabling %s : %v.", item.Name, err)
|
||||
}
|
||||
|
||||
if err, _ := LocalSync(cfg); err != nil {
|
||||
t.Fatalf("taint: failed to run localSync : %s", err)
|
||||
}
|
||||
|
||||
if !hubIdx[item.Type][item.Name].Installed {
|
||||
t.Fatalf("install: %s should be installed", item.Name)
|
||||
}
|
||||
|
@ -237,6 +251,7 @@ func testTaintItem(cfg *csconfig.Hub, t *testing.T, item Item) {
|
|||
if hubIdx[item.Type][item.Name].Tainted {
|
||||
t.Fatalf("pre-taint: %s should not be tainted", item.Name)
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(item.LocalPath, os.O_APPEND|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("(taint) opening %s (%s) : %s", item.LocalPath, item.Name, err)
|
||||
|
@ -246,32 +261,37 @@ func testTaintItem(cfg *csconfig.Hub, t *testing.T, item Item) {
|
|||
if _, err = f.WriteString("tainted"); err != nil {
|
||||
t.Fatalf("tainting %s : %s", item.Name, err)
|
||||
}
|
||||
|
||||
// Local sync and check status
|
||||
if err, _ := LocalSync(cfg); err != nil {
|
||||
t.Fatalf("taint: failed to run localSync : %s", err)
|
||||
}
|
||||
|
||||
if !hubIdx[item.Type][item.Name].Tainted {
|
||||
t.Fatalf("taint: %s should be tainted", item.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func testUpdateItem(cfg *csconfig.Hub, t *testing.T, item Item) {
|
||||
|
||||
if hubIdx[item.Type][item.Name].UpToDate {
|
||||
t.Fatalf("update: %s should NOT be up-to-date", item.Name)
|
||||
}
|
||||
|
||||
// Update it + check status
|
||||
item, err := DownloadLatest(cfg, item, true, true)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to update %s : %s", item.Name, err)
|
||||
}
|
||||
|
||||
// Local sync and check status
|
||||
if err, _ := LocalSync(cfg); err != nil {
|
||||
t.Fatalf("failed to run localSync : %s", err)
|
||||
}
|
||||
|
||||
if !hubIdx[item.Type][item.Name].UpToDate {
|
||||
t.Fatalf("update: %s should be up-to-date", item.Name)
|
||||
}
|
||||
|
||||
if hubIdx[item.Type][item.Name].Tainted {
|
||||
t.Fatalf("update: %s should not be tainted anymore", item.Name)
|
||||
}
|
||||
|
@ -281,43 +301,51 @@ func testDisableItem(cfg *csconfig.Hub, t *testing.T, item Item) {
|
|||
if !item.Installed {
|
||||
t.Fatalf("disable: %s should be installed", item.Name)
|
||||
}
|
||||
|
||||
// Remove
|
||||
item, err := DisableItem(cfg, item, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to disable item : %v", err)
|
||||
}
|
||||
|
||||
// Local sync and check status
|
||||
if err, warns := LocalSync(cfg); err != nil || len(warns) > 0 {
|
||||
t.Fatalf("failed to run localSync : %s (%+v)", err, warns)
|
||||
}
|
||||
|
||||
if hubIdx[item.Type][item.Name].Tainted {
|
||||
t.Fatalf("disable: %s should not be tainted anymore", item.Name)
|
||||
}
|
||||
|
||||
if hubIdx[item.Type][item.Name].Installed {
|
||||
t.Fatalf("disable: %s should not be installed anymore", item.Name)
|
||||
}
|
||||
|
||||
if !hubIdx[item.Type][item.Name].Downloaded {
|
||||
t.Fatalf("disable: %s should still be downloaded", item.Name)
|
||||
}
|
||||
|
||||
// Purge
|
||||
item, err = DisableItem(cfg, item, true, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to purge item : %v", err)
|
||||
}
|
||||
|
||||
// Local sync and check status
|
||||
if err, warns := LocalSync(cfg); err != nil || len(warns) > 0 {
|
||||
t.Fatalf("failed to run localSync : %s (%+v)", err, warns)
|
||||
}
|
||||
|
||||
if hubIdx[item.Type][item.Name].Installed {
|
||||
t.Fatalf("disable: %s should not be installed anymore", item.Name)
|
||||
}
|
||||
|
||||
if hubIdx[item.Type][item.Name].Downloaded {
|
||||
t.Fatalf("disable: %s should not be downloaded", item.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInstallParser(t *testing.T) {
|
||||
|
||||
/*
|
||||
- install a random parser
|
||||
- check its status
|
||||
|
@ -349,7 +377,6 @@ func TestInstallParser(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInstallCollection(t *testing.T) {
|
||||
|
||||
/*
|
||||
- install a random parser
|
||||
- check its status
|
||||
|
@ -376,6 +403,7 @@ func TestInstallCollection(t *testing.T) {
|
|||
it = hubIdx[COLLECTIONS][it.Name]
|
||||
x := GetHubStatusForItemType(COLLECTIONS, it.Name, false)
|
||||
log.Printf("%+v", x)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -395,10 +423,12 @@ func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
StatusCode: http.StatusOK,
|
||||
}
|
||||
response.Header.Set("Content-Type", "application/json")
|
||||
|
||||
responseBody := ""
|
||||
|
||||
log.Printf("---> %s", req.URL.Path)
|
||||
|
||||
/*FAKE PARSER*/
|
||||
// FAKE PARSER
|
||||
if resp, ok := responseByPath[req.URL.Path]; ok {
|
||||
responseBody = resp
|
||||
} else {
|
||||
|
@ -406,12 +436,14 @@ func (t *mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||
}
|
||||
|
||||
response.Body = io.NopCloser(strings.NewReader(responseBody))
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func fileToStringX(path string) string {
|
||||
if f, err := os.Open(path); err == nil {
|
||||
defer f.Close()
|
||||
|
||||
if data, err := io.ReadAll(f); err == nil {
|
||||
return strings.ReplaceAll(string(data), "\r\n", "\n")
|
||||
} else {
|
||||
|
|
|
@ -18,6 +18,7 @@ type DataSet struct {
|
|||
|
||||
func downloadFile(url string, destPath string) error {
|
||||
log.Debugf("downloading %s in %s", url, destPath)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -60,6 +61,7 @@ func GetData(data []*types.DataSource, dataDir string) error {
|
|||
for _, dataS := range data {
|
||||
destPath := filepath.Join(dataDir, dataS.DestPath)
|
||||
log.Infof("downloading data '%s' in '%s'", dataS.SourceURL, destPath)
|
||||
|
||||
err := downloadFile(dataS.SourceURL, destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -4,9 +4,8 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/jarcoal/httpmock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDownloadFile(t *testing.T) {
|
||||
|
@ -26,6 +25,7 @@ func TestDownloadFile(t *testing.T) {
|
|||
"https://example.com/x",
|
||||
httpmock.NewStringResponder(404, "not found"),
|
||||
)
|
||||
|
||||
err := downloadFile("https://example.com/xx", examplePath)
|
||||
assert.NoError(t, err)
|
||||
content, err := os.ReadFile(examplePath)
|
||||
|
|
|
@ -24,36 +24,45 @@ func UpdateHubIdx(hub *csconfig.Hub) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to download index: %w", err)
|
||||
}
|
||||
|
||||
ret, err := LoadPkgIndex(bidx)
|
||||
if err != nil {
|
||||
if !errors.Is(err, ReferenceMissingError) {
|
||||
return fmt.Errorf("failed to read index: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
hubIdx = ret
|
||||
|
||||
if err, _ := LocalSync(hub); err != nil {
|
||||
return fmt.Errorf("failed to sync: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func DownloadHubIdx(hub *csconfig.Hub) ([]byte, error) {
|
||||
log.Debugf("fetching index from branch %s (%s)", HubBranch, fmt.Sprintf(RawFileURLTemplate, HubBranch, HubIndexFile))
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(RawFileURLTemplate, HubBranch, HubIndexFile), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build request for hub index: %w", err)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed http request for hub index: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
if resp.StatusCode == http.StatusNotFound {
|
||||
return nil, ErrIndexNotFound
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("bad http code %d while requesting %s", resp.StatusCode, req.URL.String())
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read request answer for hub index: %w", err)
|
||||
|
@ -80,7 +89,9 @@ func DownloadHubIdx(hub *csconfig.Hub) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("while writing hub index file: %w", err)
|
||||
}
|
||||
|
||||
log.Infof("Wrote new %d bytes index to %s", wsize, hub.HubIndexFile)
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
|
@ -89,11 +100,13 @@ func DownloadLatest(hub *csconfig.Hub, target Item, overwrite bool, updateOnly b
|
|||
var err error
|
||||
|
||||
log.Debugf("Downloading %s %s", target.Type, target.Name)
|
||||
|
||||
if target.Type != COLLECTIONS {
|
||||
if !target.Installed && updateOnly && target.Downloaded {
|
||||
log.Debugf("skipping upgrade of %s : not installed", target.Name)
|
||||
return target, nil
|
||||
}
|
||||
|
||||
return DownloadItem(hub, target, overwrite)
|
||||
}
|
||||
|
||||
|
@ -116,11 +129,13 @@ func DownloadLatest(hub *csconfig.Hub, target Item, overwrite bool, updateOnly b
|
|||
//recurse as it's a collection
|
||||
if ptrtype == COLLECTIONS {
|
||||
log.Tracef("collection, recurse")
|
||||
|
||||
hubIdx[ptrtype][p], err = DownloadLatest(hub, val, overwrite, updateOnly)
|
||||
if err != nil {
|
||||
return target, fmt.Errorf("while downloading %s: %w", val.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
item, err := DownloadItem(hub, val, overwrite)
|
||||
if err != nil {
|
||||
return target, fmt.Errorf("while downloading %s: %w", val.Name, err)
|
||||
|
@ -133,77 +148,95 @@ func DownloadLatest(hub *csconfig.Hub, target Item, overwrite bool, updateOnly b
|
|||
return target, fmt.Errorf("enabling '%s': %w", item.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
hubIdx[ptrtype][p] = item
|
||||
}
|
||||
}
|
||||
|
||||
target, err = DownloadItem(hub, target, overwrite)
|
||||
if err != nil {
|
||||
return target, fmt.Errorf("failed to download item : %s", err)
|
||||
}
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func DownloadItem(hub *csconfig.Hub, target Item, overwrite bool) (Item, error) {
|
||||
var tdir = hub.HubDir
|
||||
var dataFolder = hub.DataDir
|
||||
/*if user didn't --force, don't overwrite local, tainted, up-to-date files*/
|
||||
tdir := hub.HubDir
|
||||
dataFolder := hub.DataDir
|
||||
|
||||
// if user didn't --force, don't overwrite local, tainted, up-to-date files
|
||||
if !overwrite {
|
||||
if target.Tainted {
|
||||
log.Debugf("%s : tainted, not updated", target.Name)
|
||||
return target, nil
|
||||
}
|
||||
|
||||
if target.UpToDate {
|
||||
log.Debugf("%s : up-to-date, not updated", target.Name)
|
||||
// We still have to check if data files are present
|
||||
log.Debugf("%s : up-to-date, not updated", target.Name)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf(RawFileURLTemplate, HubBranch, target.RemotePath), nil)
|
||||
if err != nil {
|
||||
return target, fmt.Errorf("while downloading %s: %w", req.URL.String(), err)
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return target, fmt.Errorf("while downloading %s: %w", req.URL.String(), err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return target, fmt.Errorf("bad http code %d for %s", resp.StatusCode, req.URL.String())
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return target, fmt.Errorf("while reading %s: %w", req.URL.String(), err)
|
||||
}
|
||||
|
||||
h := sha256.New()
|
||||
if _, err := h.Write(body); err != nil {
|
||||
return target, fmt.Errorf("while hashing %s: %w", target.Name, err)
|
||||
}
|
||||
|
||||
meow := fmt.Sprintf("%x", h.Sum(nil))
|
||||
if meow != target.Versions[target.Version].Digest {
|
||||
log.Errorf("Downloaded version doesn't match index, please 'hub update'")
|
||||
log.Debugf("got %s, expected %s", meow, target.Versions[target.Version].Digest)
|
||||
|
||||
return target, fmt.Errorf("invalid download hash for %s", target.Name)
|
||||
}
|
||||
|
||||
//all good, install
|
||||
//check if parent dir exists
|
||||
tmpdirs := strings.Split(tdir+"/"+target.RemotePath, "/")
|
||||
parent_dir := strings.Join(tmpdirs[:len(tmpdirs)-1], "/")
|
||||
|
||||
/*ensure that target file is within target dir*/
|
||||
// ensure that target file is within target dir
|
||||
finalPath, err := filepath.Abs(tdir + "/" + target.RemotePath)
|
||||
if err != nil {
|
||||
return target, fmt.Errorf("filepath.Abs error on %s: %w", tdir+"/"+target.RemotePath, err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(finalPath, tdir) {
|
||||
return target, fmt.Errorf("path %s escapes %s, abort", target.RemotePath, tdir)
|
||||
}
|
||||
/*check dir*/
|
||||
|
||||
// check dir
|
||||
if _, err = os.Stat(parent_dir); os.IsNotExist(err) {
|
||||
log.Debugf("%s doesn't exist, create", parent_dir)
|
||||
|
||||
if err := os.MkdirAll(parent_dir, os.ModePerm); err != nil {
|
||||
return target, fmt.Errorf("while creating parent directories: %w", err)
|
||||
}
|
||||
}
|
||||
/*check actual file*/
|
||||
|
||||
// check actual file
|
||||
if _, err = os.Stat(finalPath); !os.IsNotExist(err) {
|
||||
log.Warningf("%s : overwrite", target.Name)
|
||||
log.Debugf("target: %s/%s", tdir, target.RemotePath)
|
||||
|
@ -215,11 +248,14 @@ func DownloadItem(hub *csconfig.Hub, target Item, overwrite bool) (Item, error)
|
|||
if err != nil {
|
||||
return target, fmt.Errorf("while opening file: %w", err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Write(body)
|
||||
if err != nil {
|
||||
return target, fmt.Errorf("while writing file: %w", err)
|
||||
}
|
||||
|
||||
target.Downloaded = true
|
||||
target.Tainted = false
|
||||
target.UpToDate = true
|
||||
|
@ -229,6 +265,7 @@ func DownloadItem(hub *csconfig.Hub, target Item, overwrite bool) (Item, error)
|
|||
}
|
||||
|
||||
hubIdx[target.Type][target.Name] = target
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
||||
|
@ -238,37 +275,47 @@ func DownloadDataIfNeeded(hub *csconfig.Hub, target Item, force bool) error {
|
|||
itemFile *os.File
|
||||
err error
|
||||
)
|
||||
|
||||
itemFilePath := fmt.Sprintf("%s/%s/%s/%s", hub.ConfigDir, target.Type, target.Stage, target.FileName)
|
||||
|
||||
if itemFile, err = os.Open(itemFilePath); err != nil {
|
||||
return fmt.Errorf("while opening %s: %w", itemFilePath, err)
|
||||
}
|
||||
|
||||
defer itemFile.Close()
|
||||
|
||||
if err = downloadData(dataFolder, force, itemFile); err != nil {
|
||||
return fmt.Errorf("while downloading data for %s: %w", itemFilePath, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadData(dataFolder string, force bool, reader io.Reader) error {
|
||||
var err error
|
||||
|
||||
dec := yaml.NewDecoder(reader)
|
||||
|
||||
for {
|
||||
data := &DataSet{}
|
||||
|
||||
err = dec.Decode(data)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
|
||||
return fmt.Errorf("while reading file: %w", err)
|
||||
}
|
||||
|
||||
download := false
|
||||
|
||||
for _, dataS := range data.Data {
|
||||
if _, err := os.Stat(filepath.Join(dataFolder, dataS.DestPath)); os.IsNotExist(err) {
|
||||
download = true
|
||||
}
|
||||
}
|
||||
|
||||
if download || force {
|
||||
err = GetData(data.Data, dataFolder)
|
||||
if err != nil {
|
||||
|
@ -276,5 +323,6 @@ func downloadData(dataFolder string, force bool, reader io.Reader) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,38 +5,48 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
)
|
||||
|
||||
func TestDownloadHubIdx(t *testing.T) {
|
||||
back := RawFileURLTemplate
|
||||
// bad url template
|
||||
fmt.Println("Test 'bad URL'")
|
||||
|
||||
RawFileURLTemplate = "x"
|
||||
|
||||
ret, err := DownloadHubIdx(&csconfig.Hub{})
|
||||
if err == nil || !strings.HasPrefix(fmt.Sprintf("%s", err), "failed to build request for hub index: parse ") {
|
||||
log.Errorf("unexpected error %s", err)
|
||||
}
|
||||
|
||||
fmt.Printf("->%+v", ret)
|
||||
|
||||
// bad domain
|
||||
fmt.Println("Test 'bad domain'")
|
||||
|
||||
RawFileURLTemplate = "https://baddomain/%s/%s"
|
||||
|
||||
ret, err = DownloadHubIdx(&csconfig.Hub{})
|
||||
if err == nil || !strings.HasPrefix(fmt.Sprintf("%s", err), "failed http request for hub index: Get") {
|
||||
log.Errorf("unexpected error %s", err)
|
||||
}
|
||||
|
||||
fmt.Printf("->%+v", ret)
|
||||
|
||||
// bad target path
|
||||
fmt.Println("Test 'bad target path'")
|
||||
|
||||
RawFileURLTemplate = back
|
||||
|
||||
ret, err = DownloadHubIdx(&csconfig.Hub{HubIndexFile: "/does/not/exist/index.json"})
|
||||
if err == nil || !strings.HasPrefix(fmt.Sprintf("%s", err), "while opening hub index file: open /does/not/exist/index.json:") {
|
||||
log.Errorf("unexpected error %s", err)
|
||||
}
|
||||
|
||||
RawFileURLTemplate = back
|
||||
|
||||
fmt.Printf("->%+v", ret)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ func chooseHubBranch() (string, error) {
|
|||
latest, err := cwversion.Latest()
|
||||
if err != nil {
|
||||
log.Warningf("Unable to retrieve latest crowdsec version: %s, defaulting to master", err)
|
||||
//lint:ignore nilerr reason
|
||||
//lint:ignore nilerr
|
||||
return "master", nil // ignore
|
||||
}
|
||||
|
||||
|
@ -41,8 +41,10 @@ func chooseHubBranch() (string, error) {
|
|||
log.Warnf("Crowdsec is not the latest version. "+
|
||||
"Current version is '%s' and the latest stable version is '%s'. Please update it!",
|
||||
csVersion, latest)
|
||||
|
||||
log.Warnf("As a result, you will not be able to use parsers/scenarios/collections "+
|
||||
"added to Crowdsec Hub after CrowdSec %s", latest)
|
||||
|
||||
return csVersion, nil
|
||||
}
|
||||
|
||||
|
@ -58,8 +60,10 @@ func SetHubBranch() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
HubBranch = branch
|
||||
log.Debugf("Using branch '%s' for the hub", HubBranch)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -72,6 +76,7 @@ func InstallItem(csConfig *csconfig.Config, name string, obtype string, force bo
|
|||
item := *it
|
||||
if downloadOnly && item.Downloaded && item.UpToDate {
|
||||
log.Warningf("%s is already downloaded and up-to-date", item.Name)
|
||||
|
||||
if !force {
|
||||
return nil
|
||||
}
|
||||
|
@ -120,6 +125,7 @@ func RemoveMany(csConfig *csconfig.Config, itemType string, name string, all boo
|
|||
|
||||
item := *it
|
||||
item, err = DisableItem(csConfig.Hub, item, purge, forceAction)
|
||||
|
||||
if err != nil {
|
||||
log.Fatalf("unable to disable %s : %v", item.Name, err)
|
||||
}
|
||||
|
@ -127,6 +133,7 @@ func RemoveMany(csConfig *csconfig.Config, itemType string, name string, all boo
|
|||
if err := AddItem(itemType, item); err != nil {
|
||||
log.Fatalf("unable to add %s: %v", item.Name, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -139,6 +146,7 @@ func RemoveMany(csConfig *csconfig.Config, itemType string, name string, all boo
|
|||
if !v.Installed {
|
||||
continue
|
||||
}
|
||||
|
||||
v, err = DisableItem(csConfig.Hub, v, purge, forceAction)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to disable %s : %v", v.Name, err)
|
||||
|
@ -149,6 +157,7 @@ func RemoveMany(csConfig *csconfig.Config, itemType string, name string, all boo
|
|||
}
|
||||
disabled++
|
||||
}
|
||||
|
||||
log.Infof("Disabled %d items", disabled)
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ func TestUpgradeConfigNewScenarioInCollection(t *testing.T) {
|
|||
if err := UpdateHubIdx(cfg.Hub); err != nil {
|
||||
t.Fatalf("failed to download index : %s", err)
|
||||
}
|
||||
|
||||
getHubIdxOrFail(t)
|
||||
|
||||
require.True(t, hubIdx[COLLECTIONS]["crowdsecurity/test_collection"].Downloaded)
|
||||
|
@ -49,7 +50,6 @@ func TestUpgradeConfigNewScenarioInCollection(t *testing.T) {
|
|||
|
||||
require.True(t, hubIdx[SCENARIOS]["crowdsecurity/barfoo_scenario"].Downloaded)
|
||||
require.True(t, hubIdx[SCENARIOS]["crowdsecurity/barfoo_scenario"].Installed)
|
||||
|
||||
}
|
||||
|
||||
// Install a collection, disable a scenario.
|
||||
|
@ -140,6 +140,7 @@ func TestUpgradeConfigNewScenarioIsInstalledWhenReferencedScenarioIsDisabled(t *
|
|||
if err := UpdateHubIdx(cfg.Hub); err != nil {
|
||||
t.Fatalf("failed to download index : %s", err)
|
||||
}
|
||||
|
||||
require.False(t, hubIdx[SCENARIOS]["crowdsecurity/foobar_scenario"].Installed)
|
||||
getHubIdxOrFail(t)
|
||||
|
||||
|
@ -151,6 +152,7 @@ func TestUpgradeConfigNewScenarioIsInstalledWhenReferencedScenarioIsDisabled(t *
|
|||
|
||||
func assertCollectionDepsInstalled(t *testing.T, collection string) {
|
||||
t.Helper()
|
||||
|
||||
c := hubIdx[COLLECTIONS][collection]
|
||||
require.NoError(t, CollecDepsCheck(&c))
|
||||
}
|
||||
|
|
|
@ -22,15 +22,17 @@ func purgeItem(hub *csconfig.Hub, target Item) (Item, error) {
|
|||
target.Downloaded = false
|
||||
log.Infof("Removed source file [%s] : %s", target.Name, hubpath)
|
||||
hubIdx[target.Type][target.Name] = target
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
||||
// DisableItem to disable an item managed by the hub, removes the symlink if purge is true
|
||||
func DisableItem(hub *csconfig.Hub, target Item, purge bool, force bool) (Item, error) {
|
||||
var tdir = hub.ConfigDir
|
||||
var hdir = hub.HubDir
|
||||
var err error
|
||||
|
||||
tdir := hub.ConfigDir
|
||||
hdir := hub.HubDir
|
||||
|
||||
if !target.Installed {
|
||||
if purge {
|
||||
target, err = purgeItem(hub, target)
|
||||
|
@ -38,6 +40,7 @@ func DisableItem(hub *csconfig.Hub, target Item, purge bool, force bool) (Item,
|
|||
return target, err
|
||||
}
|
||||
}
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
||||
|
@ -54,7 +57,7 @@ func DisableItem(hub *csconfig.Hub, target Item, purge bool, force bool) (Item,
|
|||
return target, fmt.Errorf("%s is tainted, use '--force' to overwrite", target.Name)
|
||||
}
|
||||
|
||||
/*for a COLLECTIONS, disable sub-items*/
|
||||
// for a COLLECTIONS, disable sub-items
|
||||
if target.Type == COLLECTIONS {
|
||||
var tmp = [][]string{target.Parsers, target.PostOverflows, target.Scenarios, target.Collections}
|
||||
for idx, ptr := range tmp {
|
||||
|
@ -63,12 +66,14 @@ func DisableItem(hub *csconfig.Hub, target Item, purge bool, force bool) (Item,
|
|||
if val, ok := hubIdx[ptrtype][p]; ok {
|
||||
// check if the item doesn't belong to another collection before removing it
|
||||
toRemove := true
|
||||
|
||||
for _, collection := range val.BelongsToCollections {
|
||||
if collection != target.Name {
|
||||
toRemove = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if toRemove {
|
||||
hubIdx[ptrtype][p], err = DisableItem(hub, val, purge, force)
|
||||
if err != nil {
|
||||
|
@ -114,6 +119,7 @@ func DisableItem(hub *csconfig.Hub, target Item, purge bool, force bool) (Item,
|
|||
}
|
||||
log.Infof("Removed symlink [%s] : %s", target.Name, syml)
|
||||
}
|
||||
|
||||
target.Installed = false
|
||||
|
||||
if purge {
|
||||
|
@ -122,39 +128,46 @@ func DisableItem(hub *csconfig.Hub, target Item, purge bool, force bool) (Item,
|
|||
return target, err
|
||||
}
|
||||
}
|
||||
|
||||
hubIdx[target.Type][target.Name] = target
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
||||
// creates symlink between actual config file at hub.HubDir and hub.ConfigDir
|
||||
// Handles collections recursively
|
||||
func EnableItem(hub *csconfig.Hub, target Item) (Item, error) {
|
||||
var tdir = hub.ConfigDir
|
||||
var hdir = hub.HubDir
|
||||
var err error
|
||||
|
||||
tdir := hub.ConfigDir
|
||||
hdir := hub.HubDir
|
||||
|
||||
parent_dir := filepath.Clean(tdir + "/" + target.Type + "/" + target.Stage + "/")
|
||||
/*create directories if needed*/
|
||||
// create directories if needed
|
||||
if target.Installed {
|
||||
if target.Tainted {
|
||||
return target, fmt.Errorf("%s is tainted, won't enable unless --force", target.Name)
|
||||
}
|
||||
|
||||
if target.Local {
|
||||
return target, fmt.Errorf("%s is local, won't enable", target.Name)
|
||||
}
|
||||
/* if it's a collection, check sub-items even if the collection file itself is up-to-date */
|
||||
// if it's a collection, check sub-items even if the collection file itself is up-to-date
|
||||
if target.UpToDate && target.Type != COLLECTIONS {
|
||||
log.Tracef("%s is installed and up-to-date, skip.", target.Name)
|
||||
return target, nil
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(parent_dir); os.IsNotExist(err) {
|
||||
log.Printf("%s doesn't exist, create", parent_dir)
|
||||
|
||||
if err := os.MkdirAll(parent_dir, os.ModePerm); err != nil {
|
||||
return target, fmt.Errorf("while creating directory: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
/*install sub-items if it's a collection*/
|
||||
// install sub-items if it's a collection
|
||||
if target.Type == COLLECTIONS {
|
||||
var tmp = [][]string{target.Parsers, target.PostOverflows, target.Scenarios, target.Collections}
|
||||
for idx, ptr := range tmp {
|
||||
|
@ -197,5 +210,6 @@ func EnableItem(hub *csconfig.Hub, target Item) (Item, error) {
|
|||
log.Printf("Enabled %s : %s", target.Type, target.Name)
|
||||
target.Installed = true
|
||||
hubIdx[target.Type][target.Name] = target
|
||||
|
||||
return target, nil
|
||||
}
|
||||
|
|
|
@ -15,19 +15,20 @@ import (
|
|||
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
||||
)
|
||||
|
||||
/*the walk/parser_visit function can't receive extra args*/
|
||||
// the walk/parser_visit function can't receive extra args
|
||||
var hubdir, installdir string
|
||||
|
||||
func parser_visit(path string, f os.DirEntry, err error) error {
|
||||
|
||||
var target Item
|
||||
var local bool
|
||||
var hubpath string
|
||||
var inhub bool
|
||||
var fname string
|
||||
var ftype string
|
||||
var fauthor string
|
||||
var stage string
|
||||
var (
|
||||
target Item
|
||||
local bool
|
||||
hubpath string
|
||||
inhub bool
|
||||
fname string
|
||||
ftype string
|
||||
fauthor string
|
||||
stage string
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
log.Debugf("while syncing hub dir: %s", err)
|
||||
|
@ -52,9 +53,10 @@ func parser_visit(path string, f os.DirEntry, err error) error {
|
|||
|
||||
log.Tracef("path:%s, hubdir:%s, installdir:%s", path, hubdir, installdir)
|
||||
log.Tracef("subs:%v", subs)
|
||||
/*we're in hub (~/.hub/hub/)*/
|
||||
// we're in hub (~/.hub/hub/)
|
||||
if strings.HasPrefix(path, hubdir) {
|
||||
log.Tracef("in hub dir")
|
||||
|
||||
inhub = true
|
||||
//.../hub/parsers/s00-raw/crowdsec/skip-pretag.yaml
|
||||
//.../hub/scenarios/crowdsec/ssh_bf.yaml
|
||||
|
@ -62,11 +64,12 @@ func parser_visit(path string, f os.DirEntry, err error) error {
|
|||
if len(subs) < 4 {
|
||||
log.Fatalf("path is too short : %s (%d)", path, len(subs))
|
||||
}
|
||||
|
||||
fname = subs[len(subs)-1]
|
||||
fauthor = subs[len(subs)-2]
|
||||
stage = subs[len(subs)-3]
|
||||
ftype = subs[len(subs)-4]
|
||||
} else if strings.HasPrefix(path, installdir) { /*we're in install /etc/crowdsec/<type>/... */
|
||||
} else if strings.HasPrefix(path, installdir) { // we're in install /etc/crowdsec/<type>/...
|
||||
log.Tracef("in install dir")
|
||||
if len(subs) < 3 {
|
||||
log.Fatalf("path is too short : %s (%d)", path, len(subs))
|
||||
|
@ -91,7 +94,8 @@ func parser_visit(path string, f os.DirEntry, err error) error {
|
|||
} else if stage == COLLECTIONS {
|
||||
ftype = COLLECTIONS
|
||||
stage = ""
|
||||
} else if ftype != PARSERS && ftype != PARSERS_OVFLW /*its a PARSER / PARSER_OVFLW with a stage */ {
|
||||
} else if ftype != PARSERS && ftype != PARSERS_OVFLW {
|
||||
// its a PARSER / PARSER_OVFLW with a stage
|
||||
return fmt.Errorf("unknown configuration type for file '%s'", path)
|
||||
}
|
||||
|
||||
|
@ -105,6 +109,7 @@ func parser_visit(path string, f os.DirEntry, err error) error {
|
|||
// non symlinks are local user files or hub files
|
||||
if f.Type()&os.ModeSymlink == 0 {
|
||||
local = true
|
||||
|
||||
log.Tracef("%s isn't a symlink", path)
|
||||
} else {
|
||||
hubpath, err = os.Readlink(path)
|
||||
|
@ -139,23 +144,28 @@ func parser_visit(path string, f os.DirEntry, err error) error {
|
|||
_, target.FileName = filepath.Split(path)
|
||||
|
||||
hubIdx[ftype][fname] = target
|
||||
|
||||
return nil
|
||||
}
|
||||
// try to find which configuration item it is
|
||||
log.Tracef("check [%s] of %s", fname, ftype)
|
||||
|
||||
match := false
|
||||
|
||||
for k, v := range hubIdx[ftype] {
|
||||
log.Tracef("check [%s] vs [%s] : %s", fname, v.RemotePath, ftype+"/"+stage+"/"+fname+".yaml")
|
||||
|
||||
if fname != v.FileName {
|
||||
log.Tracef("%s != %s (filename)", fname, v.FileName)
|
||||
continue
|
||||
}
|
||||
|
||||
// wrong stage
|
||||
if v.Stage != stage {
|
||||
continue
|
||||
}
|
||||
/*if we are walking hub dir, just mark present files as downloaded*/
|
||||
|
||||
// if we are walking hub dir, just mark present files as downloaded
|
||||
if inhub {
|
||||
// wrong author
|
||||
if fauthor != v.Author {
|
||||
|
@ -175,15 +185,18 @@ func parser_visit(path string, f os.DirEntry, err error) error {
|
|||
// <type>/<stage>/<author>/<name>.yaml
|
||||
continue
|
||||
}
|
||||
|
||||
sha, err := getSHA256(path)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get sha of %s : %v", path, err)
|
||||
}
|
||||
|
||||
// let's reverse sort the versions to deal with hash collisions (#154)
|
||||
versions := make([]string, 0, len(v.Versions))
|
||||
for k := range v.Versions {
|
||||
versions = append(versions, k)
|
||||
}
|
||||
|
||||
sort.Sort(sort.Reverse(sort.StringSlice(versions)))
|
||||
|
||||
for _, version := range versions {
|
||||
|
@ -192,14 +205,14 @@ func parser_visit(path string, f os.DirEntry, err error) error {
|
|||
// log.Printf("matching filenames, wrong hash %s != %s -- %s", sha, val.Digest, spew.Sdump(v))
|
||||
continue
|
||||
}
|
||||
/*we got an exact match, update struct*/
|
||||
// we got an exact match, update struct
|
||||
if !inhub {
|
||||
log.Tracef("found exact match for %s, version is %s, latest is %s", v.Name, version, v.Version)
|
||||
v.LocalPath = path
|
||||
v.LocalVersion = version
|
||||
v.Tainted = false
|
||||
v.Downloaded = true
|
||||
/*if we're walking the hub, present file doesn't means installed file*/
|
||||
// if we're walking the hub, present file doesn't means installed file
|
||||
v.Installed = true
|
||||
v.LocalHash = sha
|
||||
_, target.FileName = filepath.Split(path)
|
||||
|
@ -207,27 +220,32 @@ func parser_visit(path string, f os.DirEntry, err error) error {
|
|||
v.Downloaded = true
|
||||
v.LocalHash = sha
|
||||
}
|
||||
|
||||
if version == v.Version {
|
||||
log.Tracef("%s is up-to-date", v.Name)
|
||||
v.UpToDate = true
|
||||
}
|
||||
|
||||
match = true
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
if !match {
|
||||
log.Tracef("got tainted match for %s : %s", v.Name, path)
|
||||
|
||||
skippedTainted += 1
|
||||
// the file and the stage is right, but the hash is wrong, it has been tainted by user
|
||||
if !inhub {
|
||||
v.LocalPath = path
|
||||
v.Installed = true
|
||||
}
|
||||
|
||||
v.UpToDate = false
|
||||
v.LocalVersion = "?"
|
||||
v.Tainted = true
|
||||
v.LocalHash = sha
|
||||
_, target.FileName = filepath.Split(path)
|
||||
|
||||
}
|
||||
// update the entry if appropriate
|
||||
// if _, ok := hubIdx[ftype][k]; !ok || !inhub || v.D {
|
||||
|
@ -237,22 +255,25 @@ func parser_visit(path string, f os.DirEntry, err error) error {
|
|||
|
||||
// } else if
|
||||
hubIdx[ftype][k] = v
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Infof("Ignoring file %s of type %s", path, ftype)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CollecDepsCheck(v *Item) error {
|
||||
|
||||
if GetVersionStatus(v) != 0 { // not up-to-date
|
||||
log.Debugf("%s dependencies not checked : not up-to-date", v.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
/*if it's a collection, ensure all the items are installed, or tag it as tainted*/
|
||||
// if it's a collection, ensure all the items are installed, or tag it as tainted
|
||||
if v.Type == COLLECTIONS {
|
||||
log.Tracef("checking submembers of %s installed:%t", v.Name, v.Installed)
|
||||
|
||||
var tmp = [][]string{v.Parsers, v.PostOverflows, v.Scenarios, v.Collections}
|
||||
for idx, ptr := range tmp {
|
||||
ptrtype := ItemTypes[idx]
|
||||
|
@ -261,48 +282,62 @@ func CollecDepsCheck(v *Item) error {
|
|||
if !ok {
|
||||
log.Fatalf("Referred %s %s in collection %s doesn't exist.", ptrtype, p, v.Name)
|
||||
}
|
||||
|
||||
log.Tracef("check %s installed:%t", val.Name, val.Installed)
|
||||
|
||||
if !v.Installed {
|
||||
continue
|
||||
}
|
||||
|
||||
if val.Type == COLLECTIONS {
|
||||
log.Tracef("collec, recurse.")
|
||||
|
||||
if err := CollecDepsCheck(&val); err != nil {
|
||||
if val.Tainted {
|
||||
v.Tainted = true
|
||||
}
|
||||
|
||||
return fmt.Errorf("sub collection %s is broken: %s", val.Name, err)
|
||||
}
|
||||
|
||||
hubIdx[ptrtype][p] = val
|
||||
}
|
||||
|
||||
// propagate the state of sub-items to set
|
||||
if val.Tainted {
|
||||
v.Tainted = true
|
||||
return fmt.Errorf("tainted %s %s, tainted.", ptrtype, p)
|
||||
return fmt.Errorf("tainted %s %s, tainted", ptrtype, p)
|
||||
}
|
||||
|
||||
if !val.Installed && v.Installed {
|
||||
v.Tainted = true
|
||||
return fmt.Errorf("missing %s %s, tainted.", ptrtype, p)
|
||||
return fmt.Errorf("missing %s %s, tainted", ptrtype, p)
|
||||
}
|
||||
|
||||
if !val.UpToDate {
|
||||
v.UpToDate = false
|
||||
return fmt.Errorf("outdated %s %s", ptrtype, p)
|
||||
}
|
||||
|
||||
skip := false
|
||||
|
||||
for idx := range val.BelongsToCollections {
|
||||
if val.BelongsToCollections[idx] == v.Name {
|
||||
skip = true
|
||||
}
|
||||
}
|
||||
|
||||
if !skip {
|
||||
val.BelongsToCollections = append(val.BelongsToCollections, v.Name)
|
||||
}
|
||||
|
||||
hubIdx[ptrtype][p] = val
|
||||
|
||||
log.Tracef("checking for %s - tainted:%t uptodate:%t", p, v.Tainted, v.UpToDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -311,17 +346,17 @@ func SyncDir(hub *csconfig.Hub, dir string) (error, []string) {
|
|||
installdir = hub.ConfigDir
|
||||
warnings := []string{}
|
||||
|
||||
/*For each, scan PARSERS, PARSERS_OVFLW, SCENARIOS and COLLECTIONS last*/
|
||||
// For each, scan PARSERS, PARSERS_OVFLW, SCENARIOS and COLLECTIONS last
|
||||
for _, scan := range ItemTypes {
|
||||
cpath, err := filepath.Abs(fmt.Sprintf("%s/%s", dir, scan))
|
||||
if err != nil {
|
||||
log.Errorf("failed %s : %s", cpath, err)
|
||||
}
|
||||
|
||||
err = filepath.WalkDir(cpath, parser_visit)
|
||||
if err != nil {
|
||||
return err, warnings
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for k, v := range hubIdx[COLLECTIONS] {
|
||||
|
@ -337,13 +372,15 @@ func SyncDir(hub *csconfig.Hub, dir string) (error, []string) {
|
|||
} else { // version is higher than the highest available from hub?
|
||||
warnings = append(warnings, fmt.Sprintf("collection %s is in the future (currently:%s, latest:%s)", v.Name, v.LocalVersion, v.Version))
|
||||
}
|
||||
|
||||
log.Debugf("installed (%s) - status:%d | installed:%s | latest : %s | full : %+v", v.Name, semver.Compare("v"+v.Version, "v"+v.LocalVersion), v.LocalVersion, v.Version, v.Versions)
|
||||
}
|
||||
}
|
||||
|
||||
return nil, warnings
|
||||
}
|
||||
|
||||
/* Updates the infos from HubInit() with the local state */
|
||||
// Updates the infos from HubInit() with the local state
|
||||
func LocalSync(hub *csconfig.Hub) (error, []string) {
|
||||
skippedLocal = 0
|
||||
skippedTainted = 0
|
||||
|
@ -352,10 +389,12 @@ func LocalSync(hub *csconfig.Hub) (error, []string) {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to scan %s : %s", hub.ConfigDir, err), warnings
|
||||
}
|
||||
|
||||
err, _ = SyncDir(hub, hub.HubDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to scan %s : %s", hub.HubDir, err), warnings
|
||||
}
|
||||
|
||||
return nil, warnings
|
||||
}
|
||||
|
||||
|
@ -363,49 +402,61 @@ func GetHubIdx(hub *csconfig.Hub) error {
|
|||
if hub == nil {
|
||||
return fmt.Errorf("no configuration found for hub")
|
||||
}
|
||||
|
||||
log.Debugf("loading hub idx %s", hub.HubIndexFile)
|
||||
|
||||
bidx, err := os.ReadFile(hub.HubIndexFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read index file: %w", err)
|
||||
}
|
||||
|
||||
ret, err := LoadPkgIndex(bidx)
|
||||
if err != nil {
|
||||
if !errors.Is(err, ReferenceMissingError) {
|
||||
log.Fatalf("Unable to load existing index : %v.", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
hubIdx = ret
|
||||
|
||||
err, _ = LocalSync(hub)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to sync Hub index with local deployment : %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*LoadPkgIndex loads a local .index.json file and returns the map of parsers/scenarios/collections associated*/
|
||||
// LoadPkgIndex loads a local .index.json file and returns the map of parsers/scenarios/collections associated
|
||||
func LoadPkgIndex(buff []byte) (map[string]map[string]Item, error) {
|
||||
var err error
|
||||
var RawIndex map[string]map[string]Item
|
||||
var missingItems []string
|
||||
var (
|
||||
err error
|
||||
RawIndex map[string]map[string]Item
|
||||
missingItems []string
|
||||
)
|
||||
|
||||
if err = json.Unmarshal(buff, &RawIndex); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal index : %v", err)
|
||||
}
|
||||
|
||||
log.Debugf("%d item types in hub index", len(ItemTypes))
|
||||
/*Iterate over the different types to complete struct */
|
||||
|
||||
// Iterate over the different types to complete struct
|
||||
for _, itemType := range ItemTypes {
|
||||
/*complete struct*/
|
||||
// complete struct
|
||||
log.Tracef("%d item", len(RawIndex[itemType]))
|
||||
|
||||
for idx, item := range RawIndex[itemType] {
|
||||
item.Name = idx
|
||||
item.Type = itemType
|
||||
x := strings.Split(item.RemotePath, "/")
|
||||
item.FileName = x[len(x)-1]
|
||||
RawIndex[itemType][idx] = item
|
||||
/*if it's a collection, check its sub-items are present*/
|
||||
//XX should be done later
|
||||
|
||||
// if it's a collection, check its sub-items are present
|
||||
// XXX should be done later
|
||||
if itemType == COLLECTIONS {
|
||||
var tmp = [][]string{item.Parsers, item.PostOverflows, item.Scenarios, item.Collections}
|
||||
for idx, ptr := range tmp {
|
||||
|
@ -420,6 +471,7 @@ func LoadPkgIndex(buff []byte) (map[string]map[string]Item, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(missingItems) > 0 {
|
||||
return RawIndex, fmt.Errorf("%q: %w", missingItems, ReferenceMissingError)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue