Refact cwhub / sort cscli output, case insensitive (#2593)

* dead code: unknown localVersion now defaults to "?"
* skip type declaration; whitespace
* sync: next item if invalid cpath
* func tests for install --force and --ignore
* shorter test names
* sort cscli <itemtype> output, with tests
* cscli: refact hub sort code
This commit is contained in:
mmetc 2023-11-15 16:59:30 +01:00 committed by GitHub
parent 4a6fd338e0
commit 79d019f9a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 205 additions and 76 deletions

View file

@ -289,7 +289,7 @@ func itemsRemoveRunner(it hubItemType) func(cmd *cobra.Command, args []string) e
if all { if all {
getter := hub.GetInstalledItems getter := hub.GetInstalledItems
if (purge) { if purge {
getter = hub.GetAllItems getter = hub.GetAllItems
} }

View file

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"sort"
"strings" "strings"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@ -15,7 +14,8 @@ import (
"github.com/crowdsecurity/crowdsec/pkg/cwhub" "github.com/crowdsecurity/crowdsec/pkg/cwhub"
) )
func selectItems(hub *cwhub.Hub, itemType string, args []string, installedOnly bool) ([]string, error) { // selectItems returns a slice of items of a given type, selected by name and sorted by case-insensitive name
func selectItems(hub *cwhub.Hub, itemType string, args []string, installedOnly bool) ([]*cwhub.Item, error) {
itemNames := hub.GetItemNames(itemType) itemNames := hub.GetItemNames(itemType)
notExist := []string{} notExist := []string{}
@ -37,26 +37,32 @@ func selectItems(hub *cwhub.Hub, itemType string, args []string, installedOnly b
installedOnly = false installedOnly = false
} }
if installedOnly { items := make([]*cwhub.Item, 0, len(itemNames))
installed := []string{}
for _, item := range itemNames { for _, itemName := range itemNames {
if hub.GetItem(itemType, item).Installed { item := hub.GetItem(itemType, itemName)
installed = append(installed, item) if installedOnly && !item.Installed {
} continue
} }
return installed, nil
items = append(items, item)
} }
return itemNames, nil
cwhub.SortItemSlice(items)
return items, nil
} }
// XXX: too complex, should be two functions (itemtypes array and args are not used together)
func ListItems(hub *cwhub.Hub, out io.Writer, itemTypes []string, args []string, showType bool, showHeader bool, all bool) error { func ListItems(hub *cwhub.Hub, out io.Writer, itemTypes []string, args []string, showType bool, showHeader bool, all bool) error {
items := make(map[string][]string) items := make(map[string][]*cwhub.Item)
for _, itemType := range itemTypes { for _, itemType := range itemTypes {
selected, err := selectItems(hub, itemType, args, !all) selected, err := selectItems(hub, itemType, args, !all)
if err != nil { if err != nil {
return err return err
} }
sort.Strings(selected)
items[itemType] = selected items[itemType] = selected
} }
@ -79,8 +85,8 @@ func ListItems(hub *cwhub.Hub, out io.Writer, itemTypes []string, args []string,
for _, itemType := range itemTypes { for _, itemType := range itemTypes {
// empty slice in case there are no items of this type // empty slice in case there are no items of this type
hubStatus[itemType] = make([]itemHubStatus, len(items[itemType])) hubStatus[itemType] = make([]itemHubStatus, len(items[itemType]))
for i, itemName := range items[itemType] {
item := hub.GetItem(itemType, itemName) for i, item := range items[itemType] {
status, emo := item.Status() status, emo := item.Status()
hubStatus[itemType][i] = itemHubStatus{ hubStatus[itemType][i] = itemHubStatus{
Name: item.Name, Name: item.Name,
@ -110,13 +116,10 @@ func ListItems(hub *cwhub.Hub, out io.Writer, itemTypes []string, args []string,
return fmt.Errorf("failed to write header: %s", err) return fmt.Errorf("failed to write header: %s", err)
} }
} }
for _, itemType := range itemTypes { for _, itemType := range itemTypes {
for _, itemName := range items[itemType] { for _, item := range items[itemType] {
item := hub.GetItem(itemType, itemName)
status, _ := item.Status() status, _ := item.Status()
if item.LocalVersion == "" {
item.LocalVersion = "n/a"
}
row := []string{ row := []string{
item.Name, item.Name,
status, status,

View file

@ -11,14 +11,13 @@ import (
"github.com/crowdsecurity/crowdsec/pkg/cwhub" "github.com/crowdsecurity/crowdsec/pkg/cwhub"
) )
func listHubItemTable(hub *cwhub.Hub, out io.Writer, title string, itemType string, itemNames []string) { func listHubItemTable(hub *cwhub.Hub, out io.Writer, title string, itemType string, items []*cwhub.Item) {
t := newLightTable(out) t := newLightTable(out)
t.SetHeaders("Name", fmt.Sprintf("%v Status", emoji.Package), "Version", "Local Path") t.SetHeaders("Name", fmt.Sprintf("%v Status", emoji.Package), "Version", "Local Path")
t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) t.SetHeaderAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
for itemName := range itemNames { for _, item := range items {
item := hub.GetItem(itemType, itemNames[itemName])
status, emo := item.Status() status, emo := item.Status()
t.AddRow(item.Name, fmt.Sprintf("%v %s", emo, status), item.LocalVersion, item.LocalPath) t.AddRow(item.Name, fmt.Sprintf("%v %s", emo, status), item.LocalVersion, item.LocalPath)
} }

View file

@ -60,15 +60,13 @@ func testHub(t *testing.T, update bool) *Hub {
os.RemoveAll(tmpDir) os.RemoveAll(tmpDir)
}) })
var hub *Hub
remote := &RemoteHubCfg{ remote := &RemoteHubCfg{
Branch: "master", Branch: "master",
URLTemplate: mockURLTemplate, URLTemplate: mockURLTemplate,
IndexPath: ".index.json", IndexPath: ".index.json",
} }
hub, err = NewHub(local, remote, update) hub, err := NewHub(local, remote, update)
require.NoError(t, err) require.NoError(t, err)
return hub return hub

View file

@ -89,6 +89,7 @@ func (i *Item) purge() error {
log.Debugf("%s doesn't exist, no need to remove", itempath) log.Debugf("%s doesn't exist, no need to remove", itempath)
return nil return nil
} }
return fmt.Errorf("while removing file: %w", err) return fmt.Errorf("while removing file: %w", err)
} }
@ -101,8 +102,6 @@ func (i *Item) purge() error {
// disable removes the symlink to the downloaded content, also removes the content if purge is true // disable removes the symlink to the downloaded content, also removes the content if purge is true
func (i *Item) disable(purge bool, force bool) error { func (i *Item) disable(purge bool, force bool) error {
// XXX: should return the number of disabled/purged items to inform the upper layer whether to reload or not // XXX: should return the number of disabled/purged items to inform the upper layer whether to reload or not
var err error
if i.IsLocal() { if i.IsLocal() {
return fmt.Errorf("%s isn't managed by hub. Please delete manually", i.Name) return fmt.Errorf("%s isn't managed by hub. Please delete manually", i.Name)
} }
@ -124,7 +123,7 @@ func (i *Item) disable(purge bool, force bool) error {
} }
if removeSub { if removeSub {
if err = sub.disable(purge, force); err != nil { if err := sub.disable(purge, force); err != nil {
return fmt.Errorf("while disabling %s: %w", sub.Name, err) return fmt.Errorf("while disabling %s: %w", sub.Name, err)
} }
} else { } else {
@ -170,7 +169,7 @@ func (i *Item) disable(purge bool, force bool) error {
return fmt.Errorf("%s isn't managed by hub", i.Name) return fmt.Errorf("%s isn't managed by hub", i.Name)
} }
if err = os.Remove(syml); err != nil { if err := os.Remove(syml); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
log.Debugf("%s doesn't exist, no need to remove", syml) log.Debugf("%s doesn't exist, no need to remove", syml)
return nil return nil
@ -184,7 +183,7 @@ func (i *Item) disable(purge bool, force bool) error {
i.Installed = false i.Installed = false
if purge { if purge {
if err = i.purge(); err != nil { if err := i.purge(); err != nil {
return err return err
} }
} }

View file

@ -3,6 +3,8 @@ package cwhub
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"sort"
"strings"
"github.com/Masterminds/semver/v3" "github.com/Masterminds/semver/v3"
"github.com/enescakir/emoji" "github.com/enescakir/emoji"
@ -291,6 +293,7 @@ func (h *Hub) GetAllItems(itemType string) ([]*Item, error) {
ret := make([]*Item, len(items)) ret := make([]*Item, len(items))
idx := 0 idx := 0
for _, item := range items { for _, item := range items {
ret[idx] = item ret[idx] = item
idx++ idx++
@ -332,3 +335,10 @@ func (h *Hub) GetInstalledItemsAsString(itemType string) ([]string, error) {
return retStr, nil return retStr, nil
} }
// SortItemSlice sorts a slice of items by name, case insensitive
func SortItemSlice(items []*Item) {
sort.Slice(items, func(i, j int) bool {
return strings.ToLower(items[i].Name) < strings.ToLower(items[j].Name)
})
}

View file

@ -150,10 +150,8 @@ func sortedVersions(raw []string) ([]string, error) {
} }
func (h *Hub) itemVisit(path string, f os.DirEntry, err error) error { func (h *Hub) itemVisit(path string, f os.DirEntry, err error) error {
var ( local := false
local bool hubpath := ""
hubpath string
)
if err != nil { if err != nil {
log.Debugf("while syncing hub dir: %s", err) log.Debugf("while syncing hub dir: %s", err)
@ -398,6 +396,7 @@ func (h *Hub) syncDir(dir string) ([]string, error) {
cpath, err := filepath.Abs(fmt.Sprintf("%s/%s", dir, scan)) cpath, err := filepath.Abs(fmt.Sprintf("%s/%s", dir, scan))
if err != nil { if err != nil {
log.Errorf("failed %s: %s", cpath, err) log.Errorf("failed %s: %s", cpath, err)
continue
} }
// explicit check for non existing directory, avoid spamming log.Debug // explicit check for non existing directory, avoid spamming log.Debug

View file

@ -79,7 +79,17 @@ teardown() {
rune -0 grep -vc 'name,status,version,description' <(output) rune -0 grep -vc 'name,status,version,description' <(output)
assert_output "$expected" assert_output "$expected"
# XXX: check alphabetical order in human, json, raw # the list should be the same in all formats, and sorted (not case sensitive)
list_raw=$(cscli collections list -o raw -a | tail -n +2 | cut -d, -f1)
list_human=$(cscli collections list -o human -a | tail -n +6 | head -n -1 | cut -d' ' -f2)
list_json=$(cscli collections list -o json -a | jq -r '.collections[].name')
rune -0 sort -f <<<"$list_raw"
assert_output "$list_raw"
assert_equal "$list_raw" "$list_json"
assert_equal "$list_raw" "$list_human"
} }
@test "cscli collections list [collection]..." { @test "cscli collections list [collection]..." {
@ -120,7 +130,7 @@ teardown() {
assert_output "2" assert_output "2"
} }
@test "cscli collections install [collection]..." { @test "cscli collections install" {
rune -1 cscli collections install rune -1 cscli collections install
assert_stderr --partial 'requires at least 1 arg(s), only received 0' assert_stderr --partial 'requires at least 1 arg(s), only received 0'
@ -148,8 +158,7 @@ teardown() {
assert_output --partial 'installed: true' assert_output --partial 'installed: true'
} }
@test "cscli collections install [collection]... (file location and download-only)" { @test "cscli collections install (file location and download-only)" {
# simple install
rune -0 cscli collections install crowdsecurity/linux --download-only rune -0 cscli collections install crowdsecurity/linux --download-only
rune -0 cscli collections inspect crowdsecurity/linux --no-metrics rune -0 cscli collections inspect crowdsecurity/linux --no-metrics
assert_output --partial 'crowdsecurity/linux' assert_output --partial 'crowdsecurity/linux'
@ -158,11 +167,34 @@ teardown() {
assert_file_not_exists "$CONFIG_DIR/collections/linux.yaml" assert_file_not_exists "$CONFIG_DIR/collections/linux.yaml"
rune -0 cscli collections install crowdsecurity/linux rune -0 cscli collections install crowdsecurity/linux
rune -0 cscli collections inspect crowdsecurity/linux --no-metrics
assert_output --partial 'installed: true'
assert_file_exists "$CONFIG_DIR/collections/linux.yaml" assert_file_exists "$CONFIG_DIR/collections/linux.yaml"
} }
@test "cscli collections install --force (tainted)" {
rune -0 cscli collections install crowdsecurity/sshd
echo "dirty" >"$CONFIG_DIR/collections/sshd.yaml"
@test "cscli collections inspect [collection]..." { rune -1 cscli collections install crowdsecurity/sshd
assert_stderr --partial "error while installing 'crowdsecurity/sshd': while enabling crowdsecurity/sshd: crowdsecurity/sshd is tainted, won't enable unless --force"
rune -0 cscli collections install crowdsecurity/sshd --force
assert_stderr --partial "crowdsecurity/sshd: overwrite"
assert_stderr --partial "Enabled crowdsecurity/sshd"
}
@test "cscli collections install --ignore (skip on errors)" {
rune -1 cscli collections install foo/bar crowdsecurity/sshd
assert_stderr --partial "can't find 'foo/bar' in collections"
refute_stderr --partial "Enabled collections: crowdsecurity/sshd"
rune -0 cscli collections install foo/bar crowdsecurity/sshd --ignore
assert_stderr --partial "can't find 'foo/bar' in collections"
assert_stderr --partial "Enabled collections: crowdsecurity/sshd"
}
@test "cscli collections inspect" {
rune -1 cscli collections inspect rune -1 cscli collections inspect
assert_stderr --partial 'requires at least 1 arg(s), only received 0' assert_stderr --partial 'requires at least 1 arg(s), only received 0'
# required for metrics # required for metrics
@ -223,7 +255,7 @@ teardown() {
assert_output "0" assert_output "0"
} }
@test "cscli collections remove [collection]..." { @test "cscli collections remove" {
rune -1 cscli collections remove rune -1 cscli collections remove
assert_stderr --partial "specify at least one collection to remove or '--all'" assert_stderr --partial "specify at least one collection to remove or '--all'"
rune -1 cscli collections remove blahblah/blahblah rune -1 cscli collections remove blahblah/blahblah
@ -277,7 +309,7 @@ teardown() {
assert_output "0" assert_output "0"
} }
@test "cscli collections remove [collections]... --force" { @test "cscli collections remove --force" {
# remove a collections that belongs to a collection # remove a collections that belongs to a collection
rune -0 cscli collections install crowdsecurity/linux rune -0 cscli collections install crowdsecurity/linux
rune -0 cscli collections remove crowdsecurity/sshd rune -0 cscli collections remove crowdsecurity/sshd
@ -285,7 +317,7 @@ teardown() {
assert_stderr --partial "Run 'sudo cscli collections remove crowdsecurity/sshd --force' if you want to force remove this collection" assert_stderr --partial "Run 'sudo cscli collections remove crowdsecurity/sshd --force' if you want to force remove this collection"
} }
@test "cscli collections upgrade [collection]..." { @test "cscli collections upgrade" {
rune -1 cscli collections upgrade rune -1 cscli collections upgrade
assert_stderr --partial "specify at least one collection to upgrade or '--all'" assert_stderr --partial "specify at least one collection to upgrade or '--all'"
rune -1 cscli collections upgrade blahblah/blahblah rune -1 cscli collections upgrade blahblah/blahblah

View file

@ -79,7 +79,17 @@ teardown() {
rune -0 grep -vc 'name,status,version,description' <(output) rune -0 grep -vc 'name,status,version,description' <(output)
assert_output "$expected" assert_output "$expected"
# XXX: check alphabetical order in human, json, raw # the list should be the same in all formats, and sorted (not case sensitive)
list_raw=$(cscli parsers list -o raw -a | tail -n +2 | cut -d, -f1)
list_human=$(cscli parsers list -o human -a | tail -n +6 | head -n -1 | cut -d' ' -f2)
list_json=$(cscli parsers list -o json -a | jq -r '.parsers[].name')
rune -0 sort -f <<<"$list_raw"
assert_output "$list_raw"
assert_equal "$list_raw" "$list_json"
assert_equal "$list_raw" "$list_human"
} }
@test "cscli parsers list [parser]..." { @test "cscli parsers list [parser]..." {
@ -120,7 +130,7 @@ teardown() {
assert_output "3" assert_output "3"
} }
@test "cscli parsers install [parser]..." { @test "cscli parsers install" {
rune -1 cscli parsers install rune -1 cscli parsers install
assert_stderr --partial 'requires at least 1 arg(s), only received 0' assert_stderr --partial 'requires at least 1 arg(s), only received 0'
@ -148,8 +158,7 @@ teardown() {
assert_output --partial 'installed: true' assert_output --partial 'installed: true'
} }
@test "cscli parsers install [parser]... (file location and download-only)" { @test "cscli parsers install (file location and download-only)" {
# simple install
rune -0 cscli parsers install crowdsecurity/whitelists --download-only rune -0 cscli parsers install crowdsecurity/whitelists --download-only
rune -0 cscli parsers inspect crowdsecurity/whitelists --no-metrics rune -0 cscli parsers inspect crowdsecurity/whitelists --no-metrics
assert_output --partial 'crowdsecurity/whitelists' assert_output --partial 'crowdsecurity/whitelists'
@ -158,13 +167,34 @@ teardown() {
assert_file_not_exists "$CONFIG_DIR/parsers/s02-enrich/whitelists.yaml" assert_file_not_exists "$CONFIG_DIR/parsers/s02-enrich/whitelists.yaml"
rune -0 cscli parsers install crowdsecurity/whitelists rune -0 cscli parsers install crowdsecurity/whitelists
rune -0 cscli parsers inspect crowdsecurity/whitelists --no-metrics
assert_output --partial 'installed: true'
assert_file_exists "$CONFIG_DIR/parsers/s02-enrich/whitelists.yaml" assert_file_exists "$CONFIG_DIR/parsers/s02-enrich/whitelists.yaml"
} }
# XXX: test install with --force @test "cscli parsers install --force (tainted)" {
# XXX: test install with --ignore rune -0 cscli parsers install crowdsecurity/whitelists
echo "dirty" >"$CONFIG_DIR/parsers/s02-enrich/whitelists.yaml"
@test "cscli parsers inspect [parser]..." { rune -1 cscli parsers install crowdsecurity/whitelists
assert_stderr --partial "error while installing 'crowdsecurity/whitelists': while enabling crowdsecurity/whitelists: crowdsecurity/whitelists is tainted, won't enable unless --force"
rune -0 cscli parsers install crowdsecurity/whitelists --force
assert_stderr --partial "crowdsecurity/whitelists: overwrite"
assert_stderr --partial "Enabled crowdsecurity/whitelists"
}
@test "cscli parsers install --ignore (skip on errors)" {
rune -1 cscli parsers install foo/bar crowdsecurity/whitelists
assert_stderr --partial "can't find 'foo/bar' in parsers"
refute_stderr --partial "Enabled parsers: crowdsecurity/whitelists"
rune -0 cscli parsers install foo/bar crowdsecurity/whitelists --ignore
assert_stderr --partial "can't find 'foo/bar' in parsers"
assert_stderr --partial "Enabled parsers: crowdsecurity/whitelists"
}
@test "cscli parsers inspect" {
rune -1 cscli parsers inspect rune -1 cscli parsers inspect
assert_stderr --partial 'requires at least 1 arg(s), only received 0' assert_stderr --partial 'requires at least 1 arg(s), only received 0'
# required for metrics # required for metrics
@ -195,8 +225,8 @@ teardown() {
# one item, raw # one item, raw
rune -0 cscli parsers inspect crowdsecurity/sshd-logs -o raw rune -0 cscli parsers inspect crowdsecurity/sshd-logs -o raw
assert_line 'type: parsers' assert_line 'type: parsers'
assert_line 'stage: s01-parse'
assert_line 'name: crowdsecurity/sshd-logs' assert_line 'name: crowdsecurity/sshd-logs'
assert_line 'stage: s01-parse'
assert_line 'author: crowdsecurity' assert_line 'author: crowdsecurity'
assert_line 'remote_path: parsers/s01-parse/crowdsecurity/sshd-logs.yaml' assert_line 'remote_path: parsers/s01-parse/crowdsecurity/sshd-logs.yaml'
assert_line 'installed: false' assert_line 'installed: false'
@ -227,7 +257,7 @@ teardown() {
assert_output "0" assert_output "0"
} }
@test "cscli parsers remove [parser]..." { @test "cscli parsers remove" {
rune -1 cscli parsers remove rune -1 cscli parsers remove
assert_stderr --partial "specify at least one parser to remove or '--all'" assert_stderr --partial "specify at least one parser to remove or '--all'"
rune -1 cscli parsers remove blahblah/blahblah rune -1 cscli parsers remove blahblah/blahblah
@ -281,7 +311,7 @@ teardown() {
assert_output "0" assert_output "0"
} }
@test "cscli parsers remove [parser]... --force" { @test "cscli parsers remove --force" {
# remove a parser that belongs to a collection # remove a parser that belongs to a collection
rune -0 cscli collections install crowdsecurity/sshd rune -0 cscli collections install crowdsecurity/sshd
rune -0 cscli parsers remove crowdsecurity/sshd-logs rune -0 cscli parsers remove crowdsecurity/sshd-logs
@ -289,7 +319,7 @@ teardown() {
assert_stderr --partial "Run 'sudo cscli parsers remove crowdsecurity/sshd-logs --force' if you want to force remove this parser" assert_stderr --partial "Run 'sudo cscli parsers remove crowdsecurity/sshd-logs --force' if you want to force remove this parser"
} }
@test "cscli parsers upgrade [parser]..." { @test "cscli parsers upgrade" {
rune -1 cscli parsers upgrade rune -1 cscli parsers upgrade
assert_stderr --partial "specify at least one parser to upgrade or '--all'" assert_stderr --partial "specify at least one parser to upgrade or '--all'"
rune -1 cscli parsers upgrade blahblah/blahblah rune -1 cscli parsers upgrade blahblah/blahblah

View file

@ -79,15 +79,24 @@ teardown() {
rune -0 grep -vc 'name,status,version,description' <(output) rune -0 grep -vc 'name,status,version,description' <(output)
assert_output "$expected" assert_output "$expected"
# XXX: check alphabetical order in human, json, raw # the list should be the same in all formats, and sorted (not case sensitive)
list_raw=$(cscli postoverflows list -o raw -a | tail -n +2 | cut -d, -f1)
list_human=$(cscli postoverflows list -o human -a | tail -n +6 | head -n -1 | cut -d' ' -f2)
list_json=$(cscli postoverflows list -o json -a | jq -r '.postoverflows[].name')
rune -0 sort -f <<<"$list_raw"
assert_output "$list_raw"
assert_equal "$list_raw" "$list_json"
assert_equal "$list_raw" "$list_human"
} }
@test "cscli postoverflows list [scenario]..." { @test "cscli postoverflows list [postoverflow]..." {
# non-existent # non-existent
rune -1 cscli postoverflows install foo/bar rune -1 cscli postoverflows install foo/bar
assert_stderr --partial "can't find 'foo/bar' in postoverflows" assert_stderr --partial "can't find 'foo/bar' in postoverflows"
# not installed # not installed
rune -0 cscli postoverflows list crowdsecurity/rdns rune -0 cscli postoverflows list crowdsecurity/rdns
assert_output --regexp 'crowdsecurity/rdns.*disabled' assert_output --regexp 'crowdsecurity/rdns.*disabled'
@ -121,7 +130,7 @@ teardown() {
assert_output "3" assert_output "3"
} }
@test "cscli postoverflows install [scenario]..." { @test "cscli postoverflows install" {
rune -1 cscli postoverflows install rune -1 cscli postoverflows install
assert_stderr --partial 'requires at least 1 arg(s), only received 0' assert_stderr --partial 'requires at least 1 arg(s), only received 0'
@ -149,8 +158,7 @@ teardown() {
assert_output --partial 'installed: true' assert_output --partial 'installed: true'
} }
@test "cscli postoverflows install [postoverflow]... (file location and download-only)" { @test "cscli postoverflows install (file location and download-only)" {
# simple install
rune -0 cscli postoverflows install crowdsecurity/rdns --download-only rune -0 cscli postoverflows install crowdsecurity/rdns --download-only
rune -0 cscli postoverflows inspect crowdsecurity/rdns --no-metrics rune -0 cscli postoverflows inspect crowdsecurity/rdns --no-metrics
assert_output --partial 'crowdsecurity/rdns' assert_output --partial 'crowdsecurity/rdns'
@ -159,13 +167,34 @@ teardown() {
assert_file_not_exists "$CONFIG_DIR/postoverflows/s00-enrich/rdns.yaml" assert_file_not_exists "$CONFIG_DIR/postoverflows/s00-enrich/rdns.yaml"
rune -0 cscli postoverflows install crowdsecurity/rdns rune -0 cscli postoverflows install crowdsecurity/rdns
rune -0 cscli postoverflows inspect crowdsecurity/rdns --no-metrics
assert_output --partial 'installed: true'
assert_file_exists "$CONFIG_DIR/postoverflows/s00-enrich/rdns.yaml" assert_file_exists "$CONFIG_DIR/postoverflows/s00-enrich/rdns.yaml"
} }
# XXX: test install with --force @test "cscli postoverflows install --force (tainted)" {
# XXX: test install with --ignore rune -0 cscli postoverflows install crowdsecurity/rdns
echo "dirty" >"$CONFIG_DIR/postoverflows/s00-enrich/rdns.yaml"
@test "cscli postoverflows inspect [scenario]..." { rune -1 cscli postoverflows install crowdsecurity/rdns
assert_stderr --partial "error while installing 'crowdsecurity/rdns': while enabling crowdsecurity/rdns: crowdsecurity/rdns is tainted, won't enable unless --force"
rune -0 cscli postoverflows install crowdsecurity/rdns --force
assert_stderr --partial "crowdsecurity/rdns: overwrite"
assert_stderr --partial "Enabled crowdsecurity/rdns"
}
@test "cscli postoverflow install --ignore (skip on errors)" {
rune -1 cscli postoverflows install foo/bar crowdsecurity/rdns
assert_stderr --partial "can't find 'foo/bar' in postoverflows"
refute_stderr --partial "Enabled postoverflows: crowdsecurity/rdns"
rune -0 cscli postoverflows install foo/bar crowdsecurity/rdns --ignore
assert_stderr --partial "can't find 'foo/bar' in postoverflows"
assert_stderr --partial "Enabled postoverflows: crowdsecurity/rdns"
}
@test "cscli postoverflows inspect" {
rune -1 cscli postoverflows inspect rune -1 cscli postoverflows inspect
assert_stderr --partial 'requires at least 1 arg(s), only received 0' assert_stderr --partial 'requires at least 1 arg(s), only received 0'
# required for metrics # required for metrics
@ -196,8 +225,8 @@ teardown() {
# one item, raw # one item, raw
rune -0 cscli postoverflows inspect crowdsecurity/rdns -o raw rune -0 cscli postoverflows inspect crowdsecurity/rdns -o raw
assert_line 'type: postoverflows' assert_line 'type: postoverflows'
assert_line 'stage: s00-enrich'
assert_line 'name: crowdsecurity/rdns' assert_line 'name: crowdsecurity/rdns'
assert_line 'stage: s00-enrich'
assert_line 'author: crowdsecurity' assert_line 'author: crowdsecurity'
assert_line 'remote_path: postoverflows/s00-enrich/crowdsecurity/rdns.yaml' assert_line 'remote_path: postoverflows/s00-enrich/crowdsecurity/rdns.yaml'
assert_line 'installed: false' assert_line 'installed: false'
@ -228,7 +257,7 @@ teardown() {
assert_output "0" assert_output "0"
} }
@test "cscli postoverflows remove [postoverflow]..." { @test "cscli postoverflows remove" {
rune -1 cscli postoverflows remove rune -1 cscli postoverflows remove
assert_stderr --partial "specify at least one postoverflow to remove or '--all'" assert_stderr --partial "specify at least one postoverflow to remove or '--all'"
rune -1 cscli postoverflows remove blahblah/blahblah rune -1 cscli postoverflows remove blahblah/blahblah
@ -282,7 +311,7 @@ teardown() {
assert_output "0" assert_output "0"
} }
@test "cscli postoverflows remove [postoverflow]... --force" { @test "cscli postoverflows remove --force" {
# remove a postoverflow that belongs to a collection # remove a postoverflow that belongs to a collection
rune -0 cscli collections install crowdsecurity/auditd rune -0 cscli collections install crowdsecurity/auditd
rune -0 cscli postoverflows remove crowdsecurity/auditd-whitelisted-process rune -0 cscli postoverflows remove crowdsecurity/auditd-whitelisted-process
@ -290,7 +319,7 @@ teardown() {
assert_stderr --partial "Run 'sudo cscli postoverflows remove crowdsecurity/auditd-whitelisted-process --force' if you want to force remove this postoverflow" assert_stderr --partial "Run 'sudo cscli postoverflows remove crowdsecurity/auditd-whitelisted-process --force' if you want to force remove this postoverflow"
} }
@test "cscli postoverflows upgrade [postoverflow]..." { @test "cscli postoverflows upgrade" {
rune -1 cscli postoverflows upgrade rune -1 cscli postoverflows upgrade
assert_stderr --partial "specify at least one postoverflow to upgrade or '--all'" assert_stderr --partial "specify at least one postoverflow to upgrade or '--all'"
rune -1 cscli postoverflows upgrade blahblah/blahblah rune -1 cscli postoverflows upgrade blahblah/blahblah

View file

@ -79,7 +79,17 @@ teardown() {
rune -0 grep -vc 'name,status,version,description' <(output) rune -0 grep -vc 'name,status,version,description' <(output)
assert_output "$expected" assert_output "$expected"
# XXX: check alphabetical order in human, json, raw # the list should be the same in all formats, and sorted (not case sensitive)
list_raw=$(cscli scenarios list -o raw -a | tail -n +2 | cut -d, -f1)
list_human=$(cscli scenarios list -o human -a | tail -n +6 | head -n -1 | cut -d' ' -f2)
list_json=$(cscli scenarios list -o json -a | jq -r '.scenarios[].name')
rune -0 sort -f <<<"$list_raw"
assert_output "$list_raw"
assert_equal "$list_raw" "$list_json"
assert_equal "$list_raw" "$list_human"
} }
@test "cscli scenarios list [scenario]..." { @test "cscli scenarios list [scenario]..." {
@ -120,7 +130,7 @@ teardown() {
assert_output "3" assert_output "3"
} }
@test "cscli scenarios install [scenario]..." { @test "cscli scenarios install" {
rune -1 cscli scenarios install rune -1 cscli scenarios install
assert_stderr --partial 'requires at least 1 arg(s), only received 0' assert_stderr --partial 'requires at least 1 arg(s), only received 0'
@ -148,7 +158,7 @@ teardown() {
assert_output --partial 'installed: true' assert_output --partial 'installed: true'
} }
@test "cscli scenarios install [scenario]... (file location and download-only)" { @test "cscli scenarios install (file location and download-only)" {
# simple install # simple install
rune -0 cscli scenarios install crowdsecurity/ssh-bf --download-only rune -0 cscli scenarios install crowdsecurity/ssh-bf --download-only
rune -0 cscli scenarios inspect crowdsecurity/ssh-bf --no-metrics rune -0 cscli scenarios inspect crowdsecurity/ssh-bf --no-metrics
@ -158,14 +168,34 @@ teardown() {
assert_file_not_exists "$CONFIG_DIR/scenarios/ssh-bf.yaml" assert_file_not_exists "$CONFIG_DIR/scenarios/ssh-bf.yaml"
rune -0 cscli scenarios install crowdsecurity/ssh-bf rune -0 cscli scenarios install crowdsecurity/ssh-bf
rune -0 cscli scenarios inspect crowdsecurity/ssh-bf --no-metrics
assert_output --partial 'installed: true'
assert_file_exists "$CONFIG_DIR/scenarios/ssh-bf.yaml" assert_file_exists "$CONFIG_DIR/scenarios/ssh-bf.yaml"
} }
# XXX: test install with --force @test "cscli scenarios install --force (tainted)" {
# XXX: test install with --ignore rune -0 cscli scenarios install crowdsecurity/ssh-bf
echo "dirty" >"$CONFIG_DIR/scenarios/ssh-bf.yaml"
rune -1 cscli scenarios install crowdsecurity/ssh-bf
assert_stderr --partial "error while installing 'crowdsecurity/ssh-bf': while enabling crowdsecurity/ssh-bf: crowdsecurity/ssh-bf is tainted, won't enable unless --force"
@test "cscli scenarios inspect [scenario]..." { rune -0 cscli scenarios install crowdsecurity/ssh-bf --force
assert_stderr --partial "crowdsecurity/ssh-bf: overwrite"
assert_stderr --partial "Enabled crowdsecurity/ssh-bf"
}
@test "cscli scenarios install --ignore (skip on errors)" {
rune -1 cscli scenarios install foo/bar crowdsecurity/ssh-bf
assert_stderr --partial "can't find 'foo/bar' in scenarios"
refute_stderr --partial "Enabled scenarios: crowdsecurity/ssh-bf"
rune -0 cscli scenarios install foo/bar crowdsecurity/ssh-bf --ignore
assert_stderr --partial "can't find 'foo/bar' in scenarios"
assert_stderr --partial "Enabled scenarios: crowdsecurity/ssh-bf"
}
@test "cscli scenarios inspect" {
rune -1 cscli scenarios inspect rune -1 cscli scenarios inspect
assert_stderr --partial 'requires at least 1 arg(s), only received 0' assert_stderr --partial 'requires at least 1 arg(s), only received 0'
# required for metrics # required for metrics
@ -226,7 +256,7 @@ teardown() {
assert_output "0" assert_output "0"
} }
@test "cscli scenarios remove [scenario]..." { @test "cscli scenarios remove" {
rune -1 cscli scenarios remove rune -1 cscli scenarios remove
assert_stderr --partial "specify at least one scenario to remove or '--all'" assert_stderr --partial "specify at least one scenario to remove or '--all'"
rune -1 cscli scenarios remove blahblah/blahblah rune -1 cscli scenarios remove blahblah/blahblah
@ -280,7 +310,7 @@ teardown() {
assert_output "0" assert_output "0"
} }
@test "cscli scenarios remove [scenario]... --force" { @test "cscli scenarios remove --force" {
# remove a scenario that belongs to a collection # remove a scenario that belongs to a collection
rune -0 cscli collections install crowdsecurity/sshd rune -0 cscli collections install crowdsecurity/sshd
rune -0 cscli scenarios remove crowdsecurity/ssh-bf rune -0 cscli scenarios remove crowdsecurity/ssh-bf
@ -288,7 +318,7 @@ teardown() {
assert_stderr --partial "Run 'sudo cscli scenarios remove crowdsecurity/ssh-bf --force' if you want to force remove this scenario" assert_stderr --partial "Run 'sudo cscli scenarios remove crowdsecurity/ssh-bf --force' if you want to force remove this scenario"
} }
@test "cscli scenarios upgrade [scenario]..." { @test "cscli scenarios upgrade" {
rune -1 cscli scenarios upgrade rune -1 cscli scenarios upgrade
assert_stderr --partial "specify at least one scenario to upgrade or '--all'" assert_stderr --partial "specify at least one scenario to upgrade or '--all'"
rune -1 cscli scenarios upgrade blahblah/blahblah rune -1 cscli scenarios upgrade blahblah/blahblah