Skip to content

Commit

Permalink
allow any number of subdirectories in /etc/crowdsec/{hubtype}
Browse files Browse the repository at this point in the history
  • Loading branch information
mmetc committed Jul 31, 2024
1 parent ae1edfe commit 2dd02b5
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 35 deletions.
28 changes: 28 additions & 0 deletions pkg/cwhub/relativepath.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cwhub

import (
"path/filepath"
"strings"
)

// relativePathComponents returns the list of path components after baseDir.
// If path is not inside baseDir, it returns an empty slice.
func relativePathComponents(path string, baseDir string) []string {
absPath, err := filepath.Abs(path)
if err != nil {
return []string{}
}

Check warning on line 14 in pkg/cwhub/relativepath.go

View check run for this annotation

Codecov / codecov/patch

pkg/cwhub/relativepath.go#L13-L14

Added lines #L13 - L14 were not covered by tests
absBaseDir, err := filepath.Abs(baseDir)
if err != nil {
return []string{}
}

Check warning on line 18 in pkg/cwhub/relativepath.go

View check run for this annotation

Codecov / codecov/patch

pkg/cwhub/relativepath.go#L17-L18

Added lines #L17 - L18 were not covered by tests

// is path inside baseDir?
relPath, err := filepath.Rel(absBaseDir, absPath)
if err != nil || strings.HasPrefix(relPath, "..") || relPath == "." {
return []string{}
}

return strings.Split(relPath, string(filepath.Separator))
}

72 changes: 72 additions & 0 deletions pkg/cwhub/relativepath_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package cwhub

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestRelativePathComponents(t *testing.T) {
tests := []struct {
name string
path string
baseDir string
expected []string
}{
{
name: "Path within baseDir",
path: "/home/user/project/src/file.go",
baseDir: "/home/user/project",
expected: []string{"src", "file.go"},
},
{
name: "Path is baseDir",
path: "/home/user/project",
baseDir: "/home/user/project",
expected: []string{},
},
{
name: "Path outside baseDir",
path: "/home/user/otherproject/src/file.go",
baseDir: "/home/user/project",
expected: []string{},
},
{
name: "Path is subdirectory of baseDir",
path: "/home/user/project/src/",
baseDir: "/home/user/project",
expected: []string{"src"},
},
{
name: "Relative paths",
path: "project/src/file.go",
baseDir: "project",
expected: []string{"src", "file.go"},
},
{
name: "BaseDir with trailing slash",
path: "/home/user/project/src/file.go",
baseDir: "/home/user/project/",
expected: []string{"src", "file.go"},
},
{
name: "Empty baseDir",
path: "/home/user/project/src/file.go",
baseDir: "",
expected: []string{},
},
{
name: "Empty path",
path: "",
baseDir: "/home/user/project",
expected: []string{},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := relativePathComponents(tt.path, tt.baseDir)
assert.Equal(t, tt.expected, result)
})
}
}
79 changes: 51 additions & 28 deletions pkg/cwhub/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,60 +80,83 @@ func (h *Hub) getItemFileInfo(path string, logger *logrus.Logger) (*itemFileInfo
hubDir := h.local.HubDir
installDir := h.local.InstallDir

subs := strings.Split(path, string(os.PathSeparator))

logger.Tracef("path:%s, hubdir:%s, installdir:%s", path, hubDir, installDir)
logger.Tracef("subs:%v", subs)
// we're in hub (~/.hub/hub/)
if strings.HasPrefix(path, hubDir) {

subsHub := relativePathComponents(path, hubDir)
subsInstall := relativePathComponents(path, installDir)

switch {
case len(subsHub) > 0:
logger.Tracef("in hub dir")

// .../hub/parsers/s00-raw/crowdsecurity/skip-pretag.yaml
// .../hub/scenarios/crowdsecurity/ssh_bf.yaml
// .../hub/profiles/crowdsecurity/linux.yaml
if len(subs) < 4 {
return nil, fmt.Errorf("path is too short: %s (%d)", path, len(subs))
if len(subsHub) < 3 {
return nil, fmt.Errorf("path is too short: %s (%d)", path, len(subsHub))
}

Check warning on line 97 in pkg/cwhub/sync.go

View check run for this annotation

Codecov / codecov/patch

pkg/cwhub/sync.go#L96-L97

Added lines #L96 - L97 were not covered by tests

h.logger.Debug("subsHub:", subsHub)

ftype := subsHub[0]
if !slices.Contains(ItemTypes, ftype) {
// this doesn't really happen anymore, because we only scan the {hubtype} directories
return nil, fmt.Errorf("unknown configuration type '%s'", ftype)
}

Check warning on line 105 in pkg/cwhub/sync.go

View check run for this annotation

Codecov / codecov/patch

pkg/cwhub/sync.go#L103-L105

Added lines #L103 - L105 were not covered by tests

stage := ""
fauthor := subsHub[1]
fname := subsHub[2]

if ftype == PARSERS || ftype == POSTOVERFLOWS {
stage = subsHub[1]
fauthor = subsHub[2]
fname = subsHub[3]
}

ret = &itemFileInfo{
inhub: true,
fname: subs[len(subs)-1],
fauthor: subs[len(subs)-2],
stage: subs[len(subs)-3],
ftype: subs[len(subs)-4],
ftype: ftype,
stage: stage,
fauthor: fauthor,
fname: fname,
}
} else if strings.HasPrefix(path, installDir) { // we're in install /etc/crowdsec/<type>/...

case len(subsInstall) > 0:
logger.Tracef("in install dir")

if len(subs) < 3 {
return nil, fmt.Errorf("path is too short: %s (%d)", path, len(subs))
}
// .../config/parser/stage/file.yaml
// .../config/postoverflow/stage/file.yaml
// .../config/scenarios/scenar.yaml
// .../config/collections/linux.yaml //file is empty

if len(subsInstall) < 2 {
return nil, fmt.Errorf("path is too short: %s (%d)", path, len(subsInstall))
}

Check warning on line 135 in pkg/cwhub/sync.go

View check run for this annotation

Codecov / codecov/patch

pkg/cwhub/sync.go#L134-L135

Added lines #L134 - L135 were not covered by tests

h.logger.Debug("subsInstall:", subsInstall)

// this can be in any number of subdirs, we ignore them

ftype := subsInstall[0]
stage := ""
if ftype == PARSERS || ftype == POSTOVERFLOWS {
stage = subsInstall[1]
}

ret = &itemFileInfo{
inhub: false,
fname: subs[len(subs)-1],
stage: subs[len(subs)-2],
ftype: subs[len(subs)-3],
ftype: ftype,
stage: stage,
fauthor: "",
fname: subsInstall[len(subsInstall)-1],
}
} else {
default:

Check warning on line 154 in pkg/cwhub/sync.go

View check run for this annotation

Codecov / codecov/patch

pkg/cwhub/sync.go#L154

Added line #L154 was not covered by tests
return nil, fmt.Errorf("file '%s' is not from hub '%s' nor from the configuration directory '%s'", path, hubDir, installDir)
}

logger.Tracef("stage:%s ftype:%s", ret.stage, ret.ftype)

if ret.ftype != PARSERS && ret.ftype != POSTOVERFLOWS {
if !slices.Contains(ItemTypes, ret.stage) {
return nil, errors.New("unknown configuration type")
}

ret.ftype = ret.stage
ret.stage = ""
}

logger.Tracef("CORRECTED [%s] by [%s] in stage [%s] of type [%s]", ret.fname, ret.fauthor, ret.stage, ret.ftype)

return ret, nil
Expand Down
24 changes: 17 additions & 7 deletions test/bats/20_hub_items.bats
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,6 @@ teardown() {
assert_output 'false'
}

@test "skip files if we can't guess their type" {
rune -0 mkdir -p "$CONFIG_DIR/scenarios/foo"
rune -0 touch "$CONFIG_DIR/scenarios/foo/bar.yaml"
rune -0 cscli hub list
assert_stderr --partial "Ignoring file $CONFIG_DIR/scenarios/foo/bar.yaml: unknown configuration type"
}

@test "don't traverse hidden directories (starting with a dot)" {
rune -0 mkdir -p "$CONFIG_DIR/scenarios/.foo"
rune -0 touch "$CONFIG_DIR/scenarios/.foo/bar.yaml"
Expand Down Expand Up @@ -260,3 +253,20 @@ teardown() {
rune -0 jq '.scenarios | length' <(output)
assert_output 1
}

@test "item files can be in a subdirectory" {
rune -0 mkdir -p "$CONFIG_DIR/scenarios/sub/sub2/sub3"
rune -0 touch "$CONFIG_DIR/scenarios/sub/imlocal.yaml"
rune -0 cscli scenarios inspect imlocal.yaml -o json
rune -0 jq -e '[.tainted,.local==false,true]' <(output)
rune -0 rm "$CONFIG_DIR/scenarios/sub/imlocal.yaml"

rune -0 ln -s "$HUB_DIR/scenarios/crowdsecurity/smb-bf.yaml" "$CONFIG_DIR/scenarios/sub/smb-bf.yaml"
rune -0 cscli scenarios inspect crowdsecurity/smb-bf -o json
rune -0 jq -e '[.tainted,.local==false,false]' <(output)
rune -0 rm "$CONFIG_DIR/scenarios/sub/smb-bf.yaml"

rune -0 ln -s "$HUB_DIR/scenarios/crowdsecurity/smb-bf.yaml" "$CONFIG_DIR/scenarios/sub/sub2/sub3/smb-bf.yaml"
rune -0 cscli scenarios inspect crowdsecurity/smb-bf -o json
rune -0 jq -e '[.tainted,.local==false,false]' <(output)
}

0 comments on commit 2dd02b5

Please sign in to comment.